diff options
47 files changed, 3353 insertions, 1927 deletions
diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp index 4f85eced93..e7d5b78d14 100644 --- a/core/io/marshalls.cpp +++ b/core/io/marshalls.cpp @@ -746,7 +746,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::PACKED_INT64_ARRAY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - int64_t count = decode_uint64(buf); + int32_t count = decode_uint32(buf); buf += 4; len -= 4; ERR_FAIL_MUL_OF(count, 8, ERR_INVALID_DATA); @@ -795,7 +795,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::PACKED_FLOAT64_ARRAY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - int64_t count = decode_uint64(buf); + int32_t count = decode_uint32(buf); buf += 4; len -= 4; ERR_FAIL_MUL_OF(count, 8, ERR_INVALID_DATA); @@ -804,7 +804,6 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int Vector<double> data; if (count) { - //const double*rbuf=(const double*)buf; data.resize(count); double *w = data.ptrw(); for (int64_t i = 0; i < count; i++) { @@ -1519,7 +1518,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo int datasize = sizeof(int64_t); if (buf) { - encode_uint64(datalen, buf); + encode_uint32(datalen, buf); buf += 4; const int64_t *r = data.ptr(); for (int64_t i = 0; i < datalen; i++) { diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml index 957619ad81..f3c64c3c7d 100644 --- a/doc/classes/TileMap.xml +++ b/doc/classes/TileMap.xml @@ -31,22 +31,43 @@ </method> <method name="get_cell_alternative_tile" qualifiers="const"> <return type="int" /> - <argument index="0" name="coords" type="Vector2i" /> - <argument index="1" name="use_proxies" type="bool" /> + <argument index="0" name="layer" type="int" /> + <argument index="1" name="coords" type="Vector2i" /> + <argument index="2" name="use_proxies" type="bool" /> <description> </description> </method> <method name="get_cell_atlas_coords" qualifiers="const"> <return type="Vector2i" /> - <argument index="0" name="coords" type="Vector2i" /> - <argument index="1" name="use_proxies" type="bool" /> + <argument index="0" name="layer" type="int" /> + <argument index="1" name="coords" type="Vector2i" /> + <argument index="2" name="use_proxies" type="bool" /> <description> </description> </method> <method name="get_cell_source_id" qualifiers="const"> <return type="int" /> - <argument index="0" name="coords" type="Vector2i" /> - <argument index="1" name="use_proxies" type="bool" /> + <argument index="0" name="layer" type="int" /> + <argument index="1" name="coords" type="Vector2i" /> + <argument index="2" name="use_proxies" type="bool" /> + <description> + </description> + </method> + <method name="get_layer_name" qualifiers="const"> + <return type="String" /> + <argument index="0" name="layer" type="int" /> + <description> + </description> + </method> + <method name="get_layer_y_sort_origin" qualifiers="const"> + <return type="int" /> + <argument index="0" name="layer" type="int" /> + <description> + </description> + </method> + <method name="get_layer_z_indexd" qualifiers="const"> + <return type="int" /> + <argument index="0" name="layer" type="int" /> <description> </description> </method> @@ -65,6 +86,7 @@ </method> <method name="get_used_cells" qualifiers="const"> <return type="Vector2i[]" /> + <argument index="0" name="layer" type="int" /> <description> Returns a [Vector2] array with the positions of all cells containing a tile from the tileset (i.e. a tile index different from [code]-1[/code]). </description> @@ -75,6 +97,18 @@ Returns a rectangle enclosing the used (non-empty) tiles of the map. </description> </method> + <method name="is_layer_enabled" qualifiers="const"> + <return type="bool" /> + <argument index="0" name="layer" type="int" /> + <description> + </description> + </method> + <method name="is_layer_y_sort_enabled" qualifiers="const"> + <return type="bool" /> + <argument index="0" name="layer" type="int" /> + <description> + </description> + </method> <method name="map_to_world" qualifiers="const"> <return type="Vector2" /> <argument index="0" name="map_position" type="Vector2i" /> @@ -84,18 +118,48 @@ </method> <method name="set_cell"> <return type="void" /> - <argument index="0" name="coords" type="Vector2i" /> - <argument index="1" name="source_id" type="int" default="-1" /> - <argument index="2" name="atlas_coords" type="Vector2i" default="Vector2i(-1, -1)" /> - <argument index="3" name="alternative_tile" type="int" default="-1" /> + <argument index="0" name="layer" type="int" /> + <argument index="1" name="coords" type="Vector2i" /> + <argument index="2" name="source_id" type="int" default="-1" /> + <argument index="3" name="atlas_coords" type="Vector2i" default="Vector2i(-1, -1)" /> + <argument index="4" name="alternative_tile" type="int" default="-1" /> <description> Sets the tile index for the cell given by a Vector2i. </description> </method> - <method name="update_dirty_quadrants"> + <method name="set_layer_enabled"> + <return type="void" /> + <argument index="0" name="layer" type="int" /> + <argument index="1" name="enabled" type="bool" /> + <description> + </description> + </method> + <method name="set_layer_name"> + <return type="void" /> + <argument index="0" name="layer" type="int" /> + <argument index="1" name="name" type="String" /> + <description> + </description> + </method> + <method name="set_layer_y_sort_enabled"> <return type="void" /> + <argument index="0" name="layer" type="int" /> + <argument index="1" name="y_sort_enabled" type="bool" /> + <description> + </description> + </method> + <method name="set_layer_y_sort_origin"> + <return type="void" /> + <argument index="0" name="layer" type="int" /> + <argument index="1" name="y_sort_origin" type="int" /> + <description> + </description> + </method> + <method name="set_layer_z_index"> + <return type="void" /> + <argument index="0" name="layer" type="int" /> + <argument index="1" name="z_index" type="int" /> <description> - Updates the tile map's quadrants, allowing things such as navigation and collision shapes to be immediately used if modified. </description> </method> <method name="world_to_map" qualifiers="const"> @@ -110,9 +174,11 @@ <member name="cell_quadrant_size" type="int" setter="set_quadrant_size" getter="get_quadrant_size" default="16"> The TileMap's quadrant size. Optimizes drawing by batching, using chunks of this size. </member> - <member name="show_collision" type="int" setter="set_collision_visibility_mode" getter="get_collision_visibility_mode" enum="TileMap.VisibilityMode" default="0"> + <member name="collision_visibility_mode" type="int" setter="set_collision_visibility_mode" getter="get_collision_visibility_mode" enum="TileMap.VisibilityMode" default="0"> + </member> + <member name="layers_count" type="int" setter="set_layers_count" getter="get_layers_count" default="1"> </member> - <member name="show_navigation" type="int" setter="set_navigation_visibility_mode" getter="get_navigation_visibility_mode" enum="TileMap.VisibilityMode" default="0"> + <member name="navigation_visibility_mode" type="int" setter="set_navigation_visibility_mode" getter="get_navigation_visibility_mode" enum="TileMap.VisibilityMode" default="0"> </member> <member name="tile_set" type="TileSet" setter="set_tileset" getter="get_tileset"> The assigned [TileSet]. diff --git a/doc/classes/TileSet.xml b/doc/classes/TileSet.xml index 27e31f25a4..439c6e3830 100644 --- a/doc/classes/TileSet.xml +++ b/doc/classes/TileSet.xml @@ -329,8 +329,6 @@ </member> <member name="uv_clipping" type="bool" setter="set_uv_clipping" getter="is_uv_clipping" default="false"> </member> - <member name="y_sorting" type="bool" setter="set_y_sorting" getter="is_y_sorting" default="false"> - </member> </members> <constants> <constant name="TILE_SHAPE_SQUARE" value="0" enum="TileShape"> diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 4a3413d9ef..d7b7e8e52d 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -417,7 +417,7 @@ bool EditorPropertyRevert::may_node_be_in_instance(Node *p_node) { return might_be; // or might not be } -bool EditorPropertyRevert::get_instantiated_node_original_property(Node *p_node, const StringName &p_prop, Variant &value) { +bool EditorPropertyRevert::get_instantiated_node_original_property(Node *p_node, const StringName &p_prop, Variant &value, bool p_check_class_default) { Node *node = p_node; Node *orig = node; @@ -455,7 +455,7 @@ bool EditorPropertyRevert::get_instantiated_node_original_property(Node *p_node, node = node->get_owner(); } - if (!found && p_node) { + if (p_check_class_default && !found && p_node) { //if not found, try default class value Variant attempt = ClassDB::class_get_default_property_value(p_node->get_class_name(), p_prop); if (attempt.get_type() != Variant::NIL) { @@ -548,7 +548,7 @@ Variant EditorPropertyRevert::get_property_revert_value(Object *p_object, const if (!ignore_parent) { //check for difference including instantiation Variant vorig; - if (EditorPropertyRevert::get_instantiated_node_original_property(node, p_property, vorig)) { + if (EditorPropertyRevert::get_instantiated_node_original_property(node, p_property, vorig, false)) { return vorig; } } diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h index 2bb856dc81..3c9ba9f39d 100644 --- a/editor/editor_inspector.h +++ b/editor/editor_inspector.h @@ -40,7 +40,7 @@ class UndoRedo; class EditorPropertyRevert { public: static bool may_node_be_in_instance(Node *p_node); - static bool get_instantiated_node_original_property(Node *p_node, const StringName &p_prop, Variant &value); + static bool get_instantiated_node_original_property(Node *p_node, const StringName &p_prop, Variant &value, bool p_check_class_default = true); static bool is_node_property_different(Node *p_node, const Variant &p_current, const Variant &p_orig); static bool is_property_value_different(const Variant &p_a, const Variant &p_b); static Variant get_property_revert_value(Object *p_object, const StringName &p_property); diff --git a/editor/editor_resource_picker.cpp b/editor/editor_resource_picker.cpp index 7d809ea656..9977f5f42c 100644 --- a/editor/editor_resource_picker.cpp +++ b/editor/editor_resource_picker.cpp @@ -505,7 +505,9 @@ bool EditorResourcePicker::_is_drop_valid(const Dictionary &p_drag_data) const { Ref<Resource> res; if (drag_data.has("type") && String(drag_data["type"]) == "script_list_element") { ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(drag_data["script_list_element"]); - res = se->get_edited_resource(); + if (se) { + res = se->get_edited_resource(); + } } else if (drag_data.has("type") && String(drag_data["type"]) == "resource") { res = drag_data["resource"]; } @@ -571,7 +573,9 @@ void EditorResourcePicker::drop_data_fw(const Point2 &p_point, const Variant &p_ Ref<Resource> dropped_resource; if (drag_data.has("type") && String(drag_data["type"]) == "script_list_element") { ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(drag_data["script_list_element"]); - dropped_resource = se->get_edited_resource(); + if (se) { + dropped_resource = se->get_edited_resource(); + } } else if (drag_data.has("type") && String(drag_data["type"]) == "resource") { dropped_resource = drag_data["resource"]; } diff --git a/editor/editor_spin_slider.cpp b/editor/editor_spin_slider.cpp index 58ab7d3cef..a802afda0f 100644 --- a/editor/editor_spin_slider.cpp +++ b/editor/editor_spin_slider.cpp @@ -32,12 +32,18 @@ #include "core/input/input.h" #include "core/math/expression.h" +#include "core/os/keyboard.h" #include "editor_node.h" #include "editor_scale.h" String EditorSpinSlider::get_tooltip(const Point2 &p_pos) const { if (grabber->is_visible()) { - return TS->format_number(rtos(get_value())) + "\n\n" + TTR("Hold Ctrl to round to integers. Hold Shift for more precise changes."); +#ifdef OSX_ENABLED + const int key = KEY_META; +#else + const int key = KEY_CTRL; +#endif + return TS->format_number(rtos(get_value())) + "\n\n" + vformat(TTR("Hold %s to round to integers. Hold Shift for more precise changes."), find_keycode_name(key)); } return TS->format_number(rtos(get_value())); } @@ -116,7 +122,7 @@ void EditorSpinSlider::_gui_input(const Ref<InputEvent> &p_event) { pre_grab_value = get_max(); } - if (mm->is_ctrl_pressed()) { + if (mm->is_command_pressed()) { // If control was just pressed, don't make the value do a huge jump in magnitude. if (grabbing_spinner_dist_cache != 0) { pre_grab_value += grabbing_spinner_dist_cache * get_step(); diff --git a/editor/icons/TileMapHighlightSelected.svg b/editor/icons/TileMapHighlightSelected.svg new file mode 100644 index 0000000000..de8a291b8e --- /dev/null +++ b/editor/icons/TileMapHighlightSelected.svg @@ -0,0 +1 @@ +<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0" fill-rule="nonzero"><path d="m4.5 5.2011663h10.5v2.005h-10.5z" stroke-width="1.14115"/><g stroke-width=".862635"><path d="m2 4.2340105v-1h1l1 2z"/><path d="m2 8.2340105v1h1l1-2z"/><path d="m1.3786796 5.5269037-.70710677.7071068.70710677.7071068 2.1213204-.7071068z"/></g><path d="m4.5 1.7061663h10.5v2.005h-10.5z" stroke-width="1.14117"/><g stroke-width="1.14116"><path d="m4.5 8.7011663h10.5v2.0049997h-10.5z"/><path d="m4.5 12.206166h10.5v2.005h-10.5z"/></g></g></svg> diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index cd16353d6a..61d435cde1 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -81,8 +81,8 @@ public: SnapDialog() { const int SPIN_BOX_GRID_RANGE = 16384; const int SPIN_BOX_ROTATION_RANGE = 360; - const float SPIN_BOX_SCALE_MIN = 0.01f; - const float SPIN_BOX_SCALE_MAX = 100; + const real_t SPIN_BOX_SCALE_MIN = 0.01; + const real_t SPIN_BOX_SCALE_MAX = 100; Label *label; VBoxContainer *container; @@ -210,7 +210,7 @@ public: child_container->add_child(scale_step); } - void set_fields(const Point2 p_grid_offset, const Point2 p_grid_step, const int p_primary_grid_steps, const float p_rotation_offset, const float p_rotation_step, const float p_scale_step) { + void set_fields(const Point2 p_grid_offset, const Point2 p_grid_step, const int p_primary_grid_steps, const real_t p_rotation_offset, const real_t p_rotation_step, const real_t p_scale_step) { grid_offset_x->set_value(p_grid_offset.x); grid_offset_y->set_value(p_grid_offset.y); grid_step_x->set_value(p_grid_step.x); @@ -221,7 +221,7 @@ public: scale_step->set_value(p_scale_step); } - void get_fields(Point2 &p_grid_offset, Point2 &p_grid_step, int &p_primary_grid_steps, float &p_rotation_offset, float &p_rotation_step, float &p_scale_step) { + void get_fields(Point2 &p_grid_offset, Point2 &p_grid_step, int &p_primary_grid_steps, real_t &p_rotation_offset, real_t &p_rotation_step, real_t &p_scale_step) { p_grid_offset = Point2(grid_offset_x->get_value(), grid_offset_y->get_value()); p_grid_step = Point2(grid_step_x->get_value(), grid_step_y->get_value()); p_primary_grid_steps = int(primary_grid_steps->get_value()); @@ -249,12 +249,12 @@ bool CanvasItemEditor::_is_node_movable(const Node *p_node, bool p_popup_warning } void CanvasItemEditor::_snap_if_closer_float( - float p_value, - float &r_current_snap, SnapTarget &r_current_snap_target, - float p_target_value, SnapTarget p_snap_target, - float p_radius) { - float radius = p_radius / zoom; - float dist = Math::abs(p_value - p_target_value); + const real_t p_value, + real_t &r_current_snap, SnapTarget &r_current_snap_target, + const real_t p_target_value, const SnapTarget p_snap_target, + const real_t p_radius) { + const real_t radius = p_radius / zoom; + const real_t dist = Math::abs(p_value - p_target_value); if ((p_radius < 0 || dist < radius) && (r_current_snap_target == SNAP_TARGET_NONE || dist < Math::abs(r_current_snap - p_value))) { r_current_snap = p_target_value; r_current_snap_target = p_snap_target; @@ -264,9 +264,9 @@ void CanvasItemEditor::_snap_if_closer_float( void CanvasItemEditor::_snap_if_closer_point( Point2 p_value, Point2 &r_current_snap, SnapTarget (&r_current_snap_target)[2], - Point2 p_target_value, SnapTarget p_snap_target, - real_t rotation, - float p_radius) { + Point2 p_target_value, const SnapTarget p_snap_target, + const real_t rotation, + const real_t p_radius) { Transform2D rot_trans = Transform2D(rotation, Point2()); p_value = rot_trans.inverse().xform(p_value); p_target_value = rot_trans.inverse().xform(p_target_value); @@ -459,7 +459,7 @@ Point2 CanvasItemEditor::snap_point(Point2 p_target, unsigned int p_modes, unsig return output; } -float CanvasItemEditor::snap_angle(float p_target, float p_start) const { +real_t CanvasItemEditor::snap_angle(real_t p_target, real_t p_start) const { if (((smart_snap_active || snap_rotation) ^ Input::get_singleton()->is_key_pressed(KEY_CTRL)) && snap_rotation_step != 0) { if (snap_relative) { return Math::snapped(p_target - snap_rotation_offset, snap_rotation_step) + snap_rotation_offset + (p_start - (int)(p_start / snap_rotation_step) * snap_rotation_step); @@ -923,7 +923,7 @@ bool CanvasItemEditor::_gui_input_rulers_and_guides(const Ref<InputEvent> &p_eve } // Hover over guides - float minimum = 1e20; + real_t minimum = 1e20; is_hovering_h_guide = false; is_hovering_v_guide = false; @@ -1264,7 +1264,7 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event, bo if (pan_gesture.is_valid() && !p_already_accepted) { // If ctrl key pressed, then zoom instead of pan. if (pan_gesture->is_ctrl_pressed()) { - const float factor = pan_gesture->get_delta().y; + const real_t factor = pan_gesture->get_delta().y; zoom_widget->set_zoom_by_increments(1); if (factor != 1.f) { @@ -1490,9 +1490,9 @@ bool CanvasItemEditor::_gui_input_anchors(const Ref<InputEvent> &p_event) { anchor_pos[i] = (transform * control->get_global_transform_with_canvas()).xform(_anchor_to_position(control, anchor_pos[i])); anchor_rects[i] = Rect2(anchor_pos[i], anchor_handle->get_size()); if (control->is_layout_rtl()) { - anchor_rects[i].position -= anchor_handle->get_size() * Vector2(float(i == 1 || i == 2), float(i <= 1)); + anchor_rects[i].position -= anchor_handle->get_size() * Vector2(real_t(i == 1 || i == 2), real_t(i <= 1)); } else { - anchor_rects[i].position -= anchor_handle->get_size() * Vector2(float(i == 0 || i == 3), float(i <= 1)); + anchor_rects[i].position -= anchor_handle->get_size() * Vector2(real_t(i == 0 || i == 3), real_t(i <= 1)); } } @@ -1646,7 +1646,7 @@ bool CanvasItemEditor::_gui_input_resize(const Ref<InputEvent> &p_event) { }; DragType resize_drag = DRAG_NONE; - float radius = (select_handle->get_size().width / 2) * 1.5; + real_t radius = (select_handle->get_size().width / 2) * 1.5; for (int i = 0; i < 4; i++) { int prev = (i + 3) % 4; @@ -1692,7 +1692,7 @@ bool CanvasItemEditor::_gui_input_resize(const Ref<InputEvent> &p_event) { bool symmetric = m->is_alt_pressed(); Rect2 local_rect = canvas_item->_edit_get_rect(); - float aspect = local_rect.get_size().y / local_rect.get_size().x; + real_t aspect = local_rect.get_size().y / local_rect.get_size().x; Point2 current_begin = local_rect.get_position(); Point2 current_end = local_rect.get_position() + local_rect.get_size(); Point2 max_begin = (symmetric) ? (current_begin + current_end - canvas_item->_edit_get_minimum_size()) / 2.0 : current_end - canvas_item->_edit_get_minimum_size(); @@ -1883,7 +1883,7 @@ bool CanvasItemEditor::_gui_input_scale(const Ref<InputEvent> &p_event) { Size2 scale = canvas_item->call("get_scale"); Size2 original_scale = scale; - float ratio = scale.y / scale.x; + real_t ratio = scale.y / scale.x; if (drag_type == DRAG_SCALE_BOTH) { Size2 scale_factor = drag_to_local / drag_from_local; if (uniform) { @@ -2012,11 +2012,13 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) { drag_to = transform.affine_inverse().xform(m->get_position()); Point2 previous_pos; - if (drag_selection.size() == 1) { - Transform2D xform = drag_selection[0]->get_global_transform_with_canvas() * drag_selection[0]->get_transform().affine_inverse(); - previous_pos = xform.xform(drag_selection[0]->_edit_get_position()); - } else { - previous_pos = _get_encompassing_rect_from_list(drag_selection).position; + if (!drag_selection.is_empty()) { + if (drag_selection.size() == 1) { + Transform2D xform = drag_selection[0]->get_global_transform_with_canvas() * drag_selection[0]->get_transform().affine_inverse(); + previous_pos = xform.xform(drag_selection[0]->_edit_get_position()); + } else { + previous_pos = _get_encompassing_rect_from_list(drag_selection).position; + } } Point2 new_pos = snap_point(previous_pos + (drag_to - drag_from), SNAP_GRID | SNAP_GUIDES | SNAP_PIXEL | SNAP_NODE_PARENT | SNAP_NODE_ANCHORS | SNAP_OTHER_NODES, 0, nullptr, drag_selection); @@ -2539,7 +2541,7 @@ void CanvasItemEditor::_update_cursor() { List<CanvasItem *> selection = _get_edited_canvas_items(); if (selection.size() == 1) { - float angle = Math::fposmod((double)selection[0]->get_global_transform_with_canvas().get_rotation(), Math_PI); + const double angle = Math::fposmod((double)selection[0]->get_global_transform_with_canvas().get_rotation(), Math_PI); if (angle > Math_PI * 7.0 / 8.0) { rotation_array_index = 0; } else if (angle > Math_PI * 5.0 / 8.0) { @@ -2645,7 +2647,7 @@ void CanvasItemEditor::_draw_margin_at_position(int p_value, Point2 p_position, } } -void CanvasItemEditor::_draw_percentage_at_position(float p_value, Point2 p_position, Side p_side) { +void CanvasItemEditor::_draw_percentage_at_position(real_t p_value, Point2 p_position, Side p_side) { String str = TS->format_number(vformat("%.1f ", p_value * 100.0)) + TS->percent_sign(); if (p_value != 0) { _draw_text_at_position(p_position, str, p_side); @@ -2670,7 +2672,7 @@ void CanvasItemEditor::_draw_guides() { if (drag_type == DRAG_V_GUIDE && i == dragged_guide_index) { continue; } - float x = xform.xform(Point2(vguides[i], 0)).x; + real_t x = xform.xform(Point2(vguides[i], 0)).x; viewport->draw_line(Point2(x, 0), Point2(x, viewport->get_size().y), guide_color, Math::round(EDSCALE)); } } @@ -2681,7 +2683,7 @@ void CanvasItemEditor::_draw_guides() { if (drag_type == DRAG_H_GUIDE && i == dragged_guide_index) { continue; } - float y = xform.xform(Point2(0, hguides[i])).y; + real_t y = xform.xform(Point2(0, hguides[i])).y; viewport->draw_line(Point2(0, y), Point2(viewport->get_size().x, y), guide_color, Math::round(EDSCALE)); } } @@ -2745,7 +2747,7 @@ void CanvasItemEditor::_draw_rulers() { ruler_transform.scale_basis(Point2(2, 2)); } } else { - float basic_rule = 100; + real_t basic_rule = 100; for (int i = 0; basic_rule * zoom > 100; i++) { basic_rule /= (i % 2) ? 5.0 : 2.0; } @@ -2774,7 +2776,7 @@ void CanvasItemEditor::_draw_rulers() { Point2 position = (transform * ruler_transform * major_subdivide * minor_subdivide).xform(Point2(i, 0)).round(); if (i % (major_subdivision * minor_subdivision) == 0) { viewport->draw_line(Point2(position.x, 0), Point2(position.x, RULER_WIDTH), graduation_color, Math::round(EDSCALE)); - float val = (ruler_transform * major_subdivide * minor_subdivide).xform(Point2(i, 0)).x; + real_t val = (ruler_transform * major_subdivide * minor_subdivide).xform(Point2(i, 0)).x; viewport->draw_string(font, Point2(position.x + 2, font->get_height(font_size)), TS->format_number(vformat(((int)val == val) ? "%d" : "%.1f", val)), HALIGN_LEFT, -1, font_size, font_color); } else { if (i % minor_subdivision == 0) { @@ -2791,7 +2793,7 @@ void CanvasItemEditor::_draw_rulers() { Point2 position = (transform * ruler_transform * major_subdivide * minor_subdivide).xform(Point2(0, i)).round(); if (i % (major_subdivision * minor_subdivision) == 0) { viewport->draw_line(Point2(0, position.y), Point2(RULER_WIDTH, position.y), graduation_color, Math::round(EDSCALE)); - float val = (ruler_transform * major_subdivide * minor_subdivide).xform(Point2(0, i)).y; + real_t val = (ruler_transform * major_subdivide * minor_subdivide).xform(Point2(0, i)).y; Transform2D text_xform = Transform2D(-Math_PI / 2.0, Point2(font->get_height(font_size), position.y - 2)); viewport->draw_set_transform_matrix(viewport->get_transform() * text_xform); @@ -2924,8 +2926,8 @@ void CanvasItemEditor::_draw_ruler_tool() { viewport->draw_string(font, text_pos, TS->format_number(vformat("%.1f px", length_vector.length())), HALIGN_LEFT, -1, font_size, font_color, outline_size, outline_color); if (draw_secondary_lines) { - const float horizontal_angle_rad = atan2(length_vector.y, length_vector.x); - const float vertical_angle_rad = Math_PI / 2.0 - horizontal_angle_rad; + const real_t horizontal_angle_rad = atan2(length_vector.y, length_vector.x); + const real_t vertical_angle_rad = Math_PI / 2.0 - horizontal_angle_rad; const int horizontal_angle = round(180 * horizontal_angle_rad / Math_PI); const int vertical_angle = round(180 * vertical_angle_rad / Math_PI); @@ -2961,28 +2963,28 @@ void CanvasItemEditor::_draw_ruler_tool() { // Angle arcs int arc_point_count = 8; - float arc_radius_max_length_percent = 0.1; - float ruler_length = length_vector.length() * zoom; - float arc_max_radius = 50.0; - float arc_line_width = 2.0; + real_t arc_radius_max_length_percent = 0.1; + real_t ruler_length = length_vector.length() * zoom; + real_t arc_max_radius = 50.0; + real_t arc_line_width = 2.0; const Vector2 end_to_begin = (end - begin); - float arc_1_start_angle = + real_t arc_1_start_angle = end_to_begin.x < 0 ? (end_to_begin.y < 0 ? 3.0 * Math_PI / 2.0 - vertical_angle_rad : Math_PI / 2.0) : (end_to_begin.y < 0 ? 3.0 * Math_PI / 2.0 : Math_PI / 2.0 - vertical_angle_rad); - float arc_1_end_angle = arc_1_start_angle + vertical_angle_rad; + real_t arc_1_end_angle = arc_1_start_angle + vertical_angle_rad; // Constrain arc to triangle height & max size - float arc_1_radius = MIN(MIN(arc_radius_max_length_percent * ruler_length, ABS(end_to_begin.y)), arc_max_radius); + real_t arc_1_radius = MIN(MIN(arc_radius_max_length_percent * ruler_length, ABS(end_to_begin.y)), arc_max_radius); - float arc_2_start_angle = + real_t arc_2_start_angle = end_to_begin.x < 0 ? (end_to_begin.y < 0 ? 0.0 : -horizontal_angle_rad) : (end_to_begin.y < 0 ? Math_PI - horizontal_angle_rad : Math_PI); - float arc_2_end_angle = arc_2_start_angle + horizontal_angle_rad; + real_t arc_2_end_angle = arc_2_start_angle + horizontal_angle_rad; // Constrain arc to triangle width & max size - float arc_2_radius = MIN(MIN(arc_radius_max_length_percent * ruler_length, ABS(end_to_begin.x)), arc_max_radius); + real_t arc_2_radius = MIN(MIN(arc_radius_max_length_percent * ruler_length, ABS(end_to_begin.x)), arc_max_radius); viewport->draw_arc(begin, arc_1_radius, arc_1_start_angle, arc_1_end_angle, arc_point_count, ruler_primary_color, Math::round(EDSCALE * arc_line_width)); viewport->draw_arc(end, arc_2_radius, arc_2_start_angle, arc_2_end_angle, arc_point_count, ruler_primary_color, Math::round(EDSCALE * arc_line_width)); @@ -3020,7 +3022,7 @@ void CanvasItemEditor::_draw_control_anchors(Control *control) { RID ci = viewport->get_canvas_item(); if (tool == TOOL_SELECT && !Object::cast_to<Container>(control->get_parent())) { // Compute the anchors - float anchors_values[4]; + real_t anchors_values[4]; anchors_values[0] = control->get_anchor(SIDE_LEFT); anchors_values[1] = control->get_anchor(SIDE_TOP); anchors_values[2] = control->get_anchor(SIDE_RIGHT); @@ -3059,7 +3061,7 @@ void CanvasItemEditor::_draw_control_helpers(Control *control) { Color color_base = Color(0.8, 0.8, 0.8, 0.5); // Compute the anchors - float anchors_values[4]; + real_t anchors_values[4]; anchors_values[0] = control->get_anchor(SIDE_LEFT); anchors_values[1] = control->get_anchor(SIDE_TOP); anchors_values[2] = control->get_anchor(SIDE_RIGHT); @@ -3105,7 +3107,7 @@ void CanvasItemEditor::_draw_control_helpers(Control *control) { Vector2 line_starts[4]; Vector2 line_ends[4]; for (int i = 0; i < 4; i++) { - float anchor_val = (i >= 2) ? ANCHOR_END - anchors_values[i] : anchors_values[i]; + real_t anchor_val = (i >= 2) ? ANCHOR_END - anchors_values[i] : anchors_values[i]; line_starts[i] = corners_pos[i].lerp(corners_pos[(i + 1) % 4], anchor_val); line_ends[i] = corners_pos[(i + 3) % 4].lerp(corners_pos[(i + 2) % 4], anchor_val); anchor_snapped = anchors_values[i] == 0.0 || anchors_values[i] == 0.5 || anchors_values[i] == 1.0; @@ -3113,7 +3115,7 @@ void CanvasItemEditor::_draw_control_helpers(Control *control) { } // Display the percentages next to the lines - float percent_val; + real_t percent_val; percent_val = anchors_values[(dragged_anchor + 2) % 4] - anchors_values[dragged_anchor]; percent_val = (dragged_anchor >= 2) ? -percent_val : percent_val; _draw_percentage_at_position(percent_val, (anchors_pos[dragged_anchor] + anchors_pos[(dragged_anchor + 1) % 4]) / 2, (Side)((dragged_anchor + 1) % 4)); @@ -3132,9 +3134,9 @@ void CanvasItemEditor::_draw_control_helpers(Control *control) { } // Draw the margin values and the node width/height when dragging control side - float ratio = 0.33; + const real_t ratio = 0.33; Transform2D parent_transform = xform * control->get_transform().affine_inverse(); - float node_pos_in_parent[4]; + real_t node_pos_in_parent[4]; Rect2 parent_rect = control->get_parent_anchorable_rect(); @@ -3451,10 +3453,10 @@ void CanvasItemEditor::_draw_straight_line(Point2 p_from, Point2 p_to, Color p_c points.push_back(Point2(0, to.y)); points.push_back(Point2(viewport_size.x, to.y)); } else { - float y_for_zero_x = (to.y * from.x - from.y * to.x) / (from.x - to.x); - float x_for_zero_y = (to.x * from.y - from.x * to.y) / (from.y - to.y); - float y_for_viewport_x = ((to.y - from.y) * (viewport_size.x - from.x)) / (to.x - from.x) + from.y; - float x_for_viewport_y = ((to.x - from.x) * (viewport_size.y - from.y)) / (to.y - from.y) + from.x; // faux + real_t y_for_zero_x = (to.y * from.x - from.y * to.x) / (from.x - to.x); + real_t x_for_zero_y = (to.x * from.y - from.x * to.y) / (from.y - to.y); + real_t y_for_viewport_x = ((to.y - from.y) * (viewport_size.x - from.x)) / (to.x - from.x) + from.y; + real_t x_for_viewport_y = ((to.x - from.x) * (viewport_size.y - from.y)) / (to.y - from.y) + from.x; // faux //bool start_set = false; if (y_for_zero_x >= 0 && y_for_zero_x <= viewport_size.y) { @@ -3600,7 +3602,7 @@ void CanvasItemEditor::_draw_locks_and_groups(Node *p_node, const Transform2D &p RID viewport_canvas_item = viewport->get_canvas_item(); if (canvas_item) { - float offset = 0; + real_t offset = 0; Ref<Texture2D> lock = get_theme_icon(SNAME("LockViewport"), SNAME("EditorIcons")); if (p_node->has_meta("_edit_lock_") && show_edit_locks) { @@ -3724,7 +3726,7 @@ void CanvasItemEditor::_notification(int p_what) { Control *control = Object::cast_to<Control>(canvas_item); if (control) { - float anchors[4]; + real_t anchors[4]; Vector2 pivot; pivot = control->get_pivot_offset(); @@ -4001,7 +4003,7 @@ void CanvasItemEditor::_update_scrollbars() { bool constrain_editor_view = bool(EditorSettings::get_singleton()->get("editors/2d/constrain_editor_view")); if (canvas_item_rect.size.height <= (local_rect.size.y / zoom)) { - float centered = -(size.y / 2) / zoom + screen_rect.y / 2; + real_t centered = -(size.y / 2) / zoom + screen_rect.y / 2; if (constrain_editor_view && ABS(centered - previous_update_view_offset.y) < ABS(centered - view_offset.y)) { view_offset.y = previous_update_view_offset.y; } @@ -4022,7 +4024,7 @@ void CanvasItemEditor::_update_scrollbars() { } if (canvas_item_rect.size.width <= (local_rect.size.x / zoom)) { - float centered = -(size.x / 2) / zoom + screen_rect.x / 2; + real_t centered = -(size.x / 2) / zoom + screen_rect.x / 2; if (constrain_editor_view && ABS(centered - previous_update_view_offset.x) < ABS(centered - view_offset.x)) { view_offset.x = previous_update_view_offset.x; } @@ -4089,7 +4091,7 @@ void CanvasItemEditor::_popup_warning_temporarily(Control *p_control, const floa info_overlay->set_offset(SIDE_LEFT, (show_rulers ? RULER_WIDTH : 0) + 10); } -void CanvasItemEditor::_update_scroll(float) { +void CanvasItemEditor::_update_scroll(real_t) { if (updating_scroll) { return; } @@ -4183,14 +4185,14 @@ void CanvasItemEditor::_set_anchors_preset(Control::LayoutPreset p_preset) { undo_redo->commit_action(); } -void CanvasItemEditor::_zoom_on_position(float p_zoom, Point2 p_position) { +void CanvasItemEditor::_zoom_on_position(real_t p_zoom, Point2 p_position) { p_zoom = CLAMP(p_zoom, MIN_ZOOM, MAX_ZOOM); if (p_zoom == zoom) { return; } - float prev_zoom = zoom; + real_t prev_zoom = zoom; zoom = p_zoom; view_offset += p_position / prev_zoom - p_position / zoom; @@ -4199,7 +4201,7 @@ void CanvasItemEditor::_zoom_on_position(float p_zoom, Point2 p_position) { // in small details (texts, lines). // This correction adds a jitter movement when zooming, so we correct only when the // zoom factor is an integer. (in the other cases, all pixels won't be aligned anyway) - float closest_zoom_factor = Math::round(zoom); + const real_t closest_zoom_factor = Math::round(zoom); if (Math::is_zero_approx(zoom - closest_zoom_factor)) { // make sure scene pixel at view_offset is aligned on a screen pixel Vector2 view_offset_int = view_offset.floor(); @@ -4211,7 +4213,7 @@ void CanvasItemEditor::_zoom_on_position(float p_zoom, Point2 p_position) { update_viewport(); } -void CanvasItemEditor::_update_zoom(float p_zoom) { +void CanvasItemEditor::_update_zoom(real_t p_zoom) { _zoom_on_position(p_zoom, viewport_scrollable->get_size() / 2.0); } @@ -4896,8 +4898,8 @@ void CanvasItemEditor::_focus_selection(int p_op) { } else { // VIEW_FRAME_TO_SELECTION if (rect.size.x > CMP_EPSILON && rect.size.y > CMP_EPSILON) { - float scale_x = viewport->get_size().x / rect.size.x; - float scale_y = viewport->get_size().y / rect.size.y; + real_t scale_x = viewport->get_size().x / rect.size.x; + real_t scale_y = viewport->get_size().y / rect.size.y; zoom = scale_x < scale_y ? scale_x : scale_y; zoom *= 0.90; viewport->update(); @@ -4962,7 +4964,7 @@ void CanvasItemEditor::set_state(const Dictionary &p_state) { if (state.has("zoom")) { // Compensate the editor scale, so that the editor scale can be changed // and the zoom level will still be the same (relative to the editor scale). - zoom = float(p_state["zoom"]) * MAX(1, EDSCALE); + zoom = real_t(p_state["zoom"]) * MAX(1, EDSCALE); zoom_widget->set_zoom(zoom); } diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index d466032588..1f59dfbc49 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -48,10 +48,10 @@ class CanvasItemEditorSelectedItem : public Object { public: Transform2D prev_xform; - float prev_rot = 0; + real_t prev_rot = 0; Rect2 prev_rect; Vector2 prev_pivot; - float prev_anchors[4] = { 0.0f }; + real_t prev_anchors[4] = { (real_t)0.0 }; Transform2D pre_drag_xform; Rect2 pre_drag_rect; @@ -250,7 +250,7 @@ private: bool show_edit_locks; bool show_transformation_gizmos; - float zoom; + real_t zoom; Point2 view_offset; Point2 previous_update_view_offset; @@ -262,9 +262,9 @@ private: int primary_grid_steps; int grid_step_multiplier; - float snap_rotation_step; - float snap_rotation_offset; - float snap_scale_step; + real_t snap_rotation_step; + real_t snap_rotation_offset; + real_t snap_scale_step; bool smart_snap_active; bool grid_snap_active; @@ -292,7 +292,7 @@ private: struct _SelectResult { CanvasItem *item = nullptr; - float z_index = 0; + real_t z_index = 0; bool has_z = true; _FORCE_INLINE_ bool operator<(const _SelectResult &p_rr) const { return has_z && p_rr.has_z ? p_rr.z_index < z_index : p_rr.has_z; @@ -309,7 +309,7 @@ private: struct BoneList { Transform2D xform; - float length = 0.f; + real_t length = 0; uint64_t last_pass = 0; }; @@ -332,7 +332,7 @@ private: struct PoseClipboard { Vector2 pos; Vector2 scale; - float rot = 0; + real_t rot = 0; ObjectID id; }; List<PoseClipboard> pose_clipboard; @@ -432,7 +432,7 @@ private: void _popup_callback(int p_op); bool updating_scroll; - void _update_scroll(float); + void _update_scroll(real_t); void _update_scrollbars(); void _append_canvas_item(CanvasItem *p_item); void _snap_changed(); @@ -459,7 +459,7 @@ private: void _draw_text_at_position(Point2 p_position, String p_string, Side p_side); void _draw_margin_at_position(int p_value, Point2 p_position, Side p_side); - void _draw_percentage_at_position(float p_value, Point2 p_position, Side p_side); + void _draw_percentage_at_position(real_t p_value, Point2 p_position, Side p_side); void _draw_straight_line(Point2 p_from, Point2 p_to, Color p_color); void _draw_smart_snapping(); @@ -501,16 +501,16 @@ private: SnapTarget snap_target[2]; Transform2D snap_transform; void _snap_if_closer_float( - float p_value, - float &r_current_snap, SnapTarget &r_current_snap_target, - float p_target_value, SnapTarget p_snap_target, - float p_radius = 10.0); + const real_t p_value, + real_t &r_current_snap, SnapTarget &r_current_snap_target, + const real_t p_target_value, const SnapTarget p_snap_target, + const real_t p_radius = 10.0); void _snap_if_closer_point( Point2 p_value, Point2 &r_current_snap, SnapTarget (&r_current_snap_target)[2], - Point2 p_target_value, SnapTarget p_snap_target, - real_t rotation = 0.0, - float p_radius = 10.0); + Point2 p_target_value, const SnapTarget p_snap_target, + const real_t rotation = 0.0, + const real_t p_radius = 10.0); void _snap_other_nodes( const Point2 p_value, const Transform2D p_transform_to_snap, @@ -527,8 +527,8 @@ private: VBoxContainer *controls_vb; EditorZoomWidget *zoom_widget; - void _update_zoom(float p_zoom); - void _zoom_on_position(float p_zoom, Point2 p_position = Point2()); + void _update_zoom(real_t p_zoom); + void _zoom_on_position(real_t p_zoom, Point2 p_position = Point2()); void _button_toggle_smart_snap(bool p_status); void _button_toggle_grid_snap(bool p_status); void _button_override_camera(bool p_pressed); @@ -557,28 +557,6 @@ protected: HBoxContainer *get_panel_hb() { return hb; } - struct compare_items_x { - bool operator()(const CanvasItem *a, const CanvasItem *b) const { - return a->get_global_transform().elements[2].x < b->get_global_transform().elements[2].x; - } - }; - - struct compare_items_y { - bool operator()(const CanvasItem *a, const CanvasItem *b) const { - return a->get_global_transform().elements[2].y < b->get_global_transform().elements[2].y; - } - }; - - struct proj_vector2_x { - float get(const Vector2 &v) { return v.x; } - void set(Vector2 &v, float f) { v.x = f; } - }; - - struct proj_vector2_y { - float get(const Vector2 &v) { return v.y; } - void set(Vector2 &v, float f) { v.y = f; } - }; - template <class P, class C> void space_selected_items(); @@ -599,7 +577,7 @@ public: }; Point2 snap_point(Point2 p_target, unsigned int p_modes = SNAP_DEFAULT, unsigned int p_forced_modes = 0, const CanvasItem *p_self_canvas_item = nullptr, List<CanvasItem *> p_other_nodes_exceptions = List<CanvasItem *>()); - float snap_angle(float p_target, float p_start = 0) const; + real_t snap_angle(real_t p_target, real_t p_start = 0) const; Transform2D get_canvas_transform() const { return transform; } diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp index 81c340e9a4..d7618b973e 100644 --- a/editor/plugins/editor_preview_plugins.cpp +++ b/editor/plugins/editor_preview_plugins.cpp @@ -724,7 +724,7 @@ Ref<Texture2D> EditorMeshPreviewPlugin::generate(const RES &p_from, const Size2 xform.basis = Basis().rotated(Vector3(0, 1, 0), -Math_PI * 0.125); xform.basis = Basis().rotated(Vector3(1, 0, 0), Math_PI * 0.125) * xform.basis; AABB rot_aabb = xform.xform(aabb); - float m = MAX(rot_aabb.size.x, rot_aabb.size.y) * 0.5; + real_t m = MAX(rot_aabb.size.x, rot_aabb.size.y) * 0.5; if (m == 0) { return Ref<Texture2D>(); } diff --git a/editor/plugins/node_3d_editor_gizmos.cpp b/editor/plugins/node_3d_editor_gizmos.cpp index 2138f943da..46e15d97bf 100644 --- a/editor/plugins/node_3d_editor_gizmos.cpp +++ b/editor/plugins/node_3d_editor_gizmos.cpp @@ -320,7 +320,7 @@ void EditorNode3DGizmo::add_vertices(const Vector<Vector3> &p_vertices, const Re instances.push_back(ins); } -void EditorNode3DGizmo::add_unscaled_billboard(const Ref<Material> &p_material, float p_scale, const Color &p_modulate) { +void EditorNode3DGizmo::add_unscaled_billboard(const Ref<Material> &p_material, real_t p_scale, const Color &p_modulate) { ERR_FAIL_COND(!spatial_node); Instance ins; diff --git a/editor/plugins/node_3d_editor_gizmos.h b/editor/plugins/node_3d_editor_gizmos.h index 61ee3a95a9..f303a61607 100644 --- a/editor/plugins/node_3d_editor_gizmos.h +++ b/editor/plugins/node_3d_editor_gizmos.h @@ -84,7 +84,7 @@ public: void add_mesh(const Ref<ArrayMesh> &p_mesh, const Ref<Material> &p_material = Ref<Material>(), const Transform3D &p_xform = Transform3D(), const Ref<SkinReference> &p_skin_reference = Ref<SkinReference>()); void add_collision_segments(const Vector<Vector3> &p_lines); void add_collision_triangles(const Ref<TriangleMesh> &p_tmesh); - void add_unscaled_billboard(const Ref<Material> &p_material, float p_scale = 1, const Color &p_modulate = Color(1, 1, 1)); + void add_unscaled_billboard(const Ref<Material> &p_material, real_t p_scale = 1, const Color &p_modulate = Color(1, 1, 1)); void add_handles(const Vector<Vector3> &p_handles, const Ref<Material> &p_material, const Vector<int> &p_ids = Vector<int>(), bool p_billboard = false, bool p_secondary = false); void add_solid_box(Ref<Material> &p_material, Vector3 p_size, Vector3 p_position = Vector3(), const Transform3D &p_xform = Transform3D()); diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index af04c9566a..c342d30012 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -107,8 +107,8 @@ void ViewportRotationControl::_notification(int p_what) { } void ViewportRotationControl::_draw() { - Vector2i center = get_size() / 2.0; - float radius = get_size().x / 2.0; + const Vector2i center = get_size() / 2.0; + const real_t radius = get_size().x / 2.0; if (focused_axis > -2 || orbiting) { draw_circle(center, radius, Color(0.5, 0.5, 0.5, 0.25)); @@ -149,15 +149,13 @@ void ViewportRotationControl::_draw_axis(const Axis2D &p_axis) { } void ViewportRotationControl::_get_sorted_axis(Vector<Axis2D> &r_axis) { - Vector2i center = get_size() / 2.0; - float radius = get_size().x / 2.0; - - float axis_radius = radius - AXIS_CIRCLE_RADIUS - 2.0 * EDSCALE; - Basis camera_basis = viewport->to_camera_transform(viewport->cursor).get_basis().inverse(); + const Vector2i center = get_size() / 2.0; + const real_t radius = get_size().x / 2.0 - AXIS_CIRCLE_RADIUS - 2.0 * EDSCALE; + const Basis camera_basis = viewport->to_camera_transform(viewport->cursor).get_basis().inverse(); for (int i = 0; i < 3; ++i) { Vector3 axis_3d = camera_basis.get_axis(i); - Vector2i axis_vector = Vector2(axis_3d.x, -axis_3d.y) * axis_radius; + Vector2i axis_vector = Vector2(axis_3d.x, -axis_3d.y) * radius; if (Math::abs(axis_3d.z) < 1.0) { Axis2D pos_axis; @@ -259,7 +257,7 @@ void ViewportRotationControl::_bind_methods() { ClassDB::bind_method(D_METHOD("_gui_input"), &ViewportRotationControl::_gui_input); } -void Node3DEditorViewport::_update_camera(float p_interp_delta) { +void Node3DEditorViewport::_update_camera(real_t p_interp_delta) { bool is_orthogonal = camera->get_projection() == Camera3D::PROJECTION_ORTHOGONAL; Cursor old_camera_cursor = camera_cursor; @@ -279,7 +277,7 @@ void Node3DEditorViewport::_update_camera(float p_interp_delta) { // We interpolate a different point here, because in freelook mode the focus point (cursor.pos) orbits around eye_pos camera_cursor.eye_pos = old_camera_cursor.eye_pos.lerp(cursor.eye_pos, CLAMP(factor, 0, 1)); - float orbit_inertia = EDITOR_GET("editors/3d/navigation_feel/orbit_inertia"); + real_t orbit_inertia = EDITOR_GET("editors/3d/navigation_feel/orbit_inertia"); orbit_inertia = MAX(0.0001, orbit_inertia); camera_cursor.x_rot = Math::lerp(old_camera_cursor.x_rot, cursor.x_rot, MIN(1.f, p_interp_delta * (1 / orbit_inertia))); camera_cursor.y_rot = Math::lerp(old_camera_cursor.y_rot, cursor.y_rot, MIN(1.f, p_interp_delta * (1 / orbit_inertia))); @@ -297,13 +295,13 @@ void Node3DEditorViewport::_update_camera(float p_interp_delta) { } else { //when not being manipulated, move softly - float free_orbit_inertia = EDITOR_GET("editors/3d/navigation_feel/orbit_inertia"); - float free_translation_inertia = EDITOR_GET("editors/3d/navigation_feel/translation_inertia"); + real_t free_orbit_inertia = EDITOR_GET("editors/3d/navigation_feel/orbit_inertia"); + real_t free_translation_inertia = EDITOR_GET("editors/3d/navigation_feel/translation_inertia"); //when being manipulated, move more quickly - float manip_orbit_inertia = EDITOR_GET("editors/3d/navigation_feel/manipulation_orbit_inertia"); - float manip_translation_inertia = EDITOR_GET("editors/3d/navigation_feel/manipulation_translation_inertia"); + real_t manip_orbit_inertia = EDITOR_GET("editors/3d/navigation_feel/manipulation_orbit_inertia"); + real_t manip_translation_inertia = EDITOR_GET("editors/3d/navigation_feel/manipulation_translation_inertia"); - float zoom_inertia = EDITOR_GET("editors/3d/navigation_feel/zoom_inertia"); + real_t zoom_inertia = EDITOR_GET("editors/3d/navigation_feel/zoom_inertia"); //determine if being manipulated bool manipulated = Input::get_singleton()->get_mouse_button_mask() & (2 | 4); @@ -311,8 +309,8 @@ void Node3DEditorViewport::_update_camera(float p_interp_delta) { manipulated |= Input::get_singleton()->is_key_pressed(KEY_ALT); manipulated |= Input::get_singleton()->is_key_pressed(KEY_CTRL); - float orbit_inertia = MAX(0.00001, manipulated ? manip_orbit_inertia : free_orbit_inertia); - float translation_inertia = MAX(0.0001, manipulated ? manip_translation_inertia : free_translation_inertia); + real_t orbit_inertia = MAX(0.00001, manipulated ? manip_orbit_inertia : free_orbit_inertia); + real_t translation_inertia = MAX(0.0001, manipulated ? manip_translation_inertia : free_translation_inertia); zoom_inertia = MAX(0.0001, zoom_inertia); camera_cursor.x_rot = Math::lerp(old_camera_cursor.x_rot, cursor.x_rot, MIN(1.f, p_interp_delta * (1 / orbit_inertia))); @@ -327,7 +325,7 @@ void Node3DEditorViewport::_update_camera(float p_interp_delta) { } camera_cursor.pos = old_camera_cursor.pos.lerp(cursor.pos, MIN(1.f, p_interp_delta * (1 / translation_inertia))); - camera_cursor.distance = Math::lerp(old_camera_cursor.distance, cursor.distance, MIN(1.f, p_interp_delta * (1 / zoom_inertia))); + camera_cursor.distance = Math::lerp(old_camera_cursor.distance, cursor.distance, MIN((real_t)1.0, p_interp_delta * (1 / zoom_inertia))); } } @@ -537,7 +535,7 @@ ObjectID Node3DEditorViewport::_select_ray(const Point2 &p_pos) { continue; } - float dist = pos.distance_to(point); + const real_t dist = pos.distance_to(point); if (dist < 0) { continue; @@ -601,7 +599,7 @@ void Node3DEditorViewport::_find_items_at_pos(const Point2 &p_pos, Vector<_RayRe continue; } - float dist = pos.distance_to(point); + const real_t dist = pos.distance_to(point); if (dist < 0) { continue; @@ -646,7 +644,7 @@ void Node3DEditorViewport::_select_region() { return; //nothing really } - float z_offset = MAX(0.0, 5.0 - get_znear()); + const real_t z_offset = MAX(0.0, 5.0 - get_znear()); Vector3 box[4] = { Vector3( @@ -912,20 +910,19 @@ bool Node3DEditorViewport::_transform_gizmo_select(const Vector2 &p_screenpos, b Vector3 ray = _get_ray(Vector2(p_screenpos.x, p_screenpos.y)); Transform3D gt = spatial_editor->get_gizmo_transform(); - float gs = gizmo_scale; if (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_MOVE) { int col_axis = -1; - float col_d = 1e20; + real_t col_d = 1e20; for (int i = 0; i < 3; i++) { - Vector3 grabber_pos = gt.origin + gt.basis.get_axis(i) * gs * (GIZMO_ARROW_OFFSET + (GIZMO_ARROW_SIZE * 0.5)); - float grabber_radius = gs * GIZMO_ARROW_SIZE; + const Vector3 grabber_pos = gt.origin + gt.basis.get_axis(i) * gizmo_scale * (GIZMO_ARROW_OFFSET + (GIZMO_ARROW_SIZE * 0.5)); + const real_t grabber_radius = gizmo_scale * GIZMO_ARROW_SIZE; Vector3 r; if (Geometry3D::segment_intersects_sphere(ray_pos, ray_pos + ray * MAX_Z, grabber_pos, grabber_radius, &r)) { - float d = r.distance_to(ray_pos); + const real_t d = r.distance_to(ray_pos); if (d < col_d) { col_d = d; col_axis = i; @@ -944,17 +941,17 @@ bool Node3DEditorViewport::_transform_gizmo_select(const Vector2 &p_screenpos, b // Allow some tolerance to make the plane easier to click, // even if the click is actually slightly outside the plane. - const Vector3 grabber_pos = gt.origin + (ivec2 + ivec3) * gs * (GIZMO_PLANE_SIZE + GIZMO_PLANE_DST * 0.6667); + const Vector3 grabber_pos = gt.origin + (ivec2 + ivec3) * gizmo_scale * (GIZMO_PLANE_SIZE + GIZMO_PLANE_DST * 0.6667); Vector3 r; Plane plane(gt.origin, gt.basis.get_axis(i).normalized()); if (plane.intersects_ray(ray_pos, ray, &r)) { - float dist = r.distance_to(grabber_pos); + const real_t dist = r.distance_to(grabber_pos); // Allow some tolerance to make the plane easier to click, // even if the click is actually slightly outside the plane. - if (dist < (gs * GIZMO_PLANE_SIZE * 1.5)) { - float d = ray_pos.distance_to(r); + if (dist < (gizmo_scale * GIZMO_PLANE_SIZE * 1.5)) { + const real_t d = ray_pos.distance_to(r); if (d < col_d) { col_d = d; col_axis = i; @@ -991,12 +988,12 @@ bool Node3DEditorViewport::_transform_gizmo_select(const Vector2 &p_screenpos, b continue; } - float dist = r.distance_to(gt.origin); - Vector3 r_dir = (r - gt.origin).normalized(); + const real_t dist = r.distance_to(gt.origin); + const Vector3 r_dir = (r - gt.origin).normalized(); if (_get_camera_normal().dot(r_dir) <= 0.005) { - if (dist > gs * (GIZMO_CIRCLE_SIZE - GIZMO_RING_HALF_WIDTH) && dist < gs * (GIZMO_CIRCLE_SIZE + GIZMO_RING_HALF_WIDTH)) { - float d = ray_pos.distance_to(r); + if (dist > gizmo_scale * (GIZMO_CIRCLE_SIZE - GIZMO_RING_HALF_WIDTH) && dist < gizmo_scale * (GIZMO_CIRCLE_SIZE + GIZMO_RING_HALF_WIDTH)) { + const real_t d = ray_pos.distance_to(r); if (d < col_d) { col_d = d; col_axis = i; @@ -1023,13 +1020,13 @@ bool Node3DEditorViewport::_transform_gizmo_select(const Vector2 &p_screenpos, b float col_d = 1e20; for (int i = 0; i < 3; i++) { - Vector3 grabber_pos = gt.origin + gt.basis.get_axis(i) * gs * GIZMO_SCALE_OFFSET; - float grabber_radius = gs * GIZMO_ARROW_SIZE; + const Vector3 grabber_pos = gt.origin + gt.basis.get_axis(i) * gizmo_scale * GIZMO_SCALE_OFFSET; + const real_t grabber_radius = gizmo_scale * GIZMO_ARROW_SIZE; Vector3 r; if (Geometry3D::segment_intersects_sphere(ray_pos, ray_pos + ray * MAX_Z, grabber_pos, grabber_radius, &r)) { - float d = r.distance_to(ray_pos); + const real_t d = r.distance_to(ray_pos); if (d < col_d) { col_d = d; col_axis = i; @@ -1043,22 +1040,22 @@ bool Node3DEditorViewport::_transform_gizmo_select(const Vector2 &p_screenpos, b col_d = 1e20; for (int i = 0; i < 3; i++) { - Vector3 ivec2 = gt.basis.get_axis((i + 1) % 3).normalized(); - Vector3 ivec3 = gt.basis.get_axis((i + 2) % 3).normalized(); + const Vector3 ivec2 = gt.basis.get_axis((i + 1) % 3).normalized(); + const Vector3 ivec3 = gt.basis.get_axis((i + 2) % 3).normalized(); // Allow some tolerance to make the plane easier to click, // even if the click is actually slightly outside the plane. - Vector3 grabber_pos = gt.origin + (ivec2 + ivec3) * gs * (GIZMO_PLANE_SIZE + GIZMO_PLANE_DST * 0.6667); + const Vector3 grabber_pos = gt.origin + (ivec2 + ivec3) * gizmo_scale * (GIZMO_PLANE_SIZE + GIZMO_PLANE_DST * 0.6667); Vector3 r; Plane plane(gt.origin, gt.basis.get_axis(i).normalized()); if (plane.intersects_ray(ray_pos, ray, &r)) { - float dist = r.distance_to(grabber_pos); + const real_t dist = r.distance_to(grabber_pos); // Allow some tolerance to make the plane easier to click, // even if the click is actually slightly outside the plane. - if (dist < (gs * GIZMO_PLANE_SIZE * 1.5)) { - float d = ray_pos.distance_to(r); + if (dist < (gizmo_scale * GIZMO_PLANE_SIZE * 1.5)) { + const real_t d = ray_pos.distance_to(r); if (d < col_d) { col_d = d; col_axis = i; @@ -1293,7 +1290,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) { if (b.is_valid()) { emit_signal(SNAME("clicked"), this); - float zoom_factor = 1 + (ZOOM_FREELOOK_MULTIPLIER - 1) * b->get_factor(); + const real_t zoom_factor = 1 + (ZOOM_FREELOOK_MULTIPLIER - 1) * b->get_factor(); switch (b->get_button_index()) { case MOUSE_BUTTON_WHEEL_UP: { if (is_freelook_active()) { @@ -1780,13 +1777,13 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) { } } else { - float center_click_dist = click.distance_to(_edit.center); - float center_inters_dist = intersection.distance_to(_edit.center); + const real_t center_click_dist = click.distance_to(_edit.center); + const real_t center_inters_dist = intersection.distance_to(_edit.center); if (center_click_dist == 0) { break; } - float scale = center_inters_dist - center_click_dist; + const real_t scale = center_inters_dist - center_click_dist; motion = Vector3(scale, scale, scale); } @@ -3527,18 +3524,15 @@ void Node3DEditorViewport::update_transform_gizmo_view() { return; } - Vector3 camz = -camera_xform.get_basis().get_axis(2).normalized(); - Vector3 camy = -camera_xform.get_basis().get_axis(1).normalized(); - Plane p(camera_xform.origin, camz); - float gizmo_d = MAX(Math::abs(p.distance_to(xform.origin)), CMP_EPSILON); - float d0 = camera->unproject_position(camera_xform.origin + camz * gizmo_d).y; - float d1 = camera->unproject_position(camera_xform.origin + camz * gizmo_d + camy).y; - float dd = Math::abs(d0 - d1); - if (dd == 0) { - dd = 0.0001; - } + const Vector3 camz = -camera_xform.get_basis().get_axis(2).normalized(); + const Vector3 camy = -camera_xform.get_basis().get_axis(1).normalized(); + const Plane p(camera_xform.origin, camz); + const real_t gizmo_d = MAX(Math::abs(p.distance_to(xform.origin)), CMP_EPSILON); + const real_t d0 = camera->unproject_position(camera_xform.origin + camz * gizmo_d).y; + const real_t d1 = camera->unproject_position(camera_xform.origin + camz * gizmo_d + camy).y; + const real_t dd = MAX(Math::abs(d0 - d1), CMP_EPSILON); - float gizmo_size = EditorSettings::get_singleton()->get("editors/3d/manipulator_gizmo_size"); + const real_t gizmo_size = EditorSettings::get_singleton()->get("editors/3d/manipulator_gizmo_size"); // At low viewport heights, multiply the gizmo scale based on the viewport height. // This prevents the gizmo from growing very large and going outside the viewport. const int viewport_base_height = 400 * MAX(1, EDSCALE); @@ -3794,7 +3788,7 @@ void Node3DEditorViewport::focus_selection() { } if (count != 0) { - center /= float(count); + center /= count; } cursor.pos = center; @@ -4458,14 +4452,14 @@ void Node3DEditorViewportContainer::_gui_input(const Ref<InputEvent> &p_event) { } if (dragging_h) { - float new_ratio = drag_begin_ratio.x + (mm->get_position().x - drag_begin_pos.x) / get_size().width; + real_t new_ratio = drag_begin_ratio.x + (mm->get_position().x - drag_begin_pos.x) / get_size().width; new_ratio = CLAMP(new_ratio, 40 / get_size().width, (get_size().width - 40) / get_size().width); ratio_h = new_ratio; queue_sort(); update(); } if (dragging_v) { - float new_ratio = drag_begin_ratio.y + (mm->get_position().y - drag_begin_pos.y) / get_size().height; + real_t new_ratio = drag_begin_ratio.y + (mm->get_position().y - drag_begin_pos.y) / get_size().height; new_ratio = CLAMP(new_ratio, 40 / get_size().height, (get_size().height - 40) / get_size().height); ratio_v = new_ratio; queue_sort(); @@ -5016,13 +5010,13 @@ void Node3DEditor::set_state(const Dictionary &p_state) { } if (d.has("zfar")) { - settings_zfar->set_value(float(d["zfar"])); + settings_zfar->set_value(double(d["zfar"])); } if (d.has("znear")) { - settings_znear->set_value(float(d["znear"])); + settings_znear->set_value(double(d["znear"])); } if (d.has("fov")) { - settings_fov->set_value(float(d["fov"])); + settings_fov->set_value(double(d["fov"])); } if (d.has("show_grid")) { bool use = d["show_grid"]; @@ -7623,8 +7617,8 @@ Vector3 Node3DEditor::snap_point(Vector3 p_target, Vector3 p_start) const { return p_target; } -float Node3DEditor::get_translate_snap() const { - float snap_value; +double Node3DEditor::get_translate_snap() const { + double snap_value; if (Input::get_singleton()->is_key_pressed(KEY_SHIFT)) { snap_value = snap_translate->get_text().to_float() / 10.0; } else { @@ -7634,8 +7628,8 @@ float Node3DEditor::get_translate_snap() const { return snap_value; } -float Node3DEditor::get_rotate_snap() const { - float snap_value; +double Node3DEditor::get_rotate_snap() const { + double snap_value; if (Input::get_singleton()->is_key_pressed(KEY_SHIFT)) { snap_value = snap_rotate->get_text().to_float() / 3.0; } else { @@ -7645,8 +7639,8 @@ float Node3DEditor::get_rotate_snap() const { return snap_value; } -float Node3DEditor::get_scale_snap() const { - float snap_value; +double Node3DEditor::get_scale_snap() const { + double snap_value; if (Input::get_singleton()->is_key_pressed(KEY_SHIFT)) { snap_value = snap_scale->get_text().to_float() / 2.0; } else { diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h index 6ac3345daf..cb2edbcbc1 100644 --- a/editor/plugins/node_3d_editor_plugin.h +++ b/editor/plugins/node_3d_editor_plugin.h @@ -201,7 +201,7 @@ private: bool orthogonal; bool auto_orthogonal; bool lock_rotation; - float gizmo_scale; + real_t gizmo_scale; bool freelook_active; real_t freelook_speed; @@ -221,7 +221,7 @@ private: struct _RayResult { Node3D *item = nullptr; - float depth = 0; + real_t depth = 0; _FORCE_INLINE_ bool operator<(const _RayResult &p_rr) const { return depth < p_rr.depth; } }; @@ -306,7 +306,7 @@ private: struct Cursor { Vector3 pos; - float x_rot, y_rot, distance; + real_t x_rot, y_rot, distance; Vector3 eye_pos; // Used in freelook mode bool region_select; Point2 region_begin, region_end; @@ -340,8 +340,7 @@ private: void set_message(String p_message, float p_time = 5); - // - void _update_camera(float p_interp_delta); + void _update_camera(real_t p_interp_delta); Transform3D to_camera_transform(const Cursor &p_cursor) const; void _draw(); @@ -450,8 +449,8 @@ public: private: View view; bool mouseover; - float ratio_h; - float ratio_v; + real_t ratio_h; + real_t ratio_v; bool hovering_v; bool hovering_h; @@ -534,9 +533,9 @@ private: Ref<Node3DGizmo> current_hover_gizmo; int current_hover_gizmo_handle; - float snap_translate_value; - float snap_rotate_value; - float snap_scale_value; + real_t snap_translate_value; + real_t snap_rotate_value; + real_t snap_scale_value; Ref<ArrayMesh> selection_box_xray; Ref<ArrayMesh> selection_box; @@ -554,7 +553,7 @@ private: struct Gizmo { bool visible = false; - float scale = 0; + real_t scale = 0; Transform3D transform; } gizmo; @@ -752,9 +751,9 @@ public: ToolMode get_tool_mode() const { return tool_mode; } bool are_local_coords_enabled() const { return tool_option_button[Node3DEditor::TOOL_OPT_LOCAL_COORDS]->is_pressed(); } bool is_snap_enabled() const { return snap_enabled ^ snap_key_enabled; } - float get_translate_snap() const; - float get_rotate_snap() const; - float get_scale_snap() const; + double get_translate_snap() const; + double get_rotate_snap() const; + double get_scale_snap() const; Ref<ArrayMesh> get_move_gizmo(int idx) const { return move_gizmo[idx]; } Ref<ArrayMesh> get_move_plane_gizmo(int idx) const { return move_plane_gizmo[idx]; } diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp index b420372eff..9377395418 100644 --- a/editor/plugins/polygon_2d_editor_plugin.cpp +++ b/editor/plugins/polygon_2d_editor_plugin.cpp @@ -400,25 +400,25 @@ void Polygon2DEditor::_set_show_grid(bool p_show) { uv_edit_draw->update(); } -void Polygon2DEditor::_set_snap_off_x(float p_val) { +void Polygon2DEditor::_set_snap_off_x(real_t p_val) { snap_offset.x = p_val; EditorSettings::get_singleton()->set_project_metadata("polygon_2d_uv_editor", "snap_offset", snap_offset); uv_edit_draw->update(); } -void Polygon2DEditor::_set_snap_off_y(float p_val) { +void Polygon2DEditor::_set_snap_off_y(real_t p_val) { snap_offset.y = p_val; EditorSettings::get_singleton()->set_project_metadata("polygon_2d_uv_editor", "snap_offset", snap_offset); uv_edit_draw->update(); } -void Polygon2DEditor::_set_snap_step_x(float p_val) { +void Polygon2DEditor::_set_snap_step_x(real_t p_val) { snap_step.x = p_val; EditorSettings::get_singleton()->set_project_metadata("polygon_2d_uv_editor", "snap_step", snap_step); uv_edit_draw->update(); } -void Polygon2DEditor::_set_snap_step_y(float p_val) { +void Polygon2DEditor::_set_snap_step_y(real_t p_val) { snap_step.y = p_val; EditorSettings::get_singleton()->set_project_metadata("polygon_2d_uv_editor", "snap_step", snap_step); uv_edit_draw->update(); @@ -569,11 +569,11 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { } int closest = -1; - float closest_dist = 1e20; + real_t closest_dist = 1e20; for (int i = points_prev.size() - internal_vertices; i < points_prev.size(); i++) { Vector2 tuv = mtx.xform(uv_create_poly_prev[i]); - float dist = tuv.distance_to(Vector2(mb->get_position().x, mb->get_position().y)); + real_t dist = tuv.distance_to(Vector2(mb->get_position().x, mb->get_position().y)); if (dist < 8 && dist < closest_dist) { closest = i; closest_dist = dist; @@ -639,11 +639,11 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { if (uv_move_current == UV_MODE_ADD_POLYGON) { int closest = -1; - float closest_dist = 1e20; + real_t closest_dist = 1e20; for (int i = 0; i < points_prev.size(); i++) { Vector2 tuv = mtx.xform(points_prev[i]); - float dist = tuv.distance_to(Vector2(mb->get_position().x, mb->get_position().y)); + real_t dist = tuv.distance_to(Vector2(mb->get_position().x, mb->get_position().y)); if (dist < 8 && dist < closest_dist) { closest = i; closest_dist = dist; @@ -825,7 +825,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { } center /= uv_new.size(); - float angle = (uv_drag_from - mtx.xform(center)).normalized().angle_to((uv_drag_to - mtx.xform(center)).normalized()); + real_t angle = (uv_drag_from - mtx.xform(center)).normalized().angle_to((uv_drag_to - mtx.xform(center)).normalized()); for (int i = 0; i < uv_new.size(); i++) { Vector2 rel = points_prev[i] - center; @@ -848,13 +848,13 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { } center /= uv_new.size(); - float from_dist = uv_drag_from.distance_to(mtx.xform(center)); - float to_dist = uv_drag_to.distance_to(mtx.xform(center)); + real_t from_dist = uv_drag_from.distance_to(mtx.xform(center)); + real_t to_dist = uv_drag_to.distance_to(mtx.xform(center)); if (from_dist < 2) { break; } - float scale = to_dist / from_dist; + real_t scale = to_dist / from_dist; for (int i = 0; i < uv_new.size(); i++) { Vector2 rel = points_prev[i] - center; @@ -881,8 +881,8 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { { int pc = painted_weights.size(); - float amount = bone_paint_strength->get_value(); - float radius = bone_paint_radius->get_value() * EDSCALE; + real_t amount = bone_paint_strength->get_value(); + real_t radius = bone_paint_radius->get_value() * EDSCALE; if (uv_mode == UV_MODE_CLEAR_WEIGHT) { amount = -amount; @@ -925,7 +925,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { } } -void Polygon2DEditor::_uv_scroll_changed(float) { +void Polygon2DEditor::_uv_scroll_changed(real_t) { if (updating_uv_scroll) { return; } diff --git a/editor/plugins/polygon_2d_editor_plugin.h b/editor/plugins/polygon_2d_editor_plugin.h index af3b2f5aef..cbe7ecf360 100644 --- a/editor/plugins/polygon_2d_editor_plugin.h +++ b/editor/plugins/polygon_2d_editor_plugin.h @@ -95,7 +95,7 @@ class Polygon2DEditor : public AbstractPolygon2DEditor { void _update_bone_list(); Vector2 uv_draw_ofs; - float uv_draw_zoom; + real_t uv_draw_zoom; Vector<Vector2> points_prev; Vector<Vector2> uv_create_uv_prev; Vector<Vector2> uv_create_poly_prev; @@ -127,17 +127,17 @@ class Polygon2DEditor : public AbstractPolygon2DEditor { void _cancel_editing(); void _update_polygon_editing_state(); - void _uv_scroll_changed(float); + void _uv_scroll_changed(real_t); void _uv_input(const Ref<InputEvent> &p_input); void _uv_draw(); void _uv_mode(int p_mode); void _set_use_snap(bool p_use); void _set_show_grid(bool p_show); - void _set_snap_off_x(float p_val); - void _set_snap_off_y(float p_val); - void _set_snap_step_x(float p_val); - void _set_snap_step_y(float p_val); + void _set_snap_off_x(real_t p_val); + void _set_snap_off_y(real_t p_val); + void _set_snap_step_x(real_t p_val); + void _set_snap_step_y(real_t p_val); void _uv_edit_mode_select(int p_mode); void _uv_edit_popup_hide(); diff --git a/editor/plugins/tiles/tile_atlas_view.cpp b/editor/plugins/tiles/tile_atlas_view.cpp index 84e40e2ffa..13f04cb804 100644 --- a/editor/plugins/tiles/tile_atlas_view.cpp +++ b/editor/plugins/tiles/tile_atlas_view.cpp @@ -32,6 +32,7 @@ #include "core/input/input.h" #include "core/os/keyboard.h" +#include "scene/2d/tile_map.h" #include "scene/gui/box_container.h" #include "scene/gui/label.h" #include "scene/gui/panel.h" @@ -259,7 +260,7 @@ void TileAtlasView::_draw_base_tiles() { Vector2i offset_pos = (margins + (atlas_coords * texture_region_size) + tile_set_atlas_source->get_tile_texture_region(atlas_coords).size / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, 0)); // Draw the tile. - TileSetPluginAtlasRendering::draw_tile(base_tiles_draw->get_canvas_item(), offset_pos, tile_set, source_id, atlas_coords, 0); + TileMap::draw_tile(base_tiles_draw->get_canvas_item(), offset_pos, tile_set, source_id, atlas_coords, 0); } } } @@ -375,7 +376,7 @@ void TileAtlasView::_draw_alternatives() { } // Draw the tile. - TileSetPluginAtlasRendering::draw_tile(alternatives_draw->get_canvas_item(), offset_pos, tile_set, source_id, atlas_coords, alternative_id); + TileMap::draw_tile(alternatives_draw->get_canvas_item(), offset_pos, tile_set, source_id, atlas_coords, alternative_id); // Increment the x position. current_pos.x += transposed ? texture_region.size.y : texture_region.size.x; diff --git a/editor/plugins/tiles/tile_data_editors.h b/editor/plugins/tiles/tile_data_editors.h index 781f26cc02..99998dc779 100644 --- a/editor/plugins/tiles/tile_data_editors.h +++ b/editor/plugins/tiles/tile_data_editors.h @@ -36,10 +36,10 @@ #include "editor/editor_node.h" #include "editor/editor_properties.h" +#include "scene/2d/tile_map.h" #include "scene/gui/box_container.h" #include "scene/gui/control.h" #include "scene/gui/label.h" -#include "scene/resources/tile_set.h" class TileDataEditor : public VBoxContainer { GDCLASS(TileDataEditor, VBoxContainer); diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp index e70aed8ed6..77084f551a 100644 --- a/editor/plugins/tiles/tile_map_editor.cpp +++ b/editor/plugins/tiles/tile_map_editor.cpp @@ -377,6 +377,11 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p return false; } + if (tile_map_layer < 0) { + return false; + } + ERR_FAIL_INDEX_V(tile_map_layer, tile_map->get_layers_count(), false); + Ref<TileSet> tile_set = tile_map->get_tileset(); if (!tile_set.is_valid()) { return false; @@ -391,7 +396,7 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) { coords_array.push_back(E->get()); } - tile_map_clipboard = tile_map->get_pattern(coords_array); + tile_map_clipboard = tile_map->get_pattern(tile_map_layer, coords_array); } if (ED_IS_SHORTCUT("tiles_editor/cut", p_event)) { @@ -399,8 +404,8 @@ 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(), 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_do_method(tile_map, "set_cell", tile_map_layer, E->get(), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); + undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E->get(), tile_map->get_cell_source_id(tile_map_layer, E->get()), tile_map->get_cell_atlas_coords(tile_map_layer, E->get()), tile_map->get_cell_alternative_tile(tile_map_layer, E->get())); } undo_redo->add_undo_method(this, "_set_tile_map_selection", _get_tile_map_selection()); tile_map_selection.clear(); @@ -430,8 +435,8 @@ 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(), 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_do_method(tile_map, "set_cell", tile_map_layer, E->get(), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); + undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E->get(), tile_map->get_cell_source_id(tile_map_layer, E->get()), tile_map->get_cell_atlas_coords(tile_map_layer, E->get()), tile_map->get_cell_alternative_tile(tile_map_layer, E->get())); } undo_redo->add_undo_method(this, "_set_tile_map_selection", _get_tile_map_selection()); tile_map_selection.clear(); @@ -456,9 +461,9 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p } Vector2i coords = E->key(); if (!drag_modified.has(coords)) { - drag_modified.insert(coords, tile_map->get_cell(coords)); + drag_modified.insert(coords, tile_map->get_cell(tile_map_layer, coords)); } - tile_map->set_cell(coords, E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); + tile_map->set_cell(tile_map_layer, coords, E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); } } break; case DRAG_TYPE_BUCKET: { @@ -472,9 +477,9 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p } Vector2i coords = E->key(); if (!drag_modified.has(coords)) { - drag_modified.insert(coords, tile_map->get_cell(coords)); + drag_modified.insert(coords, tile_map->get_cell(tile_map_layer, coords)); } - tile_map->set_cell(coords, E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); + tile_map->set_cell(tile_map_layer, coords, E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); } } } @@ -497,7 +502,9 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p if (mb->get_button_index() == MOUSE_BUTTON_LEFT) { if (mb->is_pressed()) { // Pressed - if (tool_buttons_group->get_pressed_button() == select_tool_button) { + if (drag_type == DRAG_TYPE_CLIPBOARD_PASTE) { + // Do nothing. + } else if (tool_buttons_group->get_pressed_button() == select_tool_button) { drag_start_mouse_pos = mpos; if (tile_map_selection.has(tile_map->world_to_map(drag_start_mouse_pos)) && !mb->is_shift_pressed()) { // Move the selection @@ -505,8 +512,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, tile_map->get_cell(coords)); - tile_map->set_cell(coords, TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); + drag_modified.insert(coords, tile_map->get_cell(tile_map_layer, coords)); + tile_map->set_cell(tile_map_layer, coords, TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); } } else { // Select tiles @@ -530,9 +537,9 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p } Vector2i coords = E->key(); if (!drag_modified.has(coords)) { - drag_modified.insert(coords, tile_map->get_cell(coords)); + drag_modified.insert(coords, tile_map->get_cell(tile_map_layer, coords)); } - tile_map->set_cell(coords, E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); + tile_map->set_cell(tile_map_layer, coords, E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); } } else if (tool_buttons_group->get_pressed_button() == line_tool_button) { drag_type = DRAG_TYPE_LINE; @@ -556,9 +563,9 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p } Vector2i coords = E->key(); if (!drag_modified.has(coords)) { - drag_modified.insert(coords, tile_map->get_cell(coords)); + drag_modified.insert(coords, tile_map->get_cell(tile_map_layer, coords)); } - tile_map->set_cell(coords, E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); + tile_map->set_cell(tile_map_layer, coords, E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); } } } @@ -587,6 +594,11 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over return; } + if (tile_map_layer < 0) { + return; + } + ERR_FAIL_INDEX(tile_map_layer, tile_map->get_layers_count()); + Ref<TileSet> tile_set = tile_map->get_tileset(); if (!tile_set.is_valid()) { return; @@ -623,7 +635,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) != TileSet::INVALID_SOURCE) { + if (tile_map->get_cell_source_id(tile_map_layer, 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); } @@ -637,7 +649,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) != TileSet::INVALID_SOURCE) { + if (tile_map->get_cell_source_id(tile_map_layer, coords) != TileSet::INVALID_SOURCE) { to_draw.insert(coords); } } @@ -914,6 +926,8 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_rect(Vector2i p_start TileMapPattern erase_pattern; 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> err_output; + ERR_FAIL_COND_V(pattern->is_empty(), err_output); // Compute the offset to align things to the bottom or right. bool aligned_right = p_end_cell.x < p_start_cell.x; @@ -956,6 +970,12 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i return Map<Vector2i, TileMapCell>(); } + if (tile_map_layer < 0) { + return Map<Vector2i, TileMapCell>(); + } + Map<Vector2i, TileMapCell> output; + ERR_FAIL_INDEX_V(tile_map_layer, tile_map->get_layers_count(), output); + Ref<TileSet> tile_set = tile_map->get_tileset(); if (!tile_set.is_valid()) { return Map<Vector2i, TileMapCell>(); @@ -966,9 +986,8 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i 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; if (!pattern->is_empty()) { - TileMapCell source = tile_map->get_cell(p_coords); + TileMapCell source = tile_map->get_cell(tile_map_layer, p_coords); // If we are filling empty tiles, compute the tilemap boundaries. Rect2i boundaries; @@ -985,9 +1004,9 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i Vector2i coords = to_check.back()->get(); to_check.pop_back(); if (!already_checked.has(coords)) { - 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) && + if (source.source_id == tile_map->get_cell_source_id(tile_map_layer, coords) && + source.get_atlas_coords() == tile_map->get_cell_atlas_coords(tile_map_layer, coords) && + source.alternative_tile == tile_map->get_cell_alternative_tile(tile_map_layer, 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. @@ -1027,13 +1046,13 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i } } } else { - to_check = tile_map->get_used_cells(); + to_check = tile_map->get_used_cells(tile_map_layer); } for (int i = 0; i < to_check.size(); i++) { Vector2i coords = to_check[i]; - 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) && + if (source.source_id == tile_map->get_cell_source_id(tile_map_layer, coords) && + source.get_atlas_coords() == tile_map->get_cell_atlas_coords(tile_map_layer, coords) && + source.alternative_tile == tile_map->get_cell_alternative_tile(tile_map_layer, 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. @@ -1066,6 +1085,11 @@ void TileMapEditorTilesPlugin::_stop_dragging() { return; } + if (tile_map_layer < 0) { + return; + } + ERR_FAIL_INDEX(tile_map_layer, tile_map->get_layers_count()); + Ref<TileSet> tile_set = tile_map->get_tileset(); if (!tile_set.is_valid()) { return; @@ -1091,7 +1115,7 @@ void TileMapEditorTilesPlugin::_stop_dragging() { tile_map_selection.erase(coords); } } else { - if (tile_map->get_cell_source_id(coords) != TileSet::INVALID_SOURCE) { + if (tile_map->get_cell_source_id(tile_map_layer, coords) != TileSet::INVALID_SOURCE) { tile_map_selection.insert(coords); } } @@ -1123,7 +1147,7 @@ void TileMapEditorTilesPlugin::_stop_dragging() { coords = tile_map->map_pattern(top_left, selection_used_cells[i], selection_pattern); cells_undo[coords] = TileMapCell(drag_modified[coords].source_id, drag_modified[coords].get_atlas_coords(), drag_modified[coords].alternative_tile); coords = tile_map->map_pattern(top_left + offset, selection_used_cells[i], selection_pattern); - cells_undo[coords] = TileMapCell(tile_map->get_cell_source_id(coords), tile_map->get_cell_atlas_coords(coords), tile_map->get_cell_alternative_tile(coords)); + cells_undo[coords] = TileMapCell(tile_map->get_cell_source_id(tile_map_layer, coords), tile_map->get_cell_atlas_coords(tile_map_layer, coords), tile_map->get_cell_alternative_tile(tile_map_layer, coords)); } Map<Vector2i, TileMapCell> cells_do; @@ -1138,10 +1162,10 @@ void TileMapEditorTilesPlugin::_stop_dragging() { undo_redo->create_action(TTR("Move tiles")); // Move the tiles. for (Map<Vector2i, TileMapCell>::Element *E = cells_do.front(); E; E = E->next()) { - undo_redo->add_do_method(tile_map, "set_cell", E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); + undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); } for (Map<Vector2i, TileMapCell>::Element *E = cells_undo.front(); E; E = E->next()) { - undo_redo->add_undo_method(tile_map, "set_cell", E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); + undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); } // Update the selection. @@ -1162,12 +1186,12 @@ 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) != TileSet::INVALID_SOURCE) { + if (tile_map->get_cell_source_id(tile_map_layer, coords) != TileSet::INVALID_SOURCE) { coords_array.push_back(coords); } } } - selection_pattern = tile_map->get_pattern(coords_array); + selection_pattern = tile_map->get_pattern(tile_map_layer, coords_array); if (!selection_pattern->is_empty()) { _update_tileset_selection_from_selection_pattern(); } else { @@ -1178,8 +1202,8 @@ void TileMapEditorTilesPlugin::_stop_dragging() { case DRAG_TYPE_PAINT: { undo_redo->create_action(TTR("Paint tiles")); for (Map<Vector2i, TileMapCell>::Element *E = drag_modified.front(); E; E = E->next()) { - 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())); - undo_redo->add_undo_method(tile_map, "set_cell", E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); + undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E->key(), tile_map->get_cell_source_id(tile_map_layer, E->key()), tile_map->get_cell_atlas_coords(tile_map_layer, E->key()), tile_map->get_cell_alternative_tile(tile_map_layer, E->key())); + undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); } undo_redo->commit_action(false); } break; @@ -1190,8 +1214,8 @@ void TileMapEditorTilesPlugin::_stop_dragging() { 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); - undo_redo->add_undo_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())); + undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); + undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E->key(), tile_map->get_cell_source_id(tile_map_layer, E->key()), tile_map->get_cell_atlas_coords(tile_map_layer, E->key()), tile_map->get_cell_alternative_tile(tile_map_layer, E->key())); } undo_redo->commit_action(); } break; @@ -1202,16 +1226,16 @@ void TileMapEditorTilesPlugin::_stop_dragging() { 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); - undo_redo->add_undo_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())); + undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); + undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E->key(), tile_map->get_cell_source_id(tile_map_layer, E->key()), tile_map->get_cell_atlas_coords(tile_map_layer, E->key()), tile_map->get_cell_alternative_tile(tile_map_layer, E->key())); } undo_redo->commit_action(); } break; case DRAG_TYPE_BUCKET: { undo_redo->create_action(TTR("Paint tiles")); for (Map<Vector2i, TileMapCell>::Element *E = drag_modified.front(); E; E = E->next()) { - 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())); - undo_redo->add_undo_method(tile_map, "set_cell", E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); + undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E->key(), tile_map->get_cell_source_id(tile_map_layer, E->key()), tile_map->get_cell_atlas_coords(tile_map_layer, E->key()), tile_map->get_cell_alternative_tile(tile_map_layer, E->key())); + undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); } undo_redo->commit_action(false); } break; @@ -1221,8 +1245,8 @@ void TileMapEditorTilesPlugin::_stop_dragging() { TypedArray<Vector2i> used_cells = tile_map_clipboard->get_used_cells(); for (int i = 0; i < used_cells.size(); i++) { Vector2i coords = tile_map->map_pattern(tile_map->world_to_map(mpos - mouse_offset), used_cells[i], tile_map_clipboard); - undo_redo->add_do_method(tile_map, "set_cell", coords, tile_map_clipboard->get_cell_source_id(used_cells[i]), tile_map_clipboard->get_cell_atlas_coords(used_cells[i]), tile_map_clipboard->get_cell_alternative_tile(used_cells[i])); - undo_redo->add_undo_method(tile_map, "set_cell", coords, tile_map->get_cell_source_id(coords), tile_map->get_cell_atlas_coords(coords), tile_map->get_cell_alternative_tile(coords)); + undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, coords, tile_map_clipboard->get_cell_source_id(used_cells[i]), tile_map_clipboard->get_cell_atlas_coords(used_cells[i]), tile_map_clipboard->get_cell_alternative_tile(used_cells[i])); + undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, coords, tile_map->get_cell_source_id(tile_map_layer, coords), tile_map->get_cell_atlas_coords(tile_map_layer, coords), tile_map->get_cell_alternative_tile(tile_map_layer, coords)); } undo_redo->commit_action(); } break; @@ -1301,13 +1325,15 @@ void TileMapEditorTilesPlugin::_update_selection_pattern_from_tilemap_selection( return; } + ERR_FAIL_INDEX(tile_map_layer, tile_map->get_layers_count()); + memdelete(selection_pattern); TypedArray<Vector2i> coords_array; for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) { coords_array.push_back(E->get()); } - selection_pattern = tile_map->get_pattern(coords_array); + selection_pattern = tile_map->get_pattern(tile_map_layer, coords_array); } void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_selection() { @@ -1344,8 +1370,7 @@ void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_selection( TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); if (atlas_source) { // Organize using coordinates. - for (List<const TileMapCell *>::Element *E_cell = E_source->get().front(); E_cell; E_cell = E_cell->next()) { - const TileMapCell *current = E_cell->get(); + for (const TileMapCell *current : E_source->get()) { if (current->alternative_tile == 0) { organized_pattern[current->get_atlas_coords()] = current; } else { @@ -1364,8 +1389,8 @@ void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_selection( } } else { // Add everything unorganized. - for (List<const TileMapCell *>::Element *E_cell = E_source->get().front(); E_cell; E_cell = E_cell->next()) { - unorganized.push_back(E_cell->get()); + for (const TileMapCell *cell : E_source->get()) { + unorganized.push_back(cell); } } @@ -1375,8 +1400,8 @@ void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_selection( } Vector2i organized_size = selection_pattern->get_size(); int unorganized_index = 0; - for (List<const TileMapCell *>::Element *E_cell = unorganized.front(); E_cell; E_cell = E_cell->next()) { - selection_pattern->set_cell(Vector2(organized_size.x + unorganized_index, vertical_offset), E_cell->get()->source_id, E_cell->get()->get_atlas_coords(), E_cell->get()->alternative_tile); + for (const TileMapCell *cell : unorganized) { + selection_pattern->set_cell(Vector2(organized_size.x + unorganized_index, vertical_offset), cell->source_id, cell->get_atlas_coords(), cell->alternative_tile); unorganized_index++; } vertical_offset += MAX(organized_size.y, 1); @@ -1711,13 +1736,19 @@ TypedArray<Vector2i> TileMapEditorTilesPlugin::_get_tile_map_selection() const { return output; } -void TileMapEditorTilesPlugin::edit(ObjectID p_tile_map_id) { - tile_map_id = p_tile_map_id; +void TileMapEditorTilesPlugin::edit(ObjectID p_tile_map_id, int p_tile_map_layer) { + _stop_dragging(); // Avoids staying in a wrong drag state. - // Clear the selection. - tile_set_selection.clear(); - tile_map_selection.clear(); - selection_pattern->clear(); + if (tile_map_id != p_tile_map_id) { + tile_map_id = p_tile_map_id; + + // Clear the selection. + tile_set_selection.clear(); + tile_map_selection.clear(); + selection_pattern->clear(); + } + + tile_map_layer = p_tile_map_layer; } void TileMapEditorTilesPlugin::_bind_methods() { @@ -2299,6 +2330,7 @@ Set<TileMapEditorTerrainsPlugin::Constraint> TileMapEditorTerrainsPlugin::_get_c } ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), Set<Constraint>()); + ERR_FAIL_INDEX_V(tile_map_layer, tile_map->get_layers_count(), Set<Constraint>()); // Build a set of dummy constraints get the constrained points. Set<Constraint> dummy_constraints; @@ -2322,7 +2354,7 @@ Set<TileMapEditorTerrainsPlugin::Constraint> TileMapEditorTerrainsPlugin::_get_c Map<Vector2i, TileSet::CellNeighbor> overlapping_terrain_bits = c.get_overlapping_coords_and_peering_bits(); for (Map<Vector2i, TileSet::CellNeighbor>::Element *E_overlapping = overlapping_terrain_bits.front(); E_overlapping; E_overlapping = E_overlapping->next()) { if (!p_to_replace.has(E_overlapping->key())) { - TileMapCell neighbor_cell = tile_map->get_cell(E_overlapping->key()); + TileMapCell neighbor_cell = tile_map->get_cell(tile_map_layer, E_overlapping->key()); TileData *neighbor_tile_data = nullptr; if (terrain_tiles.has(neighbor_cell) && terrain_tiles[neighbor_cell]->get_terrain_set() == p_terrain_set) { neighbor_tile_data = terrain_tiles[neighbor_cell]; @@ -2629,6 +2661,90 @@ Map<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_terrains(const Map return output; } +void TileMapEditorTerrainsPlugin::_stop_dragging() { + TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id)); + if (!tile_map) { + return; + } + + Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform(); + Vector2 mpos = xform.affine_inverse().xform(CanvasItemEditor::get_singleton()->get_viewport_control()->get_local_mouse_position()); + + switch (drag_type) { + case DRAG_TYPE_PICK: { + Vector2i coords = tile_map->world_to_map(mpos); + TileMapCell tile = tile_map->get_cell(tile_map_layer, coords); + + if (terrain_tiles.has(tile)) { + Array terrains_tile_pattern = _build_terrains_tile_pattern(terrain_tiles[tile]); + + // Find the tree item for the right terrain set. + bool need_tree_item_switch = true; + TreeItem *tree_item = terrains_tree->get_selected(); + if (tree_item) { + Dictionary metadata_dict = tree_item->get_metadata(0); + if (metadata_dict.has("terrain_set") && metadata_dict.has("terrain_id")) { + int terrain_set = metadata_dict["terrain_set"]; + int terrain_id = metadata_dict["terrain_id"]; + if (per_terrain_terrains_tile_patterns[terrain_set][terrain_id].has(terrains_tile_pattern)) { + need_tree_item_switch = false; + } + } + } + + if (need_tree_item_switch) { + for (tree_item = terrains_tree->get_root()->get_first_child(); tree_item; tree_item = tree_item->get_next_visible()) { + Dictionary metadata_dict = tree_item->get_metadata(0); + if (metadata_dict.has("terrain_set") && metadata_dict.has("terrain_id")) { + int terrain_set = metadata_dict["terrain_set"]; + int terrain_id = metadata_dict["terrain_id"]; + if (per_terrain_terrains_tile_patterns[terrain_set][terrain_id].has(terrains_tile_pattern)) { + // Found + tree_item->select(0); + _update_tiles_list(); + break; + } + } + } + } + + // Find the list item for the given tile. + if (tree_item) { + for (int i = 0; i < terrains_tile_list->get_item_count(); i++) { + Dictionary metadata_dict = terrains_tile_list->get_item_metadata(i); + TerrainsTilePattern in_meta_terrains_tile_pattern = metadata_dict["terrains_tile_pattern"]; + bool equals = true; + for (int j = 0; j < terrains_tile_pattern.size(); j++) { + if (terrains_tile_pattern[j] != in_meta_terrains_tile_pattern[j]) { + equals = false; + break; + } + } + if (equals) { + terrains_tile_list->select(i); + break; + } + } + } else { + ERR_PRINT("Terrain tile not found."); + } + } + picker_button->set_pressed(false); + } break; + case DRAG_TYPE_PAINT: { + undo_redo->create_action(TTR("Paint terrain")); + for (Map<Vector2i, TileMapCell>::Element *E = drag_modified.front(); E; E = E->next()) { + undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E->key(), tile_map->get_cell_source_id(tile_map_layer, E->key()), tile_map->get_cell_atlas_coords(tile_map_layer, E->key()), tile_map->get_cell_alternative_tile(tile_map_layer, E->key())); + undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); + } + undo_redo->commit_action(false); + } break; + default: + break; + } + drag_type = DRAG_TYPE_NONE; +} + bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p_event) { if (!is_visible_in_tree()) { // If the bottom editor is not visible, we ignore inputs. @@ -2649,6 +2765,11 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent> return false; } + if (tile_map_layer < 0) { + return false; + } + ERR_FAIL_COND_V(tile_map_layer >= tile_map->get_layers_count(), false); + // Get the selected terrain. TerrainsTilePattern selected_terrains_tile_pattern; int selected_terrain_set = -1; @@ -2690,9 +2811,9 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent> Map<Vector2i, TileMapCell> modified = _draw_terrains(to_draw, selected_terrain_set); for (Map<Vector2i, TileMapCell>::Element *E = modified.front(); E; E = E->next()) { if (!drag_modified.has(E->key())) { - drag_modified[E->key()] = tile_map->get_cell(E->key()); + drag_modified[E->key()] = tile_map->get_cell(tile_map_layer, E->key()); } - tile_map->set_cell(E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); + tile_map->set_cell(tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); } } } break; @@ -2728,86 +2849,14 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent> Map<Vector2i, TileMapCell> to_draw = _draw_terrains(terrains_to_draw, selected_terrain_set); for (Map<Vector2i, TileMapCell>::Element *E = to_draw.front(); E; E = E->next()) { - drag_modified[E->key()] = tile_map->get_cell(E->key()); - tile_map->set_cell(E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); + drag_modified[E->key()] = tile_map->get_cell(tile_map_layer, E->key()); + tile_map->set_cell(tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); } } } } else { // Released - switch (drag_type) { - case DRAG_TYPE_PICK: { - Vector2i coords = tile_map->world_to_map(mpos); - TileMapCell tile = tile_map->get_cell(coords); - - if (terrain_tiles.has(tile)) { - Array terrains_tile_pattern = _build_terrains_tile_pattern(terrain_tiles[tile]); - - // Find the tree item for the right terrain set. - bool need_tree_item_switch = true; - TreeItem *tree_item = terrains_tree->get_selected(); - if (tree_item) { - Dictionary metadata_dict = tree_item->get_metadata(0); - if (metadata_dict.has("terrain_set") && metadata_dict.has("terrain_id")) { - int terrain_set = metadata_dict["terrain_set"]; - int terrain_id = metadata_dict["terrain_id"]; - if (per_terrain_terrains_tile_patterns[terrain_set][terrain_id].has(terrains_tile_pattern)) { - need_tree_item_switch = false; - } - } - } - - if (need_tree_item_switch) { - for (tree_item = terrains_tree->get_root()->get_first_child(); tree_item; tree_item = tree_item->get_next_visible()) { - Dictionary metadata_dict = tree_item->get_metadata(0); - if (metadata_dict.has("terrain_set") && metadata_dict.has("terrain_id")) { - int terrain_set = metadata_dict["terrain_set"]; - int terrain_id = metadata_dict["terrain_id"]; - if (per_terrain_terrains_tile_patterns[terrain_set][terrain_id].has(terrains_tile_pattern)) { - // Found - tree_item->select(0); - _update_tiles_list(); - break; - } - } - } - } - - // Find the list item for the given tile. - if (tree_item) { - for (int i = 0; i < terrains_tile_list->get_item_count(); i++) { - Dictionary metadata_dict = terrains_tile_list->get_item_metadata(i); - TerrainsTilePattern in_meta_terrains_tile_pattern = metadata_dict["terrains_tile_pattern"]; - bool equals = true; - for (int j = 0; j < terrains_tile_pattern.size(); j++) { - if (terrains_tile_pattern[j] != in_meta_terrains_tile_pattern[j]) { - equals = false; - break; - } - } - if (equals) { - terrains_tile_list->select(i); - break; - } - } - } else { - ERR_PRINT("Terrain tile not found."); - } - } - picker_button->set_pressed(false); - } break; - case DRAG_TYPE_PAINT: { - undo_redo->create_action(TTR("Paint terrain")); - for (Map<Vector2i, TileMapCell>::Element *E = drag_modified.front(); E; E = E->next()) { - 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())); - undo_redo->add_undo_method(tile_map, "set_cell", E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); - } - undo_redo->commit_action(false); - } break; - default: - break; - } - drag_type = DRAG_TYPE_NONE; + _stop_dragging(); } CanvasItemEditor::get_singleton()->update_viewport(); @@ -3080,8 +3129,12 @@ void TileMapEditorTerrainsPlugin::_update_tiles_list() { } } -void TileMapEditorTerrainsPlugin::edit(ObjectID p_tile_map_id) { +void TileMapEditorTerrainsPlugin::edit(ObjectID p_tile_map_id, int p_tile_map_layer) { + _stop_dragging(); // Avoids staying in a wrong drag state. + tile_map_id = p_tile_map_id; + tile_map_layer = p_tile_map_layer; + _update_terrains_cache(); _update_terrains_tree(); _update_tiles_list(); @@ -3166,10 +3219,12 @@ void TileMapEditor::_notification(int p_what) { 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")); + toogle_highlight_selected_layer_button->set_icon(get_theme_icon(SNAME("TileMapHighlightSelected"), SNAME("EditorIcons"))); break; case NOTIFICATION_INTERNAL_PROCESS: if (is_visible_in_tree() && tileset_changed_needs_update) { _update_bottom_panel(); + _update_layers_selection(); tile_map_editor_plugins[tabs->get_current_tab()]->tile_set_changed(); CanvasItemEditor::get_singleton()->update_viewport(); tileset_changed_needs_update = false; @@ -3178,6 +3233,16 @@ void TileMapEditor::_notification(int p_what) { case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: toggle_grid_button->set_pressed(EditorSettings::get_singleton()->get("editors/tiles_editor/display_grid")); break; + case NOTIFICATION_VISIBILITY_CHANGED: + TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id)); + if (tile_map) { + if (is_visible_in_tree()) { + tile_map->set_selected_layer(tile_map_layer); + } else { + tile_map->set_selected_layer(-1); + } + } + break; } } @@ -3185,6 +3250,63 @@ void TileMapEditor::_on_grid_toggled(bool p_pressed) { EditorSettings::get_singleton()->set("editors/tiles_editor/display_grid", p_pressed); } +void TileMapEditor::_layers_selection_button_draw() { + if (!has_theme_icon(SNAME("arrow"), SNAME("OptionButton"))) { + return; + } + + RID ci = layers_selection_button->get_canvas_item(); + Ref<Texture2D> arrow = Control::get_theme_icon(SNAME("arrow"), SNAME("OptionButton")); + + Color clr = Color(1, 1, 1); + if (get_theme_constant(SNAME("modulate_arrow"))) { + switch (layers_selection_button->get_draw_mode()) { + case BaseButton::DRAW_PRESSED: + clr = get_theme_color(SNAME("font_pressed_color")); + break; + case BaseButton::DRAW_HOVER: + clr = get_theme_color(SNAME("font_hover_color")); + break; + case BaseButton::DRAW_DISABLED: + clr = get_theme_color(SNAME("font_disabled_color")); + break; + default: + clr = get_theme_color(SNAME("font_color")); + } + } + + Size2 size = layers_selection_button->get_size(); + + Point2 ofs; + if (is_layout_rtl()) { + ofs = Point2(get_theme_constant(SNAME("arrow_margin"), SNAME("OptionButton")), int(Math::abs((size.height - arrow->get_height()) / 2))); + } else { + ofs = Point2(size.width - arrow->get_width() - get_theme_constant(SNAME("arrow_margin"), SNAME("OptionButton")), int(Math::abs((size.height - arrow->get_height()) / 2))); + } + Rect2 dst_rect = Rect2(ofs, arrow->get_size()); + if (!layers_selection_button->is_pressed()) { + dst_rect.size = -dst_rect.size; + } + arrow->draw_rect(ci, dst_rect, false, clr); +} + +void TileMapEditor::_layers_selection_button_pressed() { + if (!layers_selection_popup->is_visible()) { + Size2 size = layers_selection_popup->get_contents_minimum_size(); + size.x = MAX(size.x, layers_selection_button->get_size().x); + layers_selection_popup->set_position(layers_selection_button->get_screen_position() - Size2(0, size.y * get_global_transform().get_scale().y)); + layers_selection_popup->set_size(size); + layers_selection_popup->popup(); + } else { + layers_selection_popup->hide(); + } +} + +void TileMapEditor::_layers_selection_id_pressed(int p_id) { + tile_map_layer = p_id; + _update_layers_selection(); +} + 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) { @@ -3198,18 +3320,20 @@ void TileMapEditor::_advanced_menu_button_id_pressed(int p_id) { 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); + for (int layer_index = 0; layer_index < tile_map->get_layers_count(); layer_index++) { + TypedArray<Vector2i> used_cells = tile_map->get_used_cells(layer_index); + for (int i = 0; i < used_cells.size(); i++) { + Vector2i cell_coords = used_cells[i]; + TileMapCell from = tile_map->get_cell(layer_index, 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", tile_map_layer, cell_coords, to.source_id, to.get_atlas_coords(), to.alternative_tile); + undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, cell_coords, from.source_id, from.get_atlas_coords(), from.alternative_tile); + } } } undo_redo->commit_action(); @@ -3315,7 +3439,7 @@ void TileMapEditor::_tile_map_changed() { void TileMapEditor::_tab_changed(int p_tab_id) { // Make the plugin edit the correct tilemap. - tile_map_editor_plugins[tabs->get_current_tab()]->edit(tile_map_id); + tile_map_editor_plugins[tabs->get_current_tab()]->edit(tile_map_id, tile_map_layer); // Update toolbar. for (int i = 0; i < tile_map_editor_plugins.size(); i++) { @@ -3339,7 +3463,129 @@ void TileMapEditor::_tab_changed(int p_tab_id) { CanvasItemEditor::get_singleton()->update_viewport(); } +void TileMapEditor::_layers_select_next_or_previous(bool p_next) { + TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id)); + if (!tile_map) { + return; + } + + if (tile_map->get_layers_count() < 1) { + return; + } + + if (tile_map_layer < 0) { + tile_map_layer = 0; + } + + int inc = p_next ? 1 : -1; + int origin_layer = tile_map_layer; + tile_map_layer = Math::posmod((tile_map_layer + inc), tile_map->get_layers_count()); + while (tile_map_layer != origin_layer) { + if (tile_map->is_layer_enabled(tile_map_layer)) { + break; + } + tile_map_layer = Math::posmod((tile_map_layer + inc), tile_map->get_layers_count()); + } + + _update_layers_selection(); +} + +void TileMapEditor::_update_layers_selection() { + layers_selection_popup->clear(); + + TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id)); + if (!tile_map) { + return; + } + + // Update the selected layer. + if (is_visible_in_tree() && tile_map->get_layers_count() >= 1) { + tile_map_layer = CLAMP(tile_map_layer, 0, tile_map->get_layers_count() - 1); + + // Search for an enabled layer if the current one is not. + int origin_layer = tile_map_layer; + while (tile_map_layer >= 0 && !tile_map->is_layer_enabled(tile_map_layer)) { + tile_map_layer--; + } + if (tile_map_layer < 0) { + tile_map_layer = origin_layer; + while (tile_map_layer < tile_map->get_layers_count() && !tile_map->is_layer_enabled(tile_map_layer)) { + tile_map_layer++; + } + } + if (tile_map_layer >= tile_map->get_layers_count()) { + tile_map_layer = -1; + } + } else { + tile_map_layer = -1; + } + tile_map->set_selected_layer(toogle_highlight_selected_layer_button->is_pressed() ? tile_map_layer : -1); + + // Build the list of layers. + for (int i = 0; i < tile_map->get_layers_count(); i++) { + String name = tile_map->get_layer_name(i); + layers_selection_popup->add_item(name.is_empty() ? vformat(TTR("Layer #%d"), i) : name, i); + layers_selection_popup->set_item_as_radio_checkable(i, true); + layers_selection_popup->set_item_disabled(i, !tile_map->is_layer_enabled(i)); + layers_selection_popup->set_item_checked(i, i == tile_map_layer); + } + + // Update the button label. + if (tile_map_layer >= 0) { + layers_selection_button->set_text(layers_selection_popup->get_item_text(tile_map_layer)); + } else { + layers_selection_button->set_text(TTR("Select a layer")); + } + + // Set button minimum width. + Size2 min_button_size = Size2(layers_selection_popup->get_contents_minimum_size().x, 0); + if (has_theme_icon(SNAME("arrow"), SNAME("OptionButton"))) { + Ref<Texture2D> arrow = Control::get_theme_icon(SNAME("arrow"), SNAME("OptionButton")); + min_button_size.x += arrow->get_size().x; + } + layers_selection_button->set_custom_minimum_size(min_button_size); + layers_selection_button->update(); + + tile_map_editor_plugins[tabs->get_current_tab()]->edit(tile_map_id, tile_map_layer); +} + +void TileMapEditor::_undo_redo_inspector_callback(Object *p_undo_redo, Object *p_edited, String p_property, Variant p_new_value) { + UndoRedo *undo_redo = Object::cast_to<UndoRedo>(p_undo_redo); + ERR_FAIL_COND(!undo_redo); + + TileMap *tile_map = Object::cast_to<TileMap>(p_edited); + if (tile_map) { + if (p_property == "layers_count") { + int new_layers_count = (int)p_new_value; + if (new_layers_count < tile_map->get_layers_count()) { + List<PropertyInfo> property_list; + tile_map->get_property_list(&property_list); + + for (PropertyInfo property_info : property_list) { + Vector<String> components = String(property_info.name).split("/", true, 2); + if (components.size() == 2 && components[0].begins_with("layer_") && components[0].trim_prefix("layer_").is_valid_int()) { + int index = components[0].trim_prefix("layer_").to_int(); + if (index >= new_layers_count) { + undo_redo->add_undo_property(tile_map, property_info.name, tile_map->get(property_info.name)); + } + } + } + } + } + } +} + bool TileMapEditor::forward_canvas_gui_input(const Ref<InputEvent> &p_event) { + if (ED_IS_SHORTCUT("tiles_editor/select_next_layer", p_event) && p_event->is_pressed()) { + _layers_select_next_or_previous(true); + return true; + } + + if (ED_IS_SHORTCUT("tiles_editor/select_previous_layer", p_event) && p_event->is_pressed()) { + _layers_select_next_or_previous(false); + return true; + } + return tile_map_editor_plugins[tabs->get_current_tab()]->forward_canvas_gui_input(p_event); } @@ -3363,49 +3609,52 @@ 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. - TypedArray<Vector2i> used_cells = tile_map->get_used_cells(); - for (int i = 0; i < used_cells.size(); i++) { - Vector2i coords = used_cells[i]; - int tile_source_id = tile_map->get_cell_source_id(coords); - if (tile_source_id >= 0) { - Vector2i tile_atlas_coords = tile_map->get_cell_atlas_coords(coords); - int tile_alternative_tile = tile_map->get_cell_alternative_tile(coords); - - TileSetSource *source = nullptr; - if (tile_set->has_source(tile_source_id)) { - source = *tile_set->get_source(tile_source_id); - } - - 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 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); + if (tile_map_layer >= 0) { + ERR_FAIL_COND(tile_map_layer >= tile_map->get_layers_count()); + TypedArray<Vector2i> used_cells = tile_map->get_used_cells(tile_map_layer); + for (int i = 0; i < used_cells.size(); i++) { + Vector2i coords = used_cells[i]; + int tile_source_id = tile_map->get_cell_source_id(tile_map_layer, coords); + if (tile_source_id >= 0) { + Vector2i tile_atlas_coords = tile_map->get_cell_atlas_coords(tile_map_layer, coords); + int tile_alternative_tile = tile_map->get_cell_alternative_tile(tile_map_layer, coords); + + TileSetSource *source = nullptr; + if (tile_set->has_source(tile_source_id)) { + source = *tile_set->get_source(tile_source_id); } - // Draw the warning icon. - 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); + 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 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. + 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); + } } } } @@ -3476,14 +3725,19 @@ void TileMapEditor::edit(TileMap *p_tile_map) { return; } - // Disconnect to changes. TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id)); if (tile_map) { + // Unselect layer if we are changing tile_map. + if (tile_map != p_tile_map) { + tile_map->set_selected_layer(-1); + } + + // Disconnect to changes. tile_map->disconnect("changed", callable_mp(this, &TileMapEditor::_tile_map_changed)); } - // Change the edited object. if (p_tile_map) { + // Change the edited object. tile_map_id = p_tile_map->get_instance_id(); tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id)); // Connect to changes. @@ -3494,8 +3748,10 @@ void TileMapEditor::edit(TileMap *p_tile_map) { tile_map_id = ObjectID(); } + _update_layers_selection(); + // Call the plugins. - tile_map_editor_plugins[tabs->get_current_tab()]->edit(tile_map_id); + tile_map_editor_plugins[tabs->get_current_tab()]->edit(tile_map_id, tile_map_layer); _tile_map_changed(); } @@ -3503,6 +3759,10 @@ void TileMapEditor::edit(TileMap *p_tile_map) { TileMapEditor::TileMapEditor() { set_process_internal(true); + // Shortcuts. + ED_SHORTCUT("tiles_editor/select_next_layer", TTR("Select Next Tile Map Layer"), KEY_PAGEUP); + ED_SHORTCUT("tiles_editor/select_previous_layer", TTR("Select Previous Tile Map Layer"), KEY_PAGEDOWN); + // TileMap editor plugins tile_map_editor_plugins.push_back(memnew(TileMapEditorTilesPlugin)); tile_map_editor_plugins.push_back(memnew(TileMapEditorTerrainsPlugin)); @@ -3516,18 +3776,46 @@ TileMapEditor::TileMapEditor() { tabs->connect("tab_changed", callable_mp(this, &TileMapEditor::_tab_changed)); // --- TileMap toolbar --- - tilemap_toolbar = memnew(HBoxContainer); - tilemap_toolbar->set_h_size_flags(SIZE_EXPAND_FILL); - tilemap_toolbar->add_child(tabs); + tile_map_toolbar = memnew(HBoxContainer); + tile_map_toolbar->set_h_size_flags(SIZE_EXPAND_FILL); + + // Tabs. + tile_map_toolbar->add_child(tabs); + + // Tabs toolbars. for (int i = 0; i < tile_map_editor_plugins.size(); i++) { tile_map_editor_plugins[i]->get_toolbar()->hide(); - tilemap_toolbar->add_child(tile_map_editor_plugins[i]->get_toolbar()); + tile_map_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); + tile_map_toolbar->add_child(h_empty_space); + + // Layer selector. + layers_selection_popup = memnew(PopupMenu); + layers_selection_popup->connect("id_pressed", callable_mp(this, &TileMapEditor::_layers_selection_id_pressed)); + layers_selection_popup->set_close_on_parent_focus(false); + + layers_selection_button = memnew(Button); + layers_selection_button->set_toggle_mode(true); + layers_selection_button->connect("draw", callable_mp(this, &TileMapEditor::_layers_selection_button_draw)); + layers_selection_button->connect("pressed", callable_mp(this, &TileMapEditor::_layers_selection_button_pressed)); + layers_selection_button->connect("hidden", callable_mp((Window *)layers_selection_popup, &Popup::hide)); + layers_selection_button->set_tooltip(TTR("Tile Map Layer")); + layers_selection_button->add_child(layers_selection_popup); + tile_map_toolbar->add_child(layers_selection_button); + + toogle_highlight_selected_layer_button = memnew(Button); + toogle_highlight_selected_layer_button->set_flat(true); + toogle_highlight_selected_layer_button->set_toggle_mode(true); + toogle_highlight_selected_layer_button->set_pressed(true); + toogle_highlight_selected_layer_button->connect("pressed", callable_mp(this, &TileMapEditor::_update_layers_selection)); + toogle_highlight_selected_layer_button->set_tooltip(TTR("Highlight Selected TileMap Layer")); + tile_map_toolbar->add_child(toogle_highlight_selected_layer_button); + + tile_map_toolbar->add_child(memnew(VSeparator)); // Grid toggle. toggle_grid_button = memnew(Button); @@ -3535,14 +3823,14 @@ TileMapEditor::TileMapEditor() { 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); + tile_map_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); + tile_map_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.")); @@ -3561,6 +3849,9 @@ TileMapEditor::TileMapEditor() { } _tab_changed(0); + + // Registers UndoRedo inspector callback. + EditorNode::get_singleton()->get_editor_data().add_undo_redo_inspector_hook_callback(callable_mp(this, &TileMapEditor::_undo_redo_inspector_callback)); } TileMapEditor::~TileMapEditor() { diff --git a/editor/plugins/tiles/tile_map_editor.h b/editor/plugins/tiles/tile_map_editor.h index 236774a06b..6e2f2ce2ba 100644 --- a/editor/plugins/tiles/tile_map_editor.h +++ b/editor/plugins/tiles/tile_map_editor.h @@ -47,7 +47,7 @@ public: virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) { return false; }; virtual void forward_canvas_draw_over_viewport(Control *p_overlay){}; virtual void tile_set_changed(){}; - virtual void edit(ObjectID p_tile_map_id){}; + virtual void edit(ObjectID p_tile_map_id, int p_tile_map_layer){}; }; class TileMapEditorTilesPlugin : public TileMapEditorPlugin { @@ -56,7 +56,8 @@ class TileMapEditorTilesPlugin : public TileMapEditorPlugin { private: UndoRedo *undo_redo = EditorNode::get_undo_redo(); ObjectID tile_map_id; - virtual void edit(ObjectID p_tile_map_id) override; + int tile_map_layer = -1; + virtual void edit(ObjectID p_tile_map_id, int p_tile_map_layer) override; ///// Toolbar ///// HBoxContainer *toolbar; @@ -185,7 +186,8 @@ class TileMapEditorTerrainsPlugin : public TileMapEditorPlugin { private: UndoRedo *undo_redo = EditorNode::get_undo_redo(); ObjectID tile_map_id; - virtual void edit(ObjectID p_tile_map_id) override; + int tile_map_layer = -1; + virtual void edit(ObjectID p_tile_map_id, int p_tile_map_layer) override; // Toolbar. HBoxContainer *toolbar; @@ -258,9 +260,9 @@ private: Map<Vector2i, TerrainsTilePattern> _wave_function_collapse(const Set<Vector2i> &p_to_replace, int p_terrain_set, const Set<TileMapEditorTerrainsPlugin::Constraint> p_constraints) const; TileMapCell _get_random_tile_from_pattern(int p_terrain_set, TerrainsTilePattern p_terrain_tile_pattern) const; Map<Vector2i, TileMapCell> _draw_terrains(const Map<Vector2i, TerrainsTilePattern> &p_to_paint, int p_terrain_set) const; + void _stop_dragging(); // Cached data. - TerrainsTilePattern _build_terrains_tile_pattern(TileData *p_tile_data); LocalVector<Map<TerrainsTilePattern, Set<TileMapCell>>> per_terrain_terrains_tile_patterns_tiles; LocalVector<LocalVector<Set<TerrainsTilePattern>>> per_terrain_terrains_tile_patterns; @@ -300,12 +302,20 @@ private: UndoRedo *undo_redo = EditorNode::get_undo_redo(); bool tileset_changed_needs_update = false; ObjectID tile_map_id; + int tile_map_layer = -1; // Vector to keep plugins. Vector<TileMapEditorPlugin *> tile_map_editor_plugins; // Toolbar. - HBoxContainer *tilemap_toolbar; + HBoxContainer *tile_map_toolbar; + + PopupMenu *layers_selection_popup; + Button *layers_selection_button; + Button *toogle_highlight_selected_layer_button; + void _layers_selection_button_draw(); + void _layers_selection_button_pressed(); + void _layers_selection_id_pressed(int p_id); Button *toggle_grid_button; void _on_grid_toggled(bool p_pressed); @@ -313,19 +323,26 @@ private: MenuButton *advanced_menu_button; void _advanced_menu_button_id_pressed(int p_id); - // Bottom panel + // Bottom panel. Label *missing_tileset_label; Tabs *tabs; void _update_bottom_panel(); - // TileMap + // TileMap. Ref<Texture2D> missing_tile_texture; Ref<Texture2D> warning_pattern_texture; - // CallBack + // CallBack. void _tile_map_changed(); void _tab_changed(int p_tab_changed); + // Updates. + void _layers_select_next_or_previous(bool p_next); + void _update_layers_selection(); + + // Inspector undo/redo callback. + void _undo_redo_inspector_callback(Object *p_undo_redo, Object *p_edited, String p_property, Variant p_new_value); + protected: void _notification(int p_what); void _draw_shape(Control *p_control, Rect2 p_region, TileSet::TileShape p_shape, TileSet::TileOffsetAxis p_offset_axis, Color p_color); @@ -335,7 +352,7 @@ public: void forward_canvas_draw_over_viewport(Control *p_overlay); void edit(TileMap *p_tile_map); - Control *get_toolbar() { return tilemap_toolbar; }; + Control *get_toolbar() { return tile_map_toolbar; }; TileMapEditor(); ~TileMapEditor(); diff --git a/editor/plugins/tiles/tile_proxies_manager_dialog.h b/editor/plugins/tiles/tile_proxies_manager_dialog.h index f6898e960b..6849be2cd6 100644 --- a/editor/plugins/tiles/tile_proxies_manager_dialog.h +++ b/editor/plugins/tiles/tile_proxies_manager_dialog.h @@ -34,9 +34,9 @@ #include "editor/editor_node.h" #include "editor/editor_properties.h" +#include "scene/2d/tile_map.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); diff --git a/editor/plugins/version_control_editor_plugin.cpp b/editor/plugins/version_control_editor_plugin.cpp index 5804f54649..a9a36427db 100644 --- a/editor/plugins/version_control_editor_plugin.cpp +++ b/editor/plugins/version_control_editor_plugin.cpp @@ -31,6 +31,7 @@ #include "version_control_editor_plugin.h" #include "core/object/script_language.h" +#include "core/os/keyboard.h" #include "editor/editor_file_system.h" #include "editor/editor_node.h" #include "editor/editor_scale.h" @@ -292,6 +293,29 @@ void VersionControlEditorPlugin::_update_commit_button() { commit_button->set_disabled(commit_message->get_text().strip_edges() == ""); } +void VersionControlEditorPlugin::_commit_message_gui_input(const Ref<InputEvent> &p_event) { + if (!commit_message->has_focus()) { + return; + } + if (commit_message->get_text().strip_edges().is_empty()) { + // Do not allow empty commit messages. + return; + } + const Ref<InputEventKey> k = p_event; + + if (k.is_valid() && k->is_pressed()) { + if (ED_IS_SHORTCUT("version_control/commit", p_event)) { + if (staged_files_count == 0) { + // Stage all files only when no files were previously staged. + _stage_all(); + } + _send_commit_msg(); + commit_message->accept_event(); + return; + } + } +} + void VersionControlEditorPlugin::register_editor() { if (!EditorVCSInterface::get_singleton()) { EditorNode::get_singleton()->add_control_to_dock(EditorNode::DOCK_SLOT_RIGHT_UL, version_commit_dock); @@ -462,7 +486,9 @@ VersionControlEditorPlugin::VersionControlEditorPlugin() { commit_message->set_custom_minimum_size(Size2(200, 100)); commit_message->set_wrap_enabled(true); commit_message->connect("text_changed", callable_mp(this, &VersionControlEditorPlugin::_update_commit_button)); + commit_message->connect("gui_input", callable_mp(this, &VersionControlEditorPlugin::_commit_message_gui_input)); commit_box_vbc->add_child(commit_message); + ED_SHORTCUT("version_control/commit", TTR("Commit"), KEY_MASK_CMD | KEY_ENTER); commit_button = memnew(Button); commit_button->set_text(TTR("Commit Changes")); diff --git a/editor/plugins/version_control_editor_plugin.h b/editor/plugins/version_control_editor_plugin.h index 7d7c66a7ee..d2ba63c86c 100644 --- a/editor/plugins/version_control_editor_plugin.h +++ b/editor/plugins/version_control_editor_plugin.h @@ -111,6 +111,7 @@ private: void _update_stage_status(); void _update_commit_status(); void _update_commit_button(); + void _commit_message_gui_input(const Ref<InputEvent> &p_event); friend class EditorVCSInterface; diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp index 7a2404fd80..86793af77f 100644 --- a/modules/visual_script/visual_script.cpp +++ b/modules/visual_script/visual_script.cpp @@ -271,6 +271,7 @@ void VisualScript::_node_ports_changed(int p_id) { void VisualScript::add_node(int p_id, const Ref<VisualScriptNode> &p_node, const Point2 &p_pos) { ERR_FAIL_COND(instances.size()); ERR_FAIL_COND(nodes.has(p_id)); // ID can exist only one in script. + ERR_FAIL_COND(p_node.is_null()); NodeData nd; nd.node = p_node; diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index e9efa1cf84..6afa0c0779 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -34,6 +34,8 @@ #include "core/math/geometry_2d.h" #include "core/os/os.h" +#include "servers/navigation_server_2d.h" + void TileMapPattern::set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i p_atlas_coords, int p_alternative_tile) { ERR_FAIL_COND_MSG(p_coords.x < 0 || p_coords.y < 0, vformat("Cannot set cell with negative coords in a TileMapPattern. Wrong coords: %s", p_coords)); @@ -235,40 +237,42 @@ Vector2i TileMap::transform_coords_layout(Vector2i p_coords, TileSet::TileOffset return output; } -int TileMap::get_effective_quadrant_size() const { +int TileMap::get_effective_quadrant_size(int p_layer) const { // When using YSort, the quadrant size is reduced to 1 to have one CanvasItem per quadrant - if (tile_set.is_valid() && tile_set->is_y_sorting()) { + if (is_y_sort_enabled() && layers[p_layer].y_sort_enabled) { return 1; } else { return quadrant_size; } } -Vector2i TileMap::_coords_to_quadrant_coords(const Vector2i &p_coords) const { - int quadrant_size = get_effective_quadrant_size(); +void TileMap::set_selected_layer(int p_layer_id) { + ERR_FAIL_COND(p_layer_id < -1 || p_layer_id >= (int)layers.size()); + selected_layer = p_layer_id; + emit_signal(SNAME("changed")); + _make_all_quadrants_dirty(); +} - // Rounding down, instead of simply rounding towards zero (truncating) - return Vector2i( - p_coords.x > 0 ? p_coords.x / quadrant_size : (p_coords.x - (quadrant_size - 1)) / quadrant_size, - p_coords.y > 0 ? p_coords.y / quadrant_size : (p_coords.y - (quadrant_size - 1)) / quadrant_size); +int TileMap::get_selected_layer() const { + return selected_layer; } void TileMap::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { pending_update = true; - _recreate_quadrants(); + _recreate_internals(); } break; case NOTIFICATION_EXIT_TREE: { - _clear_quadrants(); + _clear_internals(); } break; } // Transfers the notification to tileset plugins. if (tile_set.is_valid()) { - for (int i = 0; i < tile_set->get_tile_set_atlas_plugins().size(); i++) { - tile_set->get_tile_set_atlas_plugins()[i]->tilemap_notification(this, p_what); - } + _rendering_notification(p_what); + _physics_notification(p_what); + _navigation_notification(p_what); } } @@ -283,64 +287,210 @@ void TileMap::set_tileset(const Ref<TileSet> &p_tileset) { // Set the tileset, registering to its changes. if (tile_set.is_valid()) { - tile_set->disconnect("changed", callable_mp(this, &TileMap::_make_all_quadrants_dirty)); tile_set->disconnect("changed", callable_mp(this, &TileMap::_tile_set_changed)); } if (!p_tileset.is_valid()) { - _clear_quadrants(); + _clear_internals(); } tile_set = p_tileset; if (tile_set.is_valid()) { - tile_set->connect("changed", callable_mp(this, &TileMap::_make_all_quadrants_dirty), varray(true)); tile_set->connect("changed", callable_mp(this, &TileMap::_tile_set_changed)); - _recreate_quadrants(); + _recreate_internals(); } emit_signal(SNAME("changed")); } +void TileMap::set_quadrant_size(int p_size) { + ERR_FAIL_COND_MSG(p_size < 1, "TileMapQuadrant size cannot be smaller than 1."); + + quadrant_size = p_size; + _recreate_internals(); + emit_signal(SNAME("changed")); +} + int TileMap::get_quadrant_size() const { return quadrant_size; } -void TileMap::set_quadrant_size(int p_size) { - ERR_FAIL_COND_MSG(p_size < 1, "TileMapQuadrant size cannot be smaller than 1."); +void TileMap::set_layers_count(int p_layers_count) { + ERR_FAIL_COND(p_layers_count < 0); + _clear_internals(); - quadrant_size = p_size; - _recreate_quadrants(); + layers.resize(p_layers_count); + _recreate_internals(); + notify_property_list_changed(); + + if (selected_layer >= p_layers_count) { + selected_layer = -1; + } + + emit_signal(SNAME("changed")); + + update_configuration_warnings(); +} + +int TileMap::get_layers_count() const { + return layers.size(); +} + +void TileMap::set_layer_name(int p_layer, String p_name) { + ERR_FAIL_INDEX(p_layer, (int)layers.size()); + layers[p_layer].name = p_name; + emit_signal(SNAME("changed")); +} + +String TileMap::get_layer_name(int p_layer) const { + ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), String()); + return layers[p_layer].name; +} + +void TileMap::set_layer_enabled(int p_layer, bool p_enabled) { + ERR_FAIL_INDEX(p_layer, (int)layers.size()); + layers[p_layer].enabled = p_enabled; + _recreate_internals(); + emit_signal(SNAME("changed")); + + update_configuration_warnings(); +} + +bool TileMap::is_layer_enabled(int p_layer) const { + ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), false); + return layers[p_layer].enabled; +} + +void TileMap::set_layer_y_sort_enabled(int p_layer, bool p_y_sort_enabled) { + ERR_FAIL_INDEX(p_layer, (int)layers.size()); + layers[p_layer].y_sort_enabled = p_y_sort_enabled; + _recreate_internals(); emit_signal(SNAME("changed")); + + update_configuration_warnings(); +} + +bool TileMap::is_layer_y_sort_enabled(int p_layer) const { + ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), false); + return layers[p_layer].y_sort_enabled; +} + +void TileMap::set_layer_y_sort_origin(int p_layer, int p_y_sort_origin) { + ERR_FAIL_INDEX(p_layer, (int)layers.size()); + layers[p_layer].y_sort_origin = p_y_sort_origin; + _recreate_internals(); + emit_signal(SNAME("changed")); +} + +int TileMap::get_layer_y_sort_origin(int p_layer) const { + ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), false); + return layers[p_layer].y_sort_origin; +} + +void TileMap::set_layer_z_index(int p_layer, int p_z_index) { + ERR_FAIL_INDEX(p_layer, (int)layers.size()); + layers[p_layer].z_index = p_z_index; + _recreate_internals(); + emit_signal(SNAME("changed")); + + update_configuration_warnings(); +} + +int TileMap::get_layer_z_index(int p_layer) const { + ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), false); + return layers[p_layer].z_index; } void TileMap::set_collision_visibility_mode(TileMap::VisibilityMode p_show_collision) { - show_collision = p_show_collision; - _recreate_quadrants(); + collision_visibility_mode = p_show_collision; + _recreate_internals(); emit_signal(SNAME("changed")); } TileMap::VisibilityMode TileMap::get_collision_visibility_mode() { - return show_collision; + return collision_visibility_mode; } void TileMap::set_navigation_visibility_mode(TileMap::VisibilityMode p_show_navigation) { - show_navigation = p_show_navigation; - _recreate_quadrants(); + navigation_visibility_mode = p_show_navigation; + _recreate_internals(); emit_signal(SNAME("changed")); } TileMap::VisibilityMode TileMap::get_navigation_visibility_mode() { - return show_navigation; + return navigation_visibility_mode; } void TileMap::set_y_sort_enabled(bool p_enable) { Node2D::set_y_sort_enabled(p_enable); - _recreate_quadrants(); + _recreate_internals(); emit_signal(SNAME("changed")); } -void TileMap::update_dirty_quadrants() { +Vector2i TileMap::_coords_to_quadrant_coords(int p_layer, const Vector2i &p_coords) const { + int quadrant_size = get_effective_quadrant_size(p_layer); + + // Rounding down, instead of simply rounding towards zero (truncating) + return Vector2i( + p_coords.x > 0 ? p_coords.x / quadrant_size : (p_coords.x - (quadrant_size - 1)) / quadrant_size, + p_coords.y > 0 ? p_coords.y / quadrant_size : (p_coords.y - (quadrant_size - 1)) / quadrant_size); +} + +Map<Vector2i, TileMapQuadrant>::Element *TileMap::_create_quadrant(int p_layer, const Vector2i &p_qk) { + ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), nullptr); + + TileMapQuadrant q; + q.layer = p_layer; + q.coords = p_qk; + + rect_cache_dirty = true; + + // Create the debug canvas item. + RenderingServer *rs = RenderingServer::get_singleton(); + q.debug_canvas_item = rs->canvas_item_create(); + rs->canvas_item_set_z_index(q.debug_canvas_item, RS::CANVAS_ITEM_Z_MAX - 1); + rs->canvas_item_set_parent(q.debug_canvas_item, get_canvas_item()); + + // Call the create_quadrant method on plugins + if (tile_set.is_valid()) { + _rendering_create_quadrant(&q); + _physics_create_quadrant(&q); + } + + return layers[p_layer].quadrant_map.insert(p_qk, q); +} + +void TileMap::_make_quadrant_dirty(Map<Vector2i, TileMapQuadrant>::Element *Q) { + // Make the given quadrant dirty, then trigger an update later. + TileMapQuadrant &q = Q->get(); + if (!q.dirty_list_element.in_list()) { + layers[q.layer].dirty_quadrant_list.add(&q.dirty_list_element); + } + _queue_update_dirty_quadrants(); +} + +void TileMap::_make_all_quadrants_dirty() { + // Make all quandrants dirty, then trigger an update later. + for (unsigned int layer = 0; layer < layers.size(); layer++) { + for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) { + if (!E->value().dirty_list_element.in_list()) { + layers[layer].dirty_quadrant_list.add(&E->value().dirty_list_element); + } + } + } + _queue_update_dirty_quadrants(); +} + +void TileMap::_queue_update_dirty_quadrants() { + if (pending_update || !is_inside_tree()) { + return; + } + pending_update = true; + call_deferred(SNAME("_update_dirty_quadrants")); +} + +void TileMap::_update_dirty_quadrants() { if (!pending_update) { return; } @@ -349,43 +499,130 @@ void TileMap::update_dirty_quadrants() { return; } - // Update the coords cache. - for (SelfList<TileMapQuadrant> *q = dirty_quadrant_list.first(); q; q = q->next()) { - q->self()->map_to_world.clear(); - q->self()->world_to_map.clear(); - for (Set<Vector2i>::Element *E = q->self()->cells.front(); E; E = E->next()) { - Vector2i pk = E->get(); - Vector2i pk_world_coords = map_to_world(pk); - q->self()->map_to_world[pk] = pk_world_coords; - q->self()->world_to_map[pk_world_coords] = pk; + for (unsigned int layer = 0; layer < layers.size(); layer++) { + // Update the coords cache. + for (SelfList<TileMapQuadrant> *q = layers[layer].dirty_quadrant_list.first(); q; q = q->next()) { + q->self()->map_to_world.clear(); + q->self()->world_to_map.clear(); + for (Set<Vector2i>::Element *E = q->self()->cells.front(); E; E = E->next()) { + Vector2i pk = E->get(); + Vector2i pk_world_coords = map_to_world(pk); + q->self()->map_to_world[pk] = pk_world_coords; + q->self()->world_to_map[pk_world_coords] = pk; + } + } + + // Call the update_dirty_quadrant method on plugins. + _rendering_update_dirty_quadrants(layers[layer].dirty_quadrant_list); + _physics_update_dirty_quadrants(layers[layer].dirty_quadrant_list); + _navigation_update_dirty_quadrants(layers[layer].dirty_quadrant_list); + _scenes_update_dirty_quadrants(layers[layer].dirty_quadrant_list); + + // Redraw the debug canvas_items. + RenderingServer *rs = RenderingServer::get_singleton(); + for (SelfList<TileMapQuadrant> *q = layers[layer].dirty_quadrant_list.first(); q; q = q->next()) { + rs->canvas_item_clear(q->self()->debug_canvas_item); + Transform2D xform; + xform.set_origin(map_to_world(q->self()->coords * get_effective_quadrant_size(layer))); + rs->canvas_item_set_transform(q->self()->debug_canvas_item, xform); + + _rendering_draw_quadrant_debug(q->self()); + _physics_draw_quadrant_debug(q->self()); + _navigation_draw_quadrant_debug(q->self()); + _scenes_draw_quadrant_debug(q->self()); } - } - // Call the update_dirty_quadrant method on plugins. - for (int i = 0; i < tile_set->get_tile_set_atlas_plugins().size(); i++) { - tile_set->get_tile_set_atlas_plugins()[i]->update_dirty_quadrants(this, dirty_quadrant_list); + // Clear the list + while (layers[layer].dirty_quadrant_list.first()) { + layers[layer].dirty_quadrant_list.remove(layers[layer].dirty_quadrant_list.first()); + } } - // Redraw the debug canvas_items. - RenderingServer *rs = RenderingServer::get_singleton(); - for (SelfList<TileMapQuadrant> *q = dirty_quadrant_list.first(); q; q = q->next()) { - rs->canvas_item_clear(q->self()->debug_canvas_item); - Transform2D xform; - xform.set_origin(map_to_world(q->self()->coords * get_effective_quadrant_size())); - rs->canvas_item_set_transform(q->self()->debug_canvas_item, xform); - for (int i = 0; i < tile_set->get_tile_set_atlas_plugins().size(); i++) { - tile_set->get_tile_set_atlas_plugins()[i]->draw_quadrant_debug(this, q->self()); + pending_update = false; + + _recompute_rect_cache(); +} + +void TileMap::_recreate_internals() { + // Clear all internals. + _clear_internals(); + + for (unsigned int layer = 0; layer < layers.size(); layer++) { + if (!layers[layer].enabled) { + continue; + } + + // Upadate the layer internals. + _rendering_update_layer(layer); + + // Recreate the quadrants. + const Map<Vector2i, TileMapCell> &tile_map = layers[layer].tile_map; + for (Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) { + Vector2i qk = _coords_to_quadrant_coords(layer, Vector2i(E->key().x, E->key().y)); + + Map<Vector2i, TileMapQuadrant>::Element *Q = layers[layer].quadrant_map.find(qk); + if (!Q) { + Q = _create_quadrant(layer, qk); + layers[layer].dirty_quadrant_list.add(&Q->get().dirty_list_element); + } + + Vector2i pk = E->key(); + Q->get().cells.insert(pk); + + _make_quadrant_dirty(Q); } } - // Clear the list - while (dirty_quadrant_list.first()) { - dirty_quadrant_list.remove(dirty_quadrant_list.first()); + _update_dirty_quadrants(); +} + +void TileMap::_erase_quadrant(Map<Vector2i, TileMapQuadrant>::Element *Q) { + // Remove a quadrant. + TileMapQuadrant *q = &(Q->get()); + + // Call the cleanup_quadrant method on plugins. + if (tile_set.is_valid()) { + _rendering_cleanup_quadrant(q); + _physics_cleanup_quadrant(q); + _navigation_cleanup_quadrant(q); + _scenes_cleanup_quadrant(q); } - pending_update = false; + // Remove the quadrant from the dirty_list if it is there. + if (q->dirty_list_element.in_list()) { + layers[q->layer].dirty_quadrant_list.remove(&(q->dirty_list_element)); + } - _recompute_rect_cache(); + // Free the debug canvas item. + RenderingServer *rs = RenderingServer::get_singleton(); + rs->free(q->debug_canvas_item); + + layers[q->layer].quadrant_map.erase(Q); + rect_cache_dirty = true; +} + +void TileMap::_clear_layer_internals(int p_layer) { + ERR_FAIL_INDEX(p_layer, (int)layers.size()); + + // Clear quadrants. + while (layers[p_layer].quadrant_map.size()) { + _erase_quadrant(layers[p_layer].quadrant_map.front()); + } + + // Clear the layers internals. + _rendering_cleanup_layer(p_layer); + + // Clear the dirty quadrants list. + while (layers[p_layer].dirty_quadrant_list.first()) { + layers[p_layer].dirty_quadrant_list.remove(layers[p_layer].dirty_quadrant_list.first()); + } +} + +void TileMap::_clear_internals() { + // Clear quadrants. + for (unsigned int layer = 0; layer < layers.size(); layer++) { + _clear_layer_internals(layer); + } } void TileMap::_recompute_rect_cache() { @@ -397,16 +634,18 @@ void TileMap::_recompute_rect_cache() { } Rect2 r_total; - for (Map<Vector2i, TileMapQuadrant>::Element *E = quadrant_map.front(); E; E = E->next()) { - Rect2 r; - r.position = map_to_world(E->key() * get_effective_quadrant_size()); - r.expand_to(map_to_world((E->key() + Vector2i(1, 0)) * get_effective_quadrant_size())); - r.expand_to(map_to_world((E->key() + Vector2i(1, 1)) * get_effective_quadrant_size())); - r.expand_to(map_to_world((E->key() + Vector2i(0, 1)) * get_effective_quadrant_size())); - if (E == quadrant_map.front()) { - r_total = r; - } else { - r_total = r_total.merge(r); + for (unsigned int layer = 0; layer < layers.size(); layer++) { + for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) { + Rect2 r; + r.position = map_to_world(E->key() * get_effective_quadrant_size(layer)); + r.expand_to(map_to_world((E->key() + Vector2i(1, 0)) * get_effective_quadrant_size(layer))); + r.expand_to(map_to_world((E->key() + Vector2i(1, 1)) * get_effective_quadrant_size(layer))); + r.expand_to(map_to_world((E->key() + Vector2i(0, 1)) * get_effective_quadrant_size(layer))); + if (E == layers[layer].quadrant_map.front()) { + r_total = r; + } else { + r_total = r_total.merge(r); + } } } @@ -418,94 +657,884 @@ void TileMap::_recompute_rect_cache() { #endif } -Map<Vector2i, TileMapQuadrant>::Element *TileMap::_create_quadrant(const Vector2i &p_qk) { - TileMapQuadrant q; - q.coords = p_qk; +/////////////////////////////// Rendering ////////////////////////////////////// - rect_cache_dirty = true; +void TileMap::_rendering_notification(int p_what) { + switch (p_what) { + case CanvasItem::NOTIFICATION_VISIBILITY_CHANGED: { + bool visible = is_visible_in_tree(); + for (int layer = 0; layer < (int)layers.size(); layer++) { + for (Map<Vector2i, TileMapQuadrant>::Element *E_quadrant = layers[layer].quadrant_map.front(); E_quadrant; E_quadrant = E_quadrant->next()) { + TileMapQuadrant &q = E_quadrant->get(); + + // Update occluders transform. + for (Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator>::Element *E_cell = q.world_to_map.front(); E_cell; E_cell = E_cell->next()) { + Transform2D xform; + xform.set_origin(E_cell->key()); + for (const RID &occluder : q.occluders) { + RS::get_singleton()->canvas_light_occluder_set_enabled(occluder, visible); + } + } + } + } + } break; + case CanvasItem::NOTIFICATION_TRANSFORM_CHANGED: { + if (!is_inside_tree()) { + return; + } + for (int layer = 0; layer < (int)layers.size(); layer++) { + for (Map<Vector2i, TileMapQuadrant>::Element *E_quadrant = layers[layer].quadrant_map.front(); E_quadrant; E_quadrant = E_quadrant->next()) { + TileMapQuadrant &q = E_quadrant->get(); + + // Update occluders transform. + for (Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator>::Element *E_cell = q.world_to_map.front(); E_cell; E_cell = E_cell->next()) { + Transform2D xform; + xform.set_origin(E_cell->key()); + for (const RID &occluder : q.occluders) { + RS::get_singleton()->canvas_light_occluder_set_transform(occluder, get_global_transform() * xform); + } + } + } + } + } break; + case CanvasItem::NOTIFICATION_DRAW: { + if (tile_set.is_valid()) { + RenderingServer::get_singleton()->canvas_item_set_sort_children_by_y(get_canvas_item(), is_y_sort_enabled()); + } + } break; + } +} + +void TileMap::_rendering_update_layer(int p_layer) { + ERR_FAIL_INDEX(p_layer, (int)layers.size()); - // Create the debug canvas item. RenderingServer *rs = RenderingServer::get_singleton(); - q.debug_canvas_item = rs->canvas_item_create(); - rs->canvas_item_set_z_index(q.debug_canvas_item, RS::CANVAS_ITEM_Z_MAX - 1); - rs->canvas_item_set_parent(q.debug_canvas_item, get_canvas_item()); + if (!layers[p_layer].canvas_item.is_valid()) { + RID ci = rs->canvas_item_create(); + rs->canvas_item_set_parent(ci, get_canvas_item()); - // Call the create_quadrant method on plugins - if (tile_set.is_valid()) { - for (int i = 0; i < tile_set->get_tile_set_atlas_plugins().size(); i++) { - tile_set->get_tile_set_atlas_plugins()[i]->create_quadrant(this, &q); - } + /*Transform2D xform; + xform.set_origin(Vector2(0, p_layer)); + rs->canvas_item_set_transform(ci, xform);*/ + rs->canvas_item_set_draw_index(ci, p_layer); + + layers[p_layer].canvas_item = ci; } + RID &ci = layers[p_layer].canvas_item; + rs->canvas_item_set_sort_children_by_y(ci, layers[p_layer].y_sort_enabled); + rs->canvas_item_set_use_parent_material(ci, get_use_parent_material() || get_material().is_valid()); + rs->canvas_item_set_z_index(ci, layers[p_layer].z_index); + rs->canvas_item_set_default_texture_filter(ci, RS::CanvasItemTextureFilter(get_texture_filter())); + rs->canvas_item_set_default_texture_repeat(ci, RS::CanvasItemTextureRepeat(get_texture_repeat())); + rs->canvas_item_set_light_mask(ci, get_light_mask()); +} - return quadrant_map.insert(p_qk, q); +void TileMap::_rendering_cleanup_layer(int p_layer) { + ERR_FAIL_INDEX(p_layer, (int)layers.size()); + + RenderingServer *rs = RenderingServer::get_singleton(); + if (!layers[p_layer].canvas_item.is_valid()) { + rs->free(layers[p_layer].canvas_item); + } } -void TileMap::_erase_quadrant(Map<Vector2i, TileMapQuadrant>::Element *Q) { - // Remove a quadrant. - TileMapQuadrant *q = &(Q->get()); +void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) { + ERR_FAIL_COND(!is_inside_tree()); + ERR_FAIL_COND(!tile_set.is_valid()); - // Call the cleanup_quadrant method on plugins. - if (tile_set.is_valid()) { - for (int i = 0; i < tile_set->get_tile_set_atlas_plugins().size(); i++) { - tile_set->get_tile_set_atlas_plugins()[i]->cleanup_quadrant(this, q); + bool visible = is_visible_in_tree(); + + SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first(); + while (q_list_element) { + TileMapQuadrant &q = *q_list_element->self(); + + RenderingServer *rs = RenderingServer::get_singleton(); + + // Free the canvas items. + for (const RID &ci : q.canvas_items) { + rs->free(ci); + } + q.canvas_items.clear(); + + // Free the occluders. + for (const RID &occluder : q.occluders) { + rs->free(occluder); } + q.occluders.clear(); + + // Those allow to group cell per material or z-index. + Ref<ShaderMaterial> prev_material; + int prev_z_index = 0; + RID prev_canvas_item; + + // 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 = get_cell(q.layer, E_cell->value(), true); + + TileSetSource *source; + if (tile_set->has_source(c.source_id)) { + source = *tile_set->get_source(c.source_id); + + if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { + continue; + } + + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source) { + // Get the tile data. + TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile)); + Ref<ShaderMaterial> mat = tile_data->tile_get_material(); + int z_index = layers[q.layer].z_index + tile_data->get_z_index(); + + // Quandrant pos. + Vector2 position = map_to_world(q.coords * get_effective_quadrant_size(q.layer)); + if (is_y_sort_enabled() && layers[q.layer].y_sort_enabled) { + // When Y-sorting, the quandrant size is sure to be 1, we can thus offset the CanvasItem. + position.y += layers[q.layer].y_sort_origin + tile_data->get_y_sort_origin(); + } + + // --- CanvasItems --- + // Create two canvas items, for rendering and debug. + RID canvas_item; + + // Check if the material or the z_index changed. + if (prev_canvas_item == RID() || prev_material != mat || prev_z_index != z_index) { + // If so, create a new CanvasItem. + canvas_item = rs->canvas_item_create(); + if (mat.is_valid()) { + rs->canvas_item_set_material(canvas_item, mat->get_rid()); + } + rs->canvas_item_set_parent(canvas_item, layers[q.layer].canvas_item); + rs->canvas_item_set_use_parent_material(canvas_item, get_use_parent_material() || get_material().is_valid()); + + Transform2D xform; + xform.set_origin(position); + rs->canvas_item_set_transform(canvas_item, xform); + + rs->canvas_item_set_light_mask(canvas_item, get_light_mask()); + rs->canvas_item_set_z_index(canvas_item, z_index); + + rs->canvas_item_set_default_texture_filter(canvas_item, RS::CanvasItemTextureFilter(get_texture_filter())); + rs->canvas_item_set_default_texture_repeat(canvas_item, RS::CanvasItemTextureRepeat(get_texture_repeat())); + + q.canvas_items.push_back(canvas_item); + + prev_canvas_item = canvas_item; + prev_material = mat; + prev_z_index = z_index; + + } else { + // Keep the same canvas_item to draw on. + canvas_item = prev_canvas_item; + } + + // Drawing the tile in the canvas item. + Color modulate = get_self_modulate(); + if (selected_layer >= 0) { + if (q.layer < selected_layer) { + modulate = modulate.darkened(0.5); + } else if (q.layer > selected_layer) { + modulate = modulate.darkened(0.5); + modulate.a *= 0.3; + } + } + draw_tile(canvas_item, E_cell->key() - position, tile_set, c.source_id, c.get_atlas_coords(), c.alternative_tile, modulate); + + // --- Occluders --- + for (int i = 0; i < tile_set->get_occlusion_layers_count(); i++) { + Transform2D xform; + xform.set_origin(E_cell->key()); + if (tile_data->get_occluder(i).is_valid()) { + RID occluder_id = rs->canvas_light_occluder_create(); + rs->canvas_light_occluder_set_enabled(occluder_id, visible); + rs->canvas_light_occluder_set_transform(occluder_id, get_global_transform() * xform); + rs->canvas_light_occluder_set_polygon(occluder_id, tile_data->get_occluder(i)->get_rid()); + rs->canvas_light_occluder_attach_to_canvas(occluder_id, get_canvas()); + rs->canvas_light_occluder_set_light_mask(occluder_id, tile_set->get_occlusion_layer_light_mask(i)); + q.occluders.push_back(occluder_id); + } + } + } + } + } + + _rendering_quadrant_order_dirty = true; + q_list_element = q_list_element->next(); } - // Remove the quadrant from the dirty_list if it is there. - if (q->dirty_list_element.in_list()) { - dirty_quadrant_list.remove(&(q->dirty_list_element)); + // Reset the drawing indices + if (_rendering_quadrant_order_dirty) { + int index = -(int64_t)0x80000000; //always must be drawn below children. + + for (int layer = 0; layer < (int)layers.size(); layer++) { + // Sort the quadrants coords per world coordinates + Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator> world_to_map; + for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) { + world_to_map[map_to_world(E->key())] = E->key(); + } + + // Sort the quadrants + for (Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator>::Element *E = world_to_map.front(); E; E = E->next()) { + TileMapQuadrant &q = layers[layer].quadrant_map[E->value()]; + for (const RID &ci : q.canvas_items) { + RS::get_singleton()->canvas_item_set_draw_index(ci, index++); + } + } + } + _rendering_quadrant_order_dirty = false; } +} - // Free the debug canvas item. +void TileMap::_rendering_create_quadrant(TileMapQuadrant *p_quadrant) { + ERR_FAIL_COND(!tile_set.is_valid()); + + _rendering_quadrant_order_dirty = true; +} + +void TileMap::_rendering_cleanup_quadrant(TileMapQuadrant *p_quadrant) { + // Free the canvas items. + for (const RID &ci : p_quadrant->canvas_items) { + RenderingServer::get_singleton()->free(ci); + } + p_quadrant->canvas_items.clear(); + + // Free the occluders. + for (const RID &occluder : p_quadrant->occluders) { + RenderingServer::get_singleton()->free(occluder); + } + p_quadrant->occluders.clear(); +} + +void TileMap::_rendering_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { + ERR_FAIL_COND(!tile_set.is_valid()); + + if (!Engine::get_singleton()->is_editor_hint()) { + return; + } + + // Draw a placeholder for scenes needing one. RenderingServer *rs = RenderingServer::get_singleton(); - rs->free(q->debug_canvas_item); + Vector2 quadrant_pos = map_to_world(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer)); + for (Set<Vector2i>::Element *E_cell = p_quadrant->cells.front(); E_cell; E_cell = E_cell->next()) { + const TileMapCell &c = get_cell(p_quadrant->layer, E_cell->get(), true); - quadrant_map.erase(Q); - rect_cache_dirty = true; + TileSetSource *source; + if (tile_set->has_source(c.source_id)) { + source = *tile_set->get_source(c.source_id); + + if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { + continue; + } + + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source) { + Vector2i grid_size = atlas_source->get_atlas_grid_size(); + if (!atlas_source->get_texture().is_valid() || c.get_atlas_coords().x >= grid_size.x || c.get_atlas_coords().y >= grid_size.y) { + // Generate a random color from the hashed values of the tiles. + Array to_hash; + to_hash.push_back(c.source_id); + to_hash.push_back(c.get_atlas_coords()); + to_hash.push_back(c.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 a placeholder tile. + Transform2D xform; + xform.set_origin(map_to_world(E_cell->get()) - quadrant_pos); + rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform); + rs->canvas_item_add_circle(p_quadrant->debug_canvas_item, Vector2(), MIN(tile_set->get_tile_size().x, tile_set->get_tile_size().y) / 4.0, color); + } + } + } + } } -void TileMap::_make_all_quadrants_dirty(bool p_update) { - // Make all quandrants dirty, then trigger an update later. - for (Map<Vector2i, TileMapQuadrant>::Element *E = quadrant_map.front(); E; E = E->next()) { - if (!E->value().dirty_list_element.in_list()) { - dirty_quadrant_list.add(&E->value().dirty_list_element); +void TileMap::draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, Color p_modulation) { + ERR_FAIL_COND(!p_tile_set.is_valid()); + ERR_FAIL_COND(!p_tile_set->has_source(p_atlas_source_id)); + ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_tile(p_atlas_coords)); + ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_alternative_tile(p_atlas_coords, p_alternative_tile)); + + TileSetSource *source = *p_tile_set->get_source(p_atlas_source_id); + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source) { + // Get the texture. + Ref<Texture2D> tex = atlas_source->get_texture(); + if (!tex.is_valid()) { + return; + } + + // Check if we are in the texture, return otherwise. + Vector2i grid_size = atlas_source->get_atlas_grid_size(); + if (p_atlas_coords.x >= grid_size.x || p_atlas_coords.y >= grid_size.y) { + return; + } + + // Get tile data. + TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(p_atlas_coords, p_alternative_tile)); + + // Compute the offset + Rect2i source_rect = atlas_source->get_tile_texture_region(p_atlas_coords); + Vector2i tile_offset = atlas_source->get_tile_effective_texture_offset(p_atlas_coords, p_alternative_tile); + + // Compute the destination rectangle in the CanvasItem. + Rect2 dest_rect; + dest_rect.size = source_rect.size; + dest_rect.size.x += FP_ADJUST; + dest_rect.size.y += FP_ADJUST; + + bool transpose = tile_data->get_transpose(); + if (transpose) { + dest_rect.position = (p_position - Vector2(dest_rect.size.y, dest_rect.size.x) / 2 - tile_offset); + } else { + dest_rect.position = (p_position - dest_rect.size / 2 - tile_offset); + } + + if (tile_data->get_flip_h()) { + dest_rect.size.x = -dest_rect.size.x; + } + + if (tile_data->get_flip_v()) { + dest_rect.size.y = -dest_rect.size.y; } + + // Get the tile modulation. + Color modulate = tile_data->get_modulate(); + modulate = Color(modulate.r * p_modulation.r, modulate.g * p_modulation.g, modulate.b * p_modulation.b, modulate.a * p_modulation.a); + + // Draw the tile. + tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping()); } +} - if (pending_update) { +/////////////////////////////// Physics ////////////////////////////////////// + +void TileMap::_physics_notification(int p_what) { + switch (p_what) { + case CanvasItem::NOTIFICATION_TRANSFORM_CHANGED: { + // Update the bodies transforms. + if (is_inside_tree()) { + for (int layer = 0; layer < (int)layers.size(); layer++) { + Transform2D global_transform = get_global_transform(); + + for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) { + TileMapQuadrant &q = E->get(); + + Transform2D xform; + xform.set_origin(map_to_world(E->key() * get_effective_quadrant_size(layer))); + xform = global_transform * xform; + + for (int body_index = 0; body_index < q.bodies.size(); body_index++) { + PhysicsServer2D::get_singleton()->body_set_state(q.bodies[body_index], PhysicsServer2D::BODY_STATE_TRANSFORM, xform); + } + } + } + } + } break; + } +} + +void TileMap::_physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) { + ERR_FAIL_COND(!is_inside_tree()); + ERR_FAIL_COND(!tile_set.is_valid()); + + Transform2D global_transform = get_global_transform(); + PhysicsServer2D *ps = PhysicsServer2D::get_singleton(); + + SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first(); + while (q_list_element) { + TileMapQuadrant &q = *q_list_element->self(); + + Vector2 quadrant_pos = map_to_world(q.coords * get_effective_quadrant_size(q.layer)); + + // Clear shapes. + for (int body_index = 0; body_index < q.bodies.size(); body_index++) { + ps->body_clear_shapes(q.bodies[body_index]); + + // Position the bodies. + Transform2D xform; + xform.set_origin(quadrant_pos); + xform = global_transform * xform; + ps->body_set_state(q.bodies[body_index], PhysicsServer2D::BODY_STATE_TRANSFORM, xform); + } + + for (Set<Vector2i>::Element *E_cell = q.cells.front(); E_cell; E_cell = E_cell->next()) { + TileMapCell c = get_cell(q.layer, E_cell->get(), true); + + TileSetSource *source; + if (tile_set->has_source(c.source_id)) { + source = *tile_set->get_source(c.source_id); + + if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { + continue; + } + + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source) { + TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile)); + + for (int body_index = 0; body_index < q.bodies.size(); body_index++) { + // Add the shapes again. + for (int polygon_index = 0; polygon_index < tile_data->get_collision_polygons_count(body_index); polygon_index++) { + bool one_way_collision = tile_data->is_collision_polygon_one_way(body_index, polygon_index); + float one_way_collision_margin = tile_data->get_collision_polygon_one_way_margin(body_index, polygon_index); + + int shapes_count = tile_data->get_collision_polygon_shapes_count(body_index, polygon_index); + for (int shape_index = 0; shape_index < shapes_count; shape_index++) { + Transform2D xform = Transform2D(); + xform.set_origin(map_to_world(E_cell->get()) - quadrant_pos); + + // Add decomposed convex shapes. + Ref<ConvexPolygonShape2D> shape = tile_data->get_collision_polygon_shape(body_index, polygon_index, shape_index); + ps->body_add_shape(q.bodies[body_index], shape->get_rid(), xform); + ps->body_set_shape_metadata(q.bodies[body_index], shape_index, E_cell->get()); + ps->body_set_shape_as_one_way_collision(q.bodies[body_index], shape_index, one_way_collision, one_way_collision_margin); + } + } + } + } + } + } + + q_list_element = q_list_element->next(); + } +} + +void TileMap::_physics_create_quadrant(TileMapQuadrant *p_quadrant) { + ERR_FAIL_COND(!tile_set.is_valid()); + + //Get the TileMap's gobla transform. + Transform2D global_transform; + if (is_inside_tree()) { + global_transform = get_global_transform(); + } + + // Clear all bodies. + p_quadrant->bodies.clear(); + + // Create the body and set its parameters. + for (int layer = 0; layer < tile_set->get_physics_layers_count(); layer++) { + RID body = PhysicsServer2D::get_singleton()->body_create(); + PhysicsServer2D::get_singleton()->body_set_mode(body, PhysicsServer2D::BODY_MODE_STATIC); + + PhysicsServer2D::get_singleton()->body_attach_object_instance_id(body, get_instance_id()); + PhysicsServer2D::get_singleton()->body_set_collision_layer(body, tile_set->get_physics_layer_collision_layer(layer)); + PhysicsServer2D::get_singleton()->body_set_collision_mask(body, tile_set->get_physics_layer_collision_mask(layer)); + + Ref<PhysicsMaterial> physics_material = tile_set->get_physics_layer_physics_material(layer); + if (!physics_material.is_valid()) { + PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_BOUNCE, 0); + PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_FRICTION, 1); + } else { + PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_BOUNCE, physics_material->computed_bounce()); + PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_FRICTION, physics_material->computed_friction()); + } + + if (is_inside_tree()) { + RID space = get_world_2d()->get_space(); + PhysicsServer2D::get_singleton()->body_set_space(body, space); + + Transform2D xform; + xform.set_origin(map_to_world(p_quadrant->coords * get_effective_quadrant_size(layer))); + xform = global_transform * xform; + PhysicsServer2D::get_singleton()->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform); + } + + p_quadrant->bodies.push_back(body); + } +} + +void TileMap::_physics_cleanup_quadrant(TileMapQuadrant *p_quadrant) { + // Remove a quadrant. + for (int body_index = 0; body_index < p_quadrant->bodies.size(); body_index++) { + PhysicsServer2D::get_singleton()->free(p_quadrant->bodies[body_index]); + } + p_quadrant->bodies.clear(); +} + +void TileMap::_physics_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { + // Draw the debug collision shapes. + ERR_FAIL_COND(!tile_set.is_valid()); + + if (!get_tree()) { return; } - pending_update = true; - if (!is_inside_tree()) { + + bool show_collision = false; + switch (collision_visibility_mode) { + case TileMap::VISIBILITY_MODE_DEFAULT: + show_collision = !Engine::get_singleton()->is_editor_hint() && (get_tree() && get_tree()->is_debugging_navigation_hint()); + break; + case TileMap::VISIBILITY_MODE_FORCE_HIDE: + show_collision = false; + break; + case TileMap::VISIBILITY_MODE_FORCE_SHOW: + show_collision = true; + break; + } + if (!show_collision) { return; } - if (p_update) { - call_deferred(SNAME("update_dirty_quadrants")); + + RenderingServer *rs = RenderingServer::get_singleton(); + + Vector2 quadrant_pos = map_to_world(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer)); + + Color debug_collision_color = 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 = get_cell(p_quadrant->layer, E_cell->get(), true); + + Transform2D xform; + xform.set_origin(map_to_world(E_cell->get()) - quadrant_pos); + rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform); + + if (tile_set->has_source(c.source_id)) { + TileSetSource *source = *tile_set->get_source(c.source_id); + + if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { + continue; + } + + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source) { + TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile)); + + for (int body_index = 0; body_index < p_quadrant->bodies.size(); body_index++) { + for (int polygon_index = 0; polygon_index < tile_data->get_collision_polygons_count(body_index); polygon_index++) { + // Draw the debug polygon. + Vector<Vector2> polygon = tile_data->get_collision_polygon_points(body_index, polygon_index); + if (polygon.size() >= 3) { + Vector<Color> color; + color.push_back(debug_collision_color); + rs->canvas_item_add_polygon(p_quadrant->debug_canvas_item, polygon, color); + } + } + } + } + } + rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, Transform2D()); + } +}; + +/////////////////////////////// Navigation ////////////////////////////////////// + +void TileMap::_navigation_notification(int p_what) { + switch (p_what) { + case CanvasItem::NOTIFICATION_TRANSFORM_CHANGED: { + if (is_inside_tree()) { + for (int layer = 0; layer < (int)layers.size(); layer++) { + Transform2D tilemap_xform = get_global_transform(); + for (Map<Vector2i, TileMapQuadrant>::Element *E_quadrant = layers[layer].quadrant_map.front(); E_quadrant; E_quadrant = E_quadrant->next()) { + TileMapQuadrant &q = E_quadrant->get(); + for (Map<Vector2i, Vector<RID>>::Element *E_region = q.navigation_regions.front(); E_region; E_region = E_region->next()) { + for (int layer_index = 0; layer_index < E_region->get().size(); layer_index++) { + RID region = E_region->get()[layer_index]; + if (!region.is_valid()) { + continue; + } + Transform2D tile_transform; + tile_transform.set_origin(map_to_world(E_region->key())); + NavigationServer2D::get_singleton()->region_set_transform(region, tilemap_xform * tile_transform); + } + } + } + } + } + } break; } } -void TileMap::_make_quadrant_dirty(Map<Vector2i, TileMapQuadrant>::Element *Q, bool p_update) { - // Make the given quadrant dirty, then trigger an update later. - TileMapQuadrant &q = Q->get(); - if (!q.dirty_list_element.in_list()) { - dirty_quadrant_list.add(&q.dirty_list_element); +void TileMap::_navigation_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) { + ERR_FAIL_COND(!is_inside_tree()); + ERR_FAIL_COND(!tile_set.is_valid()); + + // Get colors for debug. + SceneTree *st = SceneTree::get_singleton(); + Color debug_navigation_color; + bool debug_navigation = st && st->is_debugging_navigation_hint(); + if (debug_navigation) { + debug_navigation_color = st->get_debug_navigation_color(); + } + + Transform2D tilemap_xform = get_global_transform(); + SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first(); + while (q_list_element) { + TileMapQuadrant &q = *q_list_element->self(); + + // Clear navigation shapes in the quadrant. + for (Map<Vector2i, Vector<RID>>::Element *E = q.navigation_regions.front(); E; E = E->next()) { + for (int i = 0; i < E->get().size(); i++) { + RID region = E->get()[i]; + if (!region.is_valid()) { + continue; + } + NavigationServer2D::get_singleton()->region_set_map(region, RID()); + } + } + q.navigation_regions.clear(); + + // 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 = get_cell(q.layer, E_cell->get(), true); + + TileSetSource *source; + if (tile_set->has_source(c.source_id)) { + source = *tile_set->get_source(c.source_id); + + if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { + continue; + } + + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source) { + TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile)); + q.navigation_regions[E_cell->get()].resize(tile_set->get_navigation_layers_count()); + + for (int layer_index = 0; layer_index < tile_set->get_navigation_layers_count(); layer_index++) { + Ref<NavigationPolygon> navpoly; + navpoly = tile_data->get_navigation_polygon(layer_index); + + if (navpoly.is_valid()) { + Transform2D tile_transform; + tile_transform.set_origin(map_to_world(E_cell->get())); + + RID region = NavigationServer2D::get_singleton()->region_create(); + NavigationServer2D::get_singleton()->region_set_map(region, get_world_2d()->get_navigation_map()); + NavigationServer2D::get_singleton()->region_set_transform(region, tilemap_xform * tile_transform); + NavigationServer2D::get_singleton()->region_set_navpoly(region, navpoly); + q.navigation_regions[E_cell->get()].write[layer_index] = region; + } + } + } + } + } + + q_list_element = q_list_element->next(); } +} - if (pending_update) { +void TileMap::_navigation_cleanup_quadrant(TileMapQuadrant *p_quadrant) { + // Clear navigation shapes in the quadrant. + for (Map<Vector2i, Vector<RID>>::Element *E = p_quadrant->navigation_regions.front(); E; E = E->next()) { + for (int i = 0; i < E->get().size(); i++) { + RID region = E->get()[i]; + if (!region.is_valid()) { + continue; + } + NavigationServer2D::get_singleton()->free(region); + } + } + p_quadrant->navigation_regions.clear(); +} + +void TileMap::_navigation_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { + // Draw the debug collision shapes. + ERR_FAIL_COND(!tile_set.is_valid()); + + if (!get_tree()) { return; } - pending_update = true; - if (!is_inside_tree()) { + + bool show_navigation = false; + switch (navigation_visibility_mode) { + case TileMap::VISIBILITY_MODE_DEFAULT: + show_navigation = !Engine::get_singleton()->is_editor_hint() && (get_tree() && get_tree()->is_debugging_navigation_hint()); + break; + case TileMap::VISIBILITY_MODE_FORCE_HIDE: + show_navigation = false; + break; + case TileMap::VISIBILITY_MODE_FORCE_SHOW: + show_navigation = true; + break; + } + if (!show_navigation) { + return; + } + + RenderingServer *rs = RenderingServer::get_singleton(); + + Color color = get_tree()->get_debug_navigation_color(); + RandomPCG rand; + + Vector2 quadrant_pos = map_to_world(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer)); + + for (Set<Vector2i>::Element *E_cell = p_quadrant->cells.front(); E_cell; E_cell = E_cell->next()) { + TileMapCell c = get_cell(p_quadrant->layer, E_cell->get(), true); + + TileSetSource *source; + if (tile_set->has_source(c.source_id)) { + source = *tile_set->get_source(c.source_id); + + if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { + continue; + } + + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source) { + TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile)); + + Transform2D xform; + xform.set_origin(map_to_world(E_cell->get()) - quadrant_pos); + rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform); + + for (int layer_index = 0; layer_index < tile_set->get_navigation_layers_count(); layer_index++) { + Ref<NavigationPolygon> navpoly = tile_data->get_navigation_polygon(layer_index); + if (navpoly.is_valid()) { + PackedVector2Array navigation_polygon_vertices = navpoly->get_vertices(); + + for (int i = 0; i < navpoly->get_polygon_count(); i++) { + // An array of vertices for this polygon. + Vector<int> polygon = navpoly->get_polygon(i); + Vector<Vector2> vertices; + vertices.resize(polygon.size()); + for (int j = 0; j < polygon.size(); j++) { + ERR_FAIL_INDEX(polygon[j], navigation_polygon_vertices.size()); + vertices.write[j] = navigation_polygon_vertices[polygon[j]]; + } + + // Generate the polygon color, slightly randomly modified from the settings one. + Color random_variation_color; + random_variation_color.set_hsv(color.get_h() + rand.random(-1.0, 1.0) * 0.05, color.get_s(), color.get_v() + rand.random(-1.0, 1.0) * 0.1); + random_variation_color.a = color.a; + Vector<Color> colors; + colors.push_back(random_variation_color); + + rs->canvas_item_add_polygon(p_quadrant->debug_canvas_item, vertices, colors); + } + } + } + } + } + } +} + +/////////////////////////////// Scenes ////////////////////////////////////// + +void TileMap::_scenes_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) { + ERR_FAIL_COND(!tile_set.is_valid()); + + SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first(); + while (q_list_element) { + TileMapQuadrant &q = *q_list_element->self(); + + // Clear the scenes. + for (Map<Vector2i, String>::Element *E = q.scenes.front(); E; E = E->next()) { + Node *node = get_node(E->get()); + if (node) { + node->queue_delete(); + } + } + + q.scenes.clear(); + + // Recreate the scenes. + for (Set<Vector2i>::Element *E_cell = q.cells.front(); E_cell; E_cell = E_cell->next()) { + const TileMapCell &c = get_cell(q.layer, E_cell->get(), true); + + TileSetSource *source; + if (tile_set->has_source(c.source_id)) { + source = *tile_set->get_source(c.source_id); + + if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { + continue; + } + + TileSetScenesCollectionSource *scenes_collection_source = Object::cast_to<TileSetScenesCollectionSource>(source); + if (scenes_collection_source) { + Ref<PackedScene> packed_scene = scenes_collection_source->get_scene_tile_scene(c.alternative_tile); + if (packed_scene.is_valid()) { + Node *scene = packed_scene->instantiate(); + add_child(scene); + Control *scene_as_control = Object::cast_to<Control>(scene); + Node2D *scene_as_node2d = Object::cast_to<Node2D>(scene); + if (scene_as_control) { + scene_as_control->set_position(map_to_world(E_cell->get()) + scene_as_control->get_position()); + } else if (scene_as_node2d) { + Transform2D xform; + xform.set_origin(map_to_world(E_cell->get())); + scene_as_node2d->set_transform(xform * scene_as_node2d->get_transform()); + } + q.scenes[E_cell->get()] = scene->get_name(); + } + } + } + } + + q_list_element = q_list_element->next(); + } +} + +void TileMap::_scenes_cleanup_quadrant(TileMapQuadrant *p_quadrant) { + // Clear the scenes. + for (Map<Vector2i, String>::Element *E = p_quadrant->scenes.front(); E; E = E->next()) { + Node *node = get_node(E->get()); + if (node) { + node->queue_delete(); + } + } + + p_quadrant->scenes.clear(); +} + +void TileMap::_scenes_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { + ERR_FAIL_COND(!tile_set.is_valid()); + + if (!Engine::get_singleton()->is_editor_hint()) { return; } - if (p_update) { - call_deferred(SNAME("update_dirty_quadrants")); + // Draw a placeholder for scenes needing one. + RenderingServer *rs = RenderingServer::get_singleton(); + Vector2 quadrant_pos = map_to_world(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer)); + for (Set<Vector2i>::Element *E_cell = p_quadrant->cells.front(); E_cell; E_cell = E_cell->next()) { + const TileMapCell &c = get_cell(p_quadrant->layer, E_cell->get(), true); + + TileSetSource *source; + if (tile_set->has_source(c.source_id)) { + source = *tile_set->get_source(c.source_id); + + if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { + continue; + } + + TileSetScenesCollectionSource *scenes_collection_source = Object::cast_to<TileSetScenesCollectionSource>(source); + if (scenes_collection_source) { + if (!scenes_collection_source->get_scene_tile_scene(c.alternative_tile).is_valid() || scenes_collection_source->get_scene_tile_display_placeholder(c.alternative_tile)) { + // Generate a random color from the hashed values of the tiles. + Array to_hash; + to_hash.push_back(c.source_id); + to_hash.push_back(c.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 a placeholder tile. + Transform2D xform; + xform.set_origin(map_to_world(E_cell->get()) - quadrant_pos); + rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform); + rs->canvas_item_add_circle(p_quadrant->debug_canvas_item, Vector2(), MIN(tile_set->get_tile_size().x, tile_set->get_tile_size().y) / 4.0, color); + } + } + } } } -void TileMap::set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i p_atlas_coords, int p_alternative_tile) { +void TileMap::set_cell(int p_layer, const Vector2i &p_coords, int p_source_id, const Vector2i p_atlas_coords, int p_alternative_tile) { + ERR_FAIL_INDEX(p_layer, (int)layers.size()); + // Set the current cell tile (using integer position). + Map<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map; Vector2i pk(p_coords); Map<Vector2i, TileMapCell>::Element *E = tile_map.find(pk); @@ -526,9 +1555,9 @@ void TileMap::set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i } // Get the quadrant - Vector2i qk = _coords_to_quadrant_coords(pk); + Vector2i qk = _coords_to_quadrant_coords(p_layer, pk); - Map<Vector2i, TileMapQuadrant>::Element *Q = quadrant_map.find(qk); + Map<Vector2i, TileMapQuadrant>::Element *Q = layers[p_layer].quadrant_map.find(qk); if (source_id == TileSet::INVALID_SOURCE) { // Erase existing cell in the tile map. @@ -555,7 +1584,7 @@ void TileMap::set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i // Create a new quadrant if needed, then insert the cell if needed. if (!Q) { - Q = _create_quadrant(qk); + Q = _create_quadrant(p_layer, qk); } TileMapQuadrant &q = Q->get(); q.cells.insert(pk); @@ -579,8 +1608,11 @@ void TileMap::set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i } } -int TileMap::get_cell_source_id(const Vector2i &p_coords, bool p_use_proxies) const { +int TileMap::get_cell_source_id(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const { + ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), TileSet::INVALID_SOURCE); + // Get a cell source id from position + const Map<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map; const Map<Vector2i, TileMapCell>::Element *E = tile_map.find(p_coords); if (!E) { @@ -595,8 +1627,11 @@ int TileMap::get_cell_source_id(const Vector2i &p_coords, bool p_use_proxies) co return E->get().source_id; } -Vector2i TileMap::get_cell_atlas_coords(const Vector2i &p_coords, bool p_use_proxies) const { +Vector2i TileMap::get_cell_atlas_coords(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const { + ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), TileSetSource::INVALID_ATLAS_COORDS); + // Get a cell source id from position + const Map<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map; const Map<Vector2i, TileMapCell>::Element *E = tile_map.find(p_coords); if (!E) { @@ -611,8 +1646,11 @@ Vector2i TileMap::get_cell_atlas_coords(const Vector2i &p_coords, bool p_use_pro return E->get().get_atlas_coords(); } -int TileMap::get_cell_alternative_tile(const Vector2i &p_coords, bool p_use_proxies) const { +int TileMap::get_cell_alternative_tile(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const { + ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), TileSetSource::INVALID_TILE_ALTERNATIVE); + // Get a cell source id from position + const Map<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map; const Map<Vector2i, TileMapCell>::Element *E = tile_map.find(p_coords); if (!E) { @@ -627,7 +1665,8 @@ int TileMap::get_cell_alternative_tile(const Vector2i &p_coords, bool p_use_prox return E->get().alternative_tile; } -TileMapPattern *TileMap::get_pattern(TypedArray<Vector2i> p_coords_array) { +TileMapPattern *TileMap::get_pattern(int p_layer, TypedArray<Vector2i> p_coords_array) { + ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), nullptr); ERR_FAIL_COND_V(!tile_set.is_valid(), nullptr); TileMapPattern *output = memnew(TileMapPattern); @@ -673,7 +1712,7 @@ TileMapPattern *TileMap::get_pattern(TypedArray<Vector2i> p_coords_array) { for (int i = 0; i < coords_in_pattern_array.size(); i++) { Vector2i coords = p_coords_array[i]; Vector2i coords_in_pattern = coords_in_pattern_array[i]; - output->set_cell(coords_in_pattern + ensure_positive_offset, get_cell_source_id(coords), get_cell_atlas_coords(coords), get_cell_alternative_tile(coords)); + output->set_cell(coords_in_pattern + ensure_positive_offset, get_cell_source_id(p_layer, coords), get_cell_atlas_coords(p_layer, coords), get_cell_alternative_tile(p_layer, coords)); } return output; @@ -702,17 +1741,20 @@ Vector2i TileMap::map_pattern(Vector2i p_position_in_tilemap, Vector2i p_coords_ return output; } -void TileMap::set_pattern(Vector2i p_position, const TileMapPattern *p_pattern) { +void TileMap::set_pattern(int p_layer, Vector2i p_position, const TileMapPattern *p_pattern) { + ERR_FAIL_INDEX(p_layer, (int)layers.size()); ERR_FAIL_COND(!tile_set.is_valid()); TypedArray<Vector2i> used_cells = p_pattern->get_used_cells(); for (int i = 0; i < used_cells.size(); i++) { Vector2i coords = map_pattern(p_position, used_cells[i], p_pattern); - set_cell(coords, p_pattern->get_cell_source_id(coords), p_pattern->get_cell_atlas_coords(coords), p_pattern->get_cell_alternative_tile(coords)); + set_cell(p_layer, coords, p_pattern->get_cell_source_id(coords), p_pattern->get_cell_atlas_coords(coords), p_pattern->get_cell_alternative_tile(coords)); } } -TileMapCell TileMap::get_cell(const Vector2i &p_coords, bool p_use_proxies) const { +TileMapCell TileMap::get_cell(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const { + ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), TileMapCell()); + const Map<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map; if (!tile_map.has(p_coords)) { return TileMapCell(); } else { @@ -727,77 +1769,62 @@ TileMapCell TileMap::get_cell(const Vector2i &p_coords, bool p_use_proxies) cons } } -Map<Vector2i, TileMapQuadrant> &TileMap::get_quadrant_map() { - return quadrant_map; +Map<Vector2i, TileMapQuadrant> *TileMap::get_quadrant_map(int p_layer) { + ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), nullptr); + + return &layers[p_layer].quadrant_map; } void TileMap::fix_invalid_tiles() { ERR_FAIL_COND_MSG(tile_set.is_null(), "Cannot fix invalid tiles if Tileset is not open."); - Set<Vector2i> coords; - for (Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) { - TileSetSource *source = *tile_set->get_source(E->get().source_id); - if (!source || !source->has_tile(E->get().get_atlas_coords()) || !source->has_alternative_tile(E->get().get_atlas_coords(), E->get().alternative_tile)) { - coords.insert(E->key()); + for (unsigned int i = 0; i < layers.size(); i++) { + const Map<Vector2i, TileMapCell> &tile_map = layers[i].tile_map; + Set<Vector2i> coords; + for (Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) { + TileSetSource *source = *tile_set->get_source(E->get().source_id); + if (!source || !source->has_tile(E->get().get_atlas_coords()) || !source->has_alternative_tile(E->get().get_atlas_coords(), E->get().alternative_tile)) { + coords.insert(E->key()); + } } - } - for (Set<Vector2i>::Element *E = coords.front(); E; E = E->next()) { - set_cell(E->get(), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); - } -} - -void TileMap::_recreate_quadrants() { - // Clear then recreate all quadrants. - _clear_quadrants(); - - for (Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) { - Vector2i qk = _coords_to_quadrant_coords(Vector2i(E->key().x, E->key().y)); - - Map<Vector2i, TileMapQuadrant>::Element *Q = quadrant_map.find(qk); - if (!Q) { - Q = _create_quadrant(qk); - dirty_quadrant_list.add(&Q->get().dirty_list_element); + for (Set<Vector2i>::Element *E = coords.front(); E; E = E->next()) { + set_cell(i, E->get(), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); } - - Vector2i pk = E->key(); - Q->get().cells.insert(pk); - - _make_quadrant_dirty(Q, false); } - - update_dirty_quadrants(); } -void TileMap::_clear_quadrants() { - // Clear quadrants. - while (quadrant_map.size()) { - _erase_quadrant(quadrant_map.front()); - } +void TileMap::clear_layer(int p_layer) { + ERR_FAIL_INDEX(p_layer, (int)layers.size()); - // Clear the dirty quadrants list. - while (dirty_quadrant_list.first()) { - dirty_quadrant_list.remove(dirty_quadrant_list.first()); - } + // Remove all tiles. + _clear_layer_internals(p_layer); + layers[p_layer].tile_map.clear(); + + used_size_cache_dirty = true; } void TileMap::clear() { // Remove all tiles. - _clear_quadrants(); - tile_map.clear(); + _clear_internals(); + for (unsigned int i = 0; i < layers.size(); i++) { + layers[i].tile_map.clear(); + } used_size_cache_dirty = true; } -void TileMap::_set_tile_data(const Vector<int> &p_data) { - // Set data for a given tile from raw data. +void TileMap::_set_tile_data(int p_layer, const Vector<int> &p_data) { + ERR_FAIL_INDEX(p_layer, (int)layers.size()); ERR_FAIL_COND(format > FORMAT_3); + // Set data for a given tile from raw data. + int c = p_data.size(); const int *r = p_data.ptr(); int offset = (format >= FORMAT_2) ? 3 : 2; ERR_FAIL_COND_MSG(c % offset != 0, "Corrupted tile data."); - clear(); + clear_layer(p_layer); #ifdef DISABLE_DEPRECATED ERR_FAIL_COND_MSG(format != FORMAT_3, vformat("Cannot handle deprecated TileMap data format version %d. This Godot version was compiled with no support for deprecated data.", format)); @@ -831,7 +1858,7 @@ void TileMap::_set_tile_data(const Vector<int> &p_data) { uint16_t atlas_coords_x = decode_uint16(&local[6]); 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); + set_cell(p_layer, Vector2i(x, y), source_id, Vector2i(atlas_coords_x, atlas_coords_y), alternative_tile); } else { #ifndef DISABLE_DEPRECATED // Previous decated format. @@ -854,21 +1881,25 @@ void TileMap::_set_tile_data(const Vector<int> &p_data) { if (tile_set.is_valid()) { 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]); + set_cell(p_layer, 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(p_layer, Vector2i(x, y), v, Vector2i(coord_x, coord_y), compatibility_alternative_tile); } #endif } } + emit_signal(SNAME("changed")); } -Vector<int> TileMap::_get_tile_data() const { +Vector<int> TileMap::_get_tile_data(int p_layer) const { + ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), Vector<int>()); + // Export tile data to raw format + const Map<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map; Vector<int> data; data.resize(tile_map.size() * 3); int *w = data.ptrw(); @@ -894,7 +1925,7 @@ Vector<int> TileMap::_get_tile_data() const { Rect2 TileMap::_edit_get_rect() const { // Return the visible rect of the tilemap if (pending_update) { - const_cast<TileMap *>(this)->update_dirty_quadrants(); + const_cast<TileMap *>(this)->_update_dirty_quadrants(); } else { const_cast<TileMap *>(this)->_recompute_rect_cache(); } @@ -903,38 +1934,99 @@ Rect2 TileMap::_edit_get_rect() const { #endif bool TileMap::_set(const StringName &p_name, const Variant &p_value) { + Vector<String> components = String(p_name).split("/", true, 2); if (p_name == "format") { if (p_value.get_type() == Variant::INT) { format = (DataFormat)(p_value.operator int64_t()); // Set format used for loading return true; } - } else if (p_name == "tile_data") { + } else if (p_name == "tile_data") { // Kept for compatibility reasons. if (p_value.is_array()) { - _set_tile_data(p_value); + if (layers.size() < 1) { + layers.resize(1); + } + _set_tile_data(0, p_value); return true; } return false; + } else if (components.size() == 2 && components[0].begins_with("layer_") && components[0].trim_prefix("layer_").is_valid_int()) { + int index = components[0].trim_prefix("layer_").to_int(); + if (index < 0 || index >= (int)layers.size()) { + return false; + } + + if (components[1] == "name") { + set_layer_name(index, p_value); + return true; + } else if (components[1] == "enabled") { + set_layer_enabled(index, p_value); + return true; + } else if (components[1] == "y_sort_enabled") { + set_layer_y_sort_enabled(index, p_value); + return true; + } else if (components[1] == "y_sort_origin") { + set_layer_y_sort_origin(index, p_value); + return true; + } else if (components[1] == "z_index") { + set_layer_z_index(index, p_value); + return true; + } else if (components[1] == "tile_data") { + _set_tile_data(index, p_value); + return true; + } else { + return false; + } } return false; } bool TileMap::_get(const StringName &p_name, Variant &r_ret) const { + Vector<String> components = String(p_name).split("/", true, 2); if (p_name == "format") { r_ret = FORMAT_3; // When saving, always save highest format return true; - } else if (p_name == "tile_data") { - r_ret = _get_tile_data(); - return true; + } else if (components.size() == 2 && components[0].begins_with("layer_") && components[0].trim_prefix("layer_").is_valid_int()) { + int index = components[0].trim_prefix("layer_").to_int(); + if (index < 0 || index >= (int)layers.size()) { + return false; + } + + if (components[1] == "name") { + r_ret = get_layer_name(index); + return true; + } else if (components[1] == "enabled") { + r_ret = is_layer_enabled(index); + return true; + } else if (components[1] == "y_sort_enabled") { + r_ret = is_layer_y_sort_enabled(index); + return true; + } else if (components[1] == "y_sort_origin") { + r_ret = get_layer_y_sort_origin(index); + return true; + } else if (components[1] == "z_index") { + r_ret = get_layer_z_index(index); + return true; + } else if (components[1] == "tile_data") { + r_ret = _get_tile_data(index); + return true; + } else { + return false; + } } return false; } void TileMap::_get_property_list(List<PropertyInfo> *p_list) const { - PropertyInfo p(Variant::INT, "format", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL); - p_list->push_back(p); - - p = PropertyInfo(Variant::OBJECT, "tile_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL); - p_list->push_back(p); + p_list->push_back(PropertyInfo(Variant::INT, "format", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(Variant::NIL, "Layers", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); + for (unsigned int i = 0; i < layers.size(); i++) { + p_list->push_back(PropertyInfo(Variant::STRING, vformat("layer_%d/name", i), PROPERTY_HINT_NONE)); + p_list->push_back(PropertyInfo(Variant::BOOL, vformat("layer_%d/enabled", i), PROPERTY_HINT_NONE)); + p_list->push_back(PropertyInfo(Variant::BOOL, vformat("layer_%d/y_sort_enabled", i), PROPERTY_HINT_NONE)); + p_list->push_back(PropertyInfo(Variant::INT, vformat("layer_%d/y_sort_origin", i), PROPERTY_HINT_NONE)); + p_list->push_back(PropertyInfo(Variant::INT, vformat("layer_%d/z_index", i), PROPERTY_HINT_NONE)); + p_list->push_back(PropertyInfo(Variant::OBJECT, vformat("layer_%d/tile_data", i), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + } } Vector2 TileMap::map_to_world(const Vector2i &p_pos) const { @@ -1557,12 +2649,14 @@ Vector2i TileMap::get_neighbor_cell(const Vector2i &p_coords, TileSet::CellNeigh } } -TypedArray<Vector2i> TileMap::get_used_cells() const { +TypedArray<Vector2i> TileMap::get_used_cells(int p_layer) const { + ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), TypedArray<Vector2i>()); + // Returns the cells used in the tilemap. TypedArray<Vector2i> a; - a.resize(tile_map.size()); + a.resize(layers[p_layer].tile_map.size()); int i = 0; - for (Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) { + for (Map<Vector2i, TileMapCell>::Element *E = layers[p_layer].tile_map.front(); E; E = E->next()) { Vector2i p(E->key().x, E->key().y); a[i++] = p; } @@ -1573,18 +2667,20 @@ TypedArray<Vector2i> TileMap::get_used_cells() const { Rect2 TileMap::get_used_rect() { // Not const because of cache // Return the rect of the currently used area if (used_size_cache_dirty) { - if (tile_map.size() > 0) { - used_size_cache = Rect2(tile_map.front()->key().x, tile_map.front()->key().y, 0, 0); + bool first = true; + for (unsigned int i = 0; i < layers.size(); i++) { + const Map<Vector2i, TileMapCell> &tile_map = layers[i].tile_map; + if (tile_map.size() > 0) { + if (first) { + used_size_cache = Rect2(tile_map.front()->key().x, tile_map.front()->key().y, 1, 1); + first = false; + } - for (Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) { - used_size_cache.expand_to(Vector2(E->key().x, E->key().y)); + for (Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) { + used_size_cache.expand_to(Vector2(E->key().x + 1, E->key().y + 1)); + } } - - used_size_cache.size += Vector2(1, 1); - } else { - used_size_cache = Rect2(); } - used_size_cache_dirty = false; } @@ -1596,10 +2692,13 @@ Rect2 TileMap::get_used_rect() { // Not const because of cache void TileMap::set_light_mask(int p_light_mask) { // Occlusion: set light mask. CanvasItem::set_light_mask(p_light_mask); - for (Map<Vector2i, TileMapQuadrant>::Element *E = quadrant_map.front(); E; E = E->next()) { - for (const RID &F : E->get().canvas_items) { - RenderingServer::get_singleton()->canvas_item_set_light_mask(F, get_light_mask()); + for (unsigned int layer = 0; layer < layers.size(); layer++) { + for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) { + for (const RID &ci : E->get().canvas_items) { + RenderingServer::get_singleton()->canvas_item_set_light_mask(ci, get_light_mask()); + } } + _rendering_update_layer(layer); } } @@ -1608,11 +2707,14 @@ void TileMap::set_material(const Ref<Material> &p_material) { CanvasItem::set_material(p_material); // Update material for the whole tilemap. - for (Map<Vector2i, TileMapQuadrant>::Element *E = quadrant_map.front(); E; E = E->next()) { - TileMapQuadrant &q = E->get(); - for (const RID &F : q.canvas_items) { - RS::get_singleton()->canvas_item_set_use_parent_material(F, get_use_parent_material() || get_material().is_valid()); + for (unsigned int layer = 0; layer < layers.size(); layer++) { + for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) { + TileMapQuadrant &q = E->get(); + for (const RID &ci : q.canvas_items) { + RS::get_singleton()->canvas_item_set_use_parent_material(ci, get_use_parent_material() || get_material().is_valid()); + } } + _rendering_update_layer(layer); } } @@ -1621,35 +2723,44 @@ void TileMap::set_use_parent_material(bool p_use_parent_material) { CanvasItem::set_use_parent_material(p_use_parent_material); // Update use_parent_material for the whole tilemap. - for (Map<Vector2i, TileMapQuadrant>::Element *E = quadrant_map.front(); E; E = E->next()) { - TileMapQuadrant &q = E->get(); - for (const RID &F : q.canvas_items) { - RS::get_singleton()->canvas_item_set_use_parent_material(F, get_use_parent_material() || get_material().is_valid()); + for (unsigned int layer = 0; layer < layers.size(); layer++) { + for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) { + TileMapQuadrant &q = E->get(); + for (const RID &ci : q.canvas_items) { + RS::get_singleton()->canvas_item_set_use_parent_material(ci, get_use_parent_material() || get_material().is_valid()); + } } + _rendering_update_layer(layer); } } void TileMap::set_texture_filter(TextureFilter p_texture_filter) { // Set a default texture filter for the whole tilemap CanvasItem::set_texture_filter(p_texture_filter); - for (Map<Vector2i, TileMapQuadrant>::Element *F = quadrant_map.front(); F; F = F->next()) { - TileMapQuadrant &q = F->get(); - for (const RID &E : q.canvas_items) { - RenderingServer::get_singleton()->canvas_item_set_default_texture_filter(E, RS::CanvasItemTextureFilter(p_texture_filter)); - _make_quadrant_dirty(F); + for (unsigned int layer = 0; layer < layers.size(); layer++) { + for (Map<Vector2i, TileMapQuadrant>::Element *F = layers[layer].quadrant_map.front(); F; F = F->next()) { + TileMapQuadrant &q = F->get(); + for (const RID &ci : q.canvas_items) { + RenderingServer::get_singleton()->canvas_item_set_default_texture_filter(ci, RS::CanvasItemTextureFilter(p_texture_filter)); + _make_quadrant_dirty(F); + } } + _rendering_update_layer(layer); } } void TileMap::set_texture_repeat(CanvasItem::TextureRepeat p_texture_repeat) { // Set a default texture repeat for the whole tilemap CanvasItem::set_texture_repeat(p_texture_repeat); - for (Map<Vector2i, TileMapQuadrant>::Element *F = quadrant_map.front(); F; F = F->next()) { - TileMapQuadrant &q = F->get(); - for (const RID &E : q.canvas_items) { - RenderingServer::get_singleton()->canvas_item_set_default_texture_repeat(E, RS::CanvasItemTextureRepeat(p_texture_repeat)); - _make_quadrant_dirty(F); + for (unsigned int layer = 0; layer < layers.size(); layer++) { + for (Map<Vector2i, TileMapQuadrant>::Element *F = layers[layer].quadrant_map.front(); F; F = F->next()) { + TileMapQuadrant &q = F->get(); + for (const RID &ci : q.canvas_items) { + RenderingServer::get_singleton()->canvas_item_set_default_texture_repeat(ci, RS::CanvasItemTextureRepeat(p_texture_repeat)); + _make_quadrant_dirty(F); + } } + _rendering_update_layer(layer); } } @@ -1744,6 +2855,28 @@ void TileMap::draw_cells_outline(Control *p_control, Set<Vector2i> p_cells, Colo } } +TypedArray<String> TileMap::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); + + // Retrieve the set of Z index values with a Y-sorted layer. + Set<int> y_sorted_z_index; + for (int layer = 0; layer < (int)layers.size(); layer++) { + if (layers[layer].y_sort_enabled) { + y_sorted_z_index.insert(layers[layer].z_index); + } + } + + // Check if we have a non-sorted layer in a Z-index with a Y-sorted layer. + for (int layer = 0; layer < (int)layers.size(); layer++) { + if (!layers[layer].y_sort_enabled && y_sorted_z_index.has(layers[layer].z_index)) { + warnings.push_back(TTR("A Y-sorted layer has the same Z-index value as a not Y-sorted layer.\nThis may lead to unwanted behaviors, as a layer that is not Y-sorted will be Y-sorted as a whole with tiles from Y-sorted layers.")); + break; + } + } + + return warnings; +} + void TileMap::_bind_methods() { ClassDB::bind_method(D_METHOD("set_tileset", "tileset"), &TileMap::set_tileset); ClassDB::bind_method(D_METHOD("get_tileset"), &TileMap::get_tileset); @@ -1751,22 +2884,35 @@ void TileMap::_bind_methods() { ClassDB::bind_method(D_METHOD("set_quadrant_size", "size"), &TileMap::set_quadrant_size); ClassDB::bind_method(D_METHOD("get_quadrant_size"), &TileMap::get_quadrant_size); - ClassDB::bind_method(D_METHOD("set_collision_visibility_mode", "show_collision"), &TileMap::set_collision_visibility_mode); + ClassDB::bind_method(D_METHOD("set_layers_count", "layers_count"), &TileMap::set_layers_count); + ClassDB::bind_method(D_METHOD("get_layers_count"), &TileMap::get_layers_count); + ClassDB::bind_method(D_METHOD("set_layer_name", "layer", "name"), &TileMap::set_layer_name); + ClassDB::bind_method(D_METHOD("get_layer_name", "layer"), &TileMap::get_layer_name); + ClassDB::bind_method(D_METHOD("set_layer_enabled", "layer", "enabled"), &TileMap::set_layer_enabled); + ClassDB::bind_method(D_METHOD("is_layer_enabled", "layer"), &TileMap::is_layer_enabled); + ClassDB::bind_method(D_METHOD("set_layer_y_sort_enabled", "layer", "y_sort_enabled"), &TileMap::set_layer_y_sort_enabled); + ClassDB::bind_method(D_METHOD("is_layer_y_sort_enabled", "layer"), &TileMap::is_layer_y_sort_enabled); + ClassDB::bind_method(D_METHOD("set_layer_y_sort_origin", "layer", "y_sort_origin"), &TileMap::set_layer_y_sort_origin); + ClassDB::bind_method(D_METHOD("get_layer_y_sort_origin", "layer"), &TileMap::get_layer_y_sort_origin); + ClassDB::bind_method(D_METHOD("set_layer_z_index", "layer", "z_index"), &TileMap::set_layer_z_index); + ClassDB::bind_method(D_METHOD("get_layer_z_indexd", "layer"), &TileMap::get_layer_z_index); + + ClassDB::bind_method(D_METHOD("set_collision_visibility_mode", "collision_visibility_mode"), &TileMap::set_collision_visibility_mode); ClassDB::bind_method(D_METHOD("get_collision_visibility_mode"), &TileMap::get_collision_visibility_mode); - ClassDB::bind_method(D_METHOD("set_navigation_visibility_mode", "show_navigation"), &TileMap::set_navigation_visibility_mode); + ClassDB::bind_method(D_METHOD("set_navigation_visibility_mode", "navigation_visibility_mode"), &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(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("set_cell", "layer", "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", "layer", "coords", "use_proxies"), &TileMap::get_cell_source_id); + ClassDB::bind_method(D_METHOD("get_cell_atlas_coords", "layer", "coords", "use_proxies"), &TileMap::get_cell_atlas_coords); + ClassDB::bind_method(D_METHOD("get_cell_alternative_tile", "layer", "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); ClassDB::bind_method(D_METHOD("clear"), &TileMap::clear); - ClassDB::bind_method(D_METHOD("get_used_cells"), &TileMap::get_used_cells); + ClassDB::bind_method(D_METHOD("get_used_cells", "layer"), &TileMap::get_used_cells); ClassDB::bind_method(D_METHOD("get_used_rect"), &TileMap::get_used_rect); ClassDB::bind_method(D_METHOD("map_to_world", "map_position"), &TileMap::map_to_world); @@ -1774,15 +2920,19 @@ void TileMap::_bind_methods() { ClassDB::bind_method(D_METHOD("get_neighbor_cell", "coords", "neighbor"), &TileMap::get_neighbor_cell); - ClassDB::bind_method(D_METHOD("update_dirty_quadrants"), &TileMap::update_dirty_quadrants); + ClassDB::bind_method(D_METHOD("_update_dirty_quadrants"), &TileMap::_update_dirty_quadrants); - ClassDB::bind_method(D_METHOD("_set_tile_data"), &TileMap::_set_tile_data); - ClassDB::bind_method(D_METHOD("_get_tile_data"), &TileMap::_get_tile_data); + ClassDB::bind_method(D_METHOD("_set_tile_data", "layer"), &TileMap::_set_tile_data); + ClassDB::bind_method(D_METHOD("_get_tile_data", "layer"), &TileMap::_get_tile_data); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tile_set", PROPERTY_HINT_RESOURCE_TYPE, "TileSet"), "set_tileset", "get_tileset"); ADD_PROPERTY(PropertyInfo(Variant::INT, "cell_quadrant_size", PROPERTY_HINT_RANGE, "1,128,1"), "set_quadrant_size", "get_quadrant_size"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "show_collision", PROPERTY_HINT_ENUM, "Default,Force Show,Force Hide"), "set_collision_visibility_mode", "get_collision_visibility_mode"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "show_navigation", PROPERTY_HINT_ENUM, "Default,Force Show,Force Hide"), "set_navigation_visibility_mode", "get_navigation_visibility_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_visibility_mode", PROPERTY_HINT_ENUM, "Default,Force Show,Force Hide"), "set_collision_visibility_mode", "get_collision_visibility_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_visibility_mode", PROPERTY_HINT_ENUM, "Default,Force Show,Force Hide"), "set_navigation_visibility_mode", "get_navigation_visibility_mode"); + + ADD_GROUP("Layers", ""); + ADD_PROPERTY(PropertyInfo(Variant::INT, "layers_count"), "set_layers_count", "get_layers_count"); + ADD_PROPERTY_DEFAULT("layers_count", 1); ADD_PROPERTY_DEFAULT("format", FORMAT_1); @@ -1795,17 +2945,19 @@ void TileMap::_bind_methods() { void TileMap::_tile_set_changed() { emit_signal(SNAME("changed")); - _make_all_quadrants_dirty(true); + _recreate_internals(); } TileMap::TileMap() { set_notify_transform(true); set_notify_local_transform(false); + + layers.resize(1); } TileMap::~TileMap() { if (tile_set.is_valid()) { tile_set->disconnect("changed", callable_mp(this, &TileMap::_tile_set_changed)); } - _clear_quadrants(); + _clear_internals(); } diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h index 9e35e73ad8..4800780f94 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -99,7 +99,8 @@ struct TileMapQuadrant { // Dirty list element SelfList<TileMapQuadrant> dirty_list_element; - // Quadrant coords. + // Quadrant layer and coords. + int layer = -1; Vector2i coords; // TileMapCells @@ -126,6 +127,7 @@ struct TileMapQuadrant { Map<Vector2i, String> scenes; void operator=(const TileMapQuadrant &q) { + layer = q.layer; coords = q.coords; debug_canvas_item = q.debug_canvas_item; canvas_items = q.canvas_items; @@ -136,6 +138,7 @@ struct TileMapQuadrant { TileMapQuadrant(const TileMapQuadrant &q) : dirty_list_element(this) { + layer = q.layer; coords = q.coords; debug_canvas_item = q.debug_canvas_item; canvas_items = q.canvas_items; @@ -196,11 +199,13 @@ private: }; mutable DataFormat format = FORMAT_1; // Assume lowest possible format if none is present; + static constexpr float FP_ADJUST = 0.00001; + // Properties. Ref<TileSet> tile_set; int quadrant_size = 16; - VisibilityMode show_collision = VISIBILITY_MODE_DEFAULT; - VisibilityMode show_navigation = VISIBILITY_MODE_DEFAULT; + VisibilityMode collision_visibility_mode = VISIBILITY_MODE_DEFAULT; + VisibilityMode navigation_visibility_mode = VISIBILITY_MODE_DEFAULT; // Updates. bool pending_update = false; @@ -211,25 +216,69 @@ private: Rect2 used_size_cache; bool used_size_cache_dirty = true; - // Map of cells. - Map<Vector2i, TileMapCell> tile_map; + // TileMap layers. + struct TileMapLayer { + String name; + bool enabled = true; + bool y_sort_enabled = false; + int y_sort_origin = 0; + int z_index = 0; + RID canvas_item; + Map<Vector2i, TileMapCell> tile_map; + Map<Vector2i, TileMapQuadrant> quadrant_map; + SelfList<TileMapQuadrant>::List dirty_quadrant_list; + }; + LocalVector<TileMapLayer> layers; + int selected_layer = -1; + + // Quadrants and internals management. + Vector2i _coords_to_quadrant_coords(int p_layer, const Vector2i &p_coords) const; + + Map<Vector2i, TileMapQuadrant>::Element *_create_quadrant(int p_layer, const Vector2i &p_qk); + + void _make_quadrant_dirty(Map<Vector2i, TileMapQuadrant>::Element *Q); + void _make_all_quadrants_dirty(); + void _queue_update_dirty_quadrants(); + + void _update_dirty_quadrants(); - // Quadrants management. - Map<Vector2i, TileMapQuadrant> quadrant_map; - Vector2i _coords_to_quadrant_coords(const Vector2i &p_coords) const; - SelfList<TileMapQuadrant>::List dirty_quadrant_list; + void _recreate_internals(); - Map<Vector2i, TileMapQuadrant>::Element *_create_quadrant(const Vector2i &p_qk); void _erase_quadrant(Map<Vector2i, TileMapQuadrant>::Element *Q); - void _make_all_quadrants_dirty(bool p_update = true); - void _make_quadrant_dirty(Map<Vector2i, TileMapQuadrant>::Element *Q, bool p_update = true); - void _recreate_quadrants(); - void _clear_quadrants(); + void _clear_layer_internals(int p_layer); + void _clear_internals(); + + // Rect caching. void _recompute_rect_cache(); + // Per-system methods. + bool _rendering_quadrant_order_dirty = false; + void _rendering_notification(int p_what); + void _rendering_update_layer(int p_layer); + void _rendering_cleanup_layer(int p_layer); + void _rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list); + void _rendering_create_quadrant(TileMapQuadrant *p_quadrant); + void _rendering_cleanup_quadrant(TileMapQuadrant *p_quadrant); + void _rendering_draw_quadrant_debug(TileMapQuadrant *p_quadrant); + + void _physics_notification(int p_what); + void _physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list); + void _physics_create_quadrant(TileMapQuadrant *p_quadrant); + void _physics_cleanup_quadrant(TileMapQuadrant *p_quadrant); + void _physics_draw_quadrant_debug(TileMapQuadrant *p_quadrant); + + void _navigation_notification(int p_what); + void _navigation_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list); + void _navigation_cleanup_quadrant(TileMapQuadrant *p_quadrant); + void _navigation_draw_quadrant_debug(TileMapQuadrant *p_quadrant); + + void _scenes_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list); + void _scenes_cleanup_quadrant(TileMapQuadrant *p_quadrant); + void _scenes_draw_quadrant_debug(TileMapQuadrant *p_quadrant); + // Set and get tiles from data arrays. - void _set_tile_data(const Vector<int> &p_data); - Vector<int> _get_tile_data() const; + void _set_tile_data(int p_layer, const Vector<int> &p_data); + Vector<int> _get_tile_data(int p_layer) const; void _tile_set_changed(); @@ -258,27 +307,45 @@ public: void set_quadrant_size(int p_size); int get_quadrant_size() const; + static void draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, Color p_modulation = Color(1.0, 1.0, 1.0, 1.0)); + + // Layers management. + void set_layers_count(int p_layers_count); + int get_layers_count() const; + void set_layer_name(int p_layer, String p_name); + String get_layer_name(int p_layer) const; + void set_layer_enabled(int p_layer, bool p_visible); + bool is_layer_enabled(int p_layer) const; + void set_layer_y_sort_enabled(int p_layer, bool p_enabled); + bool is_layer_y_sort_enabled(int p_layer) const; + void set_layer_y_sort_origin(int p_layer, int p_y_sort_origin); + int get_layer_y_sort_origin(int p_layer) const; + void set_layer_z_index(int p_layer, int p_z_index); + int get_layer_z_index(int p_layer) const; + void set_selected_layer(int p_layer_id); // For editor use. + int get_selected_layer() const; + void set_collision_visibility_mode(VisibilityMode p_show_collision); VisibilityMode get_collision_visibility_mode(); void set_navigation_visibility_mode(VisibilityMode p_show_navigation); 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, 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; + void set_cell(int p_layer, 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(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const; + Vector2i get_cell_atlas_coords(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const; + int get_cell_alternative_tile(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const; - TileMapPattern *get_pattern(TypedArray<Vector2i> p_coords_array); + TileMapPattern *get_pattern(int p_layer, 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); + void set_pattern(int p_layer, Vector2i p_position, const TileMapPattern *p_pattern); // Not exposed to users - 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; + TileMapCell get_cell(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const; + Map<Vector2i, TileMapQuadrant> *get_quadrant_map(int p_layer); + int get_effective_quadrant_size(int p_layer) const; + //--- - void update_dirty_quadrants(); virtual void set_y_sort_enabled(bool p_enable) override; Vector2 map_to_world(const Vector2i &p_pos) const; @@ -287,7 +354,7 @@ public: bool is_existing_neighbor(TileSet::CellNeighbor p_cell_neighbor) const; Vector2i get_neighbor_cell(const Vector2i &p_coords, TileSet::CellNeighbor p_cell_neighbor) const; - TypedArray<Vector2i> get_used_cells() const; + TypedArray<Vector2i> get_used_cells(int p_layer) const; Rect2 get_used_rect(); // Not const because of cache // Override some methods of the CanvasItem class to pass the changes to the quadrants CanvasItems @@ -297,13 +364,19 @@ public: virtual void set_texture_filter(CanvasItem::TextureFilter p_texture_filter) override; virtual void set_texture_repeat(CanvasItem::TextureRepeat p_texture_repeat) override; + // Fixing a nclearing methods. void fix_invalid_tiles(); + + void clear_layer(int p_layer); void clear(); // Helpers TypedArray<Vector2i> get_surrounding_tiles(Vector2i coords); void draw_cells_outline(Control *p_control, Set<Vector2i> p_cells, Color p_color, Transform2D p_transform = Transform2D()); + // Configuration warnings. + TypedArray<String> get_configuration_warnings() const override; + TileMap(); ~TileMap(); }; diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp index a57e986877..542011618d 100644 --- a/scene/animation/tween.cpp +++ b/scene/animation/tween.cpp @@ -140,6 +140,8 @@ bool Tween::is_valid() { } Ref<Tween> Tween::bind_node(Node *p_node) { + ERR_FAIL_NULL_V(p_node, this); + bound_node = p_node->get_instance_id(); is_bound = true; return this; diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 81fcb96187..a2e6872da6 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -222,41 +222,42 @@ Transform2D Control::_get_internal_transform() const { bool Control::_set(const StringName &p_name, const Variant &p_value) { String name = p_name; - if (!name.begins_with("custom")) { + // Prefixes "custom_*" are supported for compatibility with 3.x. + if (!name.begins_with("theme_override") && !name.begins_with("custom")) { return false; } if (p_value.get_type() == Variant::NIL) { - if (name.begins_with("custom_icons/")) { + if (name.begins_with("theme_override_icons/") || name.begins_with("custom_icons/")) { String dname = name.get_slicec('/', 1); if (data.icon_override.has(dname)) { data.icon_override[dname]->disconnect("changed", callable_mp(this, &Control::_override_changed)); } data.icon_override.erase(dname); notification(NOTIFICATION_THEME_CHANGED); - } else if (name.begins_with("custom_styles/")) { + } else if (name.begins_with("theme_override_styles/") || name.begins_with("custom_styles/")) { String dname = name.get_slicec('/', 1); if (data.style_override.has(dname)) { data.style_override[dname]->disconnect("changed", callable_mp(this, &Control::_override_changed)); } data.style_override.erase(dname); notification(NOTIFICATION_THEME_CHANGED); - } else if (name.begins_with("custom_fonts/")) { + } else if (name.begins_with("theme_override_fonts/") || name.begins_with("custom_fonts/")) { String dname = name.get_slicec('/', 1); if (data.font_override.has(dname)) { data.font_override[dname]->disconnect("changed", callable_mp(this, &Control::_override_changed)); } data.font_override.erase(dname); notification(NOTIFICATION_THEME_CHANGED); - } else if (name.begins_with("custom_font_sizes/")) { + } else if (name.begins_with("theme_override_font_sizes/") || name.begins_with("custom_font_sizes/")) { String dname = name.get_slicec('/', 1); data.font_size_override.erase(dname); notification(NOTIFICATION_THEME_CHANGED); - } else if (name.begins_with("custom_colors/")) { + } else if (name.begins_with("theme_override_colors/") || name.begins_with("custom_colors/")) { String dname = name.get_slicec('/', 1); data.color_override.erase(dname); notification(NOTIFICATION_THEME_CHANGED); - } else if (name.begins_with("custom_constants/")) { + } else if (name.begins_with("theme_override_constants/") || name.begins_with("custom_constants/")) { String dname = name.get_slicec('/', 1); data.constant_override.erase(dname); notification(NOTIFICATION_THEME_CHANGED); @@ -265,22 +266,22 @@ bool Control::_set(const StringName &p_name, const Variant &p_value) { } } else { - if (name.begins_with("custom_icons/")) { + if (name.begins_with("theme_override_icons/") || name.begins_with("custom_icons/")) { String dname = name.get_slicec('/', 1); add_theme_icon_override(dname, p_value); - } else if (name.begins_with("custom_styles/")) { + } else if (name.begins_with("theme_override_styles/") || name.begins_with("custom_styles/")) { String dname = name.get_slicec('/', 1); add_theme_style_override(dname, p_value); - } else if (name.begins_with("custom_fonts/")) { + } else if (name.begins_with("theme_override_fonts/") || name.begins_with("custom_fonts/")) { String dname = name.get_slicec('/', 1); add_theme_font_override(dname, p_value); - } else if (name.begins_with("custom_font_sizes/")) { + } else if (name.begins_with("theme_override_font_sizes/") || name.begins_with("custom_font_sizes/")) { String dname = name.get_slicec('/', 1); add_theme_font_size_override(dname, p_value); - } else if (name.begins_with("custom_colors/")) { + } else if (name.begins_with("theme_override_colors/") || name.begins_with("custom_colors/")) { String dname = name.get_slicec('/', 1); add_theme_color_override(dname, p_value); - } else if (name.begins_with("custom_constants/")) { + } else if (name.begins_with("theme_override_constants/") || name.begins_with("custom_constants/")) { String dname = name.get_slicec('/', 1); add_theme_constant_override(dname, p_value); } else { @@ -307,27 +308,26 @@ void Control::_update_minimum_size() { bool Control::_get(const StringName &p_name, Variant &r_ret) const { String sname = p_name; - - if (!sname.begins_with("custom")) { + if (!sname.begins_with("theme_override")) { return false; } - if (sname.begins_with("custom_icons/")) { + if (sname.begins_with("theme_override_icons/")) { String name = sname.get_slicec('/', 1); r_ret = data.icon_override.has(name) ? Variant(data.icon_override[name]) : Variant(); - } else if (sname.begins_with("custom_styles/")) { + } else if (sname.begins_with("theme_override_styles/")) { String name = sname.get_slicec('/', 1); r_ret = data.style_override.has(name) ? Variant(data.style_override[name]) : Variant(); - } else if (sname.begins_with("custom_fonts/")) { + } else if (sname.begins_with("theme_override_fonts/")) { String name = sname.get_slicec('/', 1); r_ret = data.font_override.has(name) ? Variant(data.font_override[name]) : Variant(); - } else if (sname.begins_with("custom_font_sizes/")) { + } else if (sname.begins_with("theme_override_font_sizes/")) { String name = sname.get_slicec('/', 1); r_ret = data.font_size_override.has(name) ? Variant(data.font_size_override[name]) : Variant(); - } else if (sname.begins_with("custom_colors/")) { + } else if (sname.begins_with("theme_override_colors/")) { String name = sname.get_slicec('/', 1); r_ret = data.color_override.has(name) ? Variant(data.color_override[name]) : Variant(); - } else if (sname.begins_with("custom_constants/")) { + } else if (sname.begins_with("theme_override_constants/")) { String name = sname.get_slicec('/', 1); r_ret = data.constant_override.has(name) ? Variant(data.constant_override[name]) : Variant(); } else { @@ -340,28 +340,30 @@ bool Control::_get(const StringName &p_name, Variant &r_ret) const { void Control::_get_property_list(List<PropertyInfo> *p_list) const { Ref<Theme> theme = Theme::get_default(); + p_list->push_back(PropertyInfo(Variant::NIL, "Theme Overrides", PROPERTY_HINT_NONE, "theme_override_", PROPERTY_USAGE_GROUP)); + { List<StringName> names; - theme->get_icon_list(get_class_name(), &names); + theme->get_color_list(get_class_name(), &names); for (const StringName &E : names) { uint32_t usage = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CHECKABLE; - if (data.icon_override.has(E)) { + if (data.color_override.has(E)) { usage |= PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_CHECKED; } - p_list->push_back(PropertyInfo(Variant::OBJECT, "custom_icons/" + E, PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", usage)); + p_list->push_back(PropertyInfo(Variant::COLOR, "theme_override_colors/" + E, PROPERTY_HINT_NONE, "", usage)); } } { List<StringName> names; - theme->get_stylebox_list(get_class_name(), &names); + theme->get_constant_list(get_class_name(), &names); for (const StringName &E : names) { uint32_t usage = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CHECKABLE; - if (data.style_override.has(E)) { + if (data.constant_override.has(E)) { usage |= PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_CHECKED; } - p_list->push_back(PropertyInfo(Variant::OBJECT, "custom_styles/" + E, PROPERTY_HINT_RESOURCE_TYPE, "StyleBox", usage)); + p_list->push_back(PropertyInfo(Variant::INT, "theme_override_constants/" + E, PROPERTY_HINT_RANGE, "-16384,16384", usage)); } } { @@ -373,7 +375,7 @@ void Control::_get_property_list(List<PropertyInfo> *p_list) const { usage |= PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_CHECKED; } - p_list->push_back(PropertyInfo(Variant::OBJECT, "custom_fonts/" + E, PROPERTY_HINT_RESOURCE_TYPE, "Font", usage)); + p_list->push_back(PropertyInfo(Variant::OBJECT, "theme_override_fonts/" + E, PROPERTY_HINT_RESOURCE_TYPE, "Font", usage)); } } { @@ -385,31 +387,31 @@ void Control::_get_property_list(List<PropertyInfo> *p_list) const { usage |= PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_CHECKED; } - p_list->push_back(PropertyInfo(Variant::INT, "custom_font_sizes/" + E, PROPERTY_HINT_NONE, "", usage)); + p_list->push_back(PropertyInfo(Variant::INT, "theme_override_font_sizes/" + E, PROPERTY_HINT_NONE, "", usage)); } } { List<StringName> names; - theme->get_color_list(get_class_name(), &names); + theme->get_icon_list(get_class_name(), &names); for (const StringName &E : names) { uint32_t usage = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CHECKABLE; - if (data.color_override.has(E)) { + if (data.icon_override.has(E)) { usage |= PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_CHECKED; } - p_list->push_back(PropertyInfo(Variant::COLOR, "custom_colors/" + E, PROPERTY_HINT_NONE, "", usage)); + p_list->push_back(PropertyInfo(Variant::OBJECT, "theme_override_icons/" + E, PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", usage)); } } { List<StringName> names; - theme->get_constant_list(get_class_name(), &names); + theme->get_stylebox_list(get_class_name(), &names); for (const StringName &E : names) { uint32_t usage = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CHECKABLE; - if (data.constant_override.has(E)) { + if (data.style_override.has(E)) { usage |= PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_CHECKED; } - p_list->push_back(PropertyInfo(Variant::INT, "custom_constants/" + E, PROPERTY_HINT_RANGE, "-16384,16384", usage)); + p_list->push_back(PropertyInfo(Variant::OBJECT, "theme_override_styles/" + E, PROPERTY_HINT_RESOURCE_TYPE, "StyleBox", usage)); } } } @@ -2835,6 +2837,9 @@ void Control::_bind_methods() { ADD_GROUP("Layout Direction", "layout_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "layout_direction", PROPERTY_HINT_ENUM, "Inherited,Locale,Left-to-Right,Right-to-Left"), "set_layout_direction", "get_layout_direction"); + ADD_GROUP("Auto Translate", ""); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_translate"), "set_auto_translate", "is_auto_translating"); + ADD_GROUP("Rect", "rect_"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_position", "get_position"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_global_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "_set_global_position", "get_global_position"); @@ -2870,10 +2875,6 @@ void Control::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "theme", PROPERTY_HINT_RESOURCE_TYPE, "Theme"), "set_theme", "get_theme"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "theme_type_variation", PROPERTY_HINT_ENUM_SUGGESTION), "set_theme_type_variation", "get_theme_type_variation"); - ADD_GROUP("Auto Translate", ""); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_translate"), "set_auto_translate", "is_auto_translating"); - ADD_GROUP("", ""); - BIND_ENUM_CONSTANT(FOCUS_NONE); BIND_ENUM_CONSTANT(FOCUS_CLICK); BIND_ENUM_CONSTANT(FOCUS_ALL); diff --git a/scene/gui/tree.h b/scene/gui/tree.h index 10e6642303..be711b4c4f 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -462,8 +462,6 @@ private: void _gui_input(Ref<InputEvent> p_event); void _notification(int p_what); - Size2 get_minimum_size() const override; - void item_edited(int p_column, TreeItem *p_item, bool p_lmb = true); void item_changed(int p_column, TreeItem *p_item); void item_selected(int p_column, TreeItem *p_item); @@ -721,6 +719,8 @@ public: void set_allow_reselect(bool p_allow); bool get_allow_reselect() const; + Size2 get_minimum_size() const override; + Tree(); ~Tree(); }; diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp index 8550af8bcb..cab6c0378a 100644 --- a/scene/resources/environment.cpp +++ b/scene/resources/environment.cpp @@ -449,6 +449,7 @@ bool Environment::is_sdfgi_enabled() const { } void Environment::set_sdfgi_cascades(SDFGICascades p_cascades) { + ERR_FAIL_INDEX(p_cascades, SDFGI_CASCADES_8 + 1); sdfgi_cascades = p_cascades; _update_sdfgi(); } diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp index 9f8c35b668..875aa30824 100644 --- a/scene/resources/surface_tool.cpp +++ b/scene/resources/surface_tool.cpp @@ -1120,6 +1120,7 @@ SurfaceTool::CustomFormat SurfaceTool::get_custom_format(int p_index) const { void SurfaceTool::optimize_indices_for_cache() { ERR_FAIL_COND(optimize_vertex_cache_func == nullptr); ERR_FAIL_COND(index_array.size() == 0); + ERR_FAIL_COND(index_array.size() % 3 != 0); LocalVector old_index_array = index_array; memset(index_array.ptr(), 0, index_array.size() * sizeof(int)); diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index 737b47ed95..fcd31143a8 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -62,11 +62,6 @@ const char *TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[] = { "top_right_corner" }; -// --- Plugins --- -Vector<TileSetPlugin *> TileSet::get_tile_set_atlas_plugins() const { - return tile_set_plugins_vector; -} - // -- Shape and layout -- void TileSet::set_tile_shape(TileSet::TileShape p_shape) { tile_shape = p_shape; @@ -205,21 +200,11 @@ void TileSet::set_uv_clipping(bool p_uv_clipping) { uv_clipping = p_uv_clipping; emit_changed(); } + bool TileSet::is_uv_clipping() const { return uv_clipping; }; -void TileSet::set_y_sorting(bool p_y_sort) { - if (y_sorting == p_y_sort) { - return; - } - y_sorting = p_y_sort; - emit_changed(); -} -bool TileSet::is_y_sorting() const { - return y_sorting; -}; - void TileSet::set_occlusion_layers_count(int p_occlusion_layers_count) { ERR_FAIL_COND(p_occlusion_layers_count < 0); if (occlusion_layers.size() == p_occlusion_layers_count) { @@ -2604,8 +2589,6 @@ void TileSet::_bind_methods() { // Rendering. ClassDB::bind_method(D_METHOD("set_uv_clipping", "uv_clipping"), &TileSet::set_uv_clipping); ClassDB::bind_method(D_METHOD("is_uv_clipping"), &TileSet::is_uv_clipping); - ClassDB::bind_method(D_METHOD("set_y_sorting", "y_sorting"), &TileSet::set_y_sorting); - ClassDB::bind_method(D_METHOD("is_y_sorting"), &TileSet::is_y_sorting); ClassDB::bind_method(D_METHOD("set_occlusion_layers_count", "occlusion_layers_count"), &TileSet::set_occlusion_layers_count); ClassDB::bind_method(D_METHOD("get_occlusion_layers_count"), &TileSet::get_occlusion_layers_count); @@ -2670,7 +2653,6 @@ void TileSet::_bind_methods() { 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"); ADD_PROPERTY(PropertyInfo(Variant::INT, "occlusion_layers_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_occlusion_layers_count", "get_occlusion_layers_count"); ADD_GROUP("Physics", ""); @@ -2727,12 +2709,6 @@ TileSet::TileSet() { // Instantiate the tile meshes. tile_lines_mesh.instantiate(); tile_filled_mesh.instantiate(); - - // Instantiate and list all plugins. - tile_set_plugins_vector.append(memnew(TileSetPluginAtlasRendering)); - tile_set_plugins_vector.append(memnew(TileSetPluginAtlasPhysics)); - tile_set_plugins_vector.append(memnew(TileSetPluginAtlasNavigation)); - tile_set_plugins_vector.append(memnew(TileSetPluginScenesCollections)); } TileSet::~TileSet() { @@ -2744,9 +2720,6 @@ TileSet::~TileSet() { while (!source_ids.is_empty()) { remove_source(source_ids[0]); } - for (int i = 0; i < tile_set_plugins_vector.size(); i++) { - memdelete(tile_set_plugins_vector[i]); - } } /////////////////////////////// TileSetSource ////////////////////////////////////// @@ -2972,23 +2945,22 @@ void TileSetAtlasSource::_get_property_list(List<PropertyInfo> *p_list) const { // Get the alternative tile's properties and append them to the list of properties. List<PropertyInfo> alternative_property_list; E_alternative->get()->get_property_list(&alternative_property_list); - for (List<PropertyInfo>::Element *E_property = alternative_property_list.front(); E_property; E_property = E_property->next()) { - property_info = E_property->get(); + for (PropertyInfo &alternative_property_info : alternative_property_list) { bool valid; - Variant default_value = ClassDB::class_get_default_property_value("TileData", property_info.name, &valid); - Variant value = E_alternative->get()->get(property_info.name); + Variant default_value = ClassDB::class_get_default_property_value("TileData", alternative_property_info.name, &valid); + Variant value = E_alternative->get()->get(alternative_property_info.name); if (valid && value == default_value) { property_info.usage ^= PROPERTY_USAGE_STORAGE; } - property_info.name = vformat("%s/%s", vformat("%d", E_alternative->key()), property_info.name); - tile_property_list.push_back(property_info); + alternative_property_info.name = vformat("%s/%s", vformat("%d", E_alternative->key()), alternative_property_info.name); + tile_property_list.push_back(alternative_property_info); } } // Add all alternative. - for (List<PropertyInfo>::Element *E_property = tile_property_list.front(); E_property; E_property = E_property->next()) { - E_property->get().name = vformat("%s/%s", vformat("%d:%d", E_tile->key().x, E_tile->key().y), E_property->get().name); - p_list->push_back(E_property->get()); + for (PropertyInfo &tile_property_info : tile_property_list) { + tile_property_info.name = vformat("%s/%s", vformat("%d:%d", E_tile->key().x, E_tile->key().y), tile_property_info.name); + p_list->push_back(tile_property_info); } } } @@ -3817,6 +3789,7 @@ int TileData::get_terrain_set() const { } void TileData::set_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit, int p_terrain_index) { + ERR_FAIL_INDEX(p_peering_bit, TileSet::CellNeighbor::CELL_NEIGHBOR_MAX); ERR_FAIL_COND(terrain_set < 0); ERR_FAIL_COND(p_terrain_index < -1); if (tile_set) { @@ -4238,842 +4211,3 @@ void TileData::_bind_methods() { ADD_SIGNAL(MethodInfo("changed")); } -/////////////////////////////// TileSetPluginAtlasRendering ////////////////////////////////////// - -void TileSetPluginAtlasRendering::tilemap_notification(TileMap *p_tile_map, int p_what) { - switch (p_what) { - case CanvasItem::NOTIFICATION_VISIBILITY_CHANGED: { - bool visible = p_tile_map->is_visible_in_tree(); - for (Map<Vector2i, TileMapQuadrant>::Element *E_quadrant = p_tile_map->get_quadrant_map().front(); E_quadrant; E_quadrant = E_quadrant->next()) { - TileMapQuadrant &q = E_quadrant->get(); - - // Update occluders transform. - for (Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator>::Element *E_cell = q.world_to_map.front(); E_cell; E_cell = E_cell->next()) { - Transform2D xform; - xform.set_origin(E_cell->key()); - for (List<RID>::Element *E_occluder_id = q.occluders.front(); E_occluder_id; E_occluder_id = E_occluder_id->next()) { - RS::get_singleton()->canvas_light_occluder_set_enabled(E_occluder_id->get(), visible); - } - } - } - } break; - case CanvasItem::NOTIFICATION_TRANSFORM_CHANGED: { - if (!p_tile_map->is_inside_tree()) { - return; - } - - for (Map<Vector2i, TileMapQuadrant>::Element *E_quadrant = p_tile_map->get_quadrant_map().front(); E_quadrant; E_quadrant = E_quadrant->next()) { - TileMapQuadrant &q = E_quadrant->get(); - - // Update occluders transform. - for (Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator>::Element *E_cell = q.world_to_map.front(); E_cell; E_cell = E_cell->next()) { - Transform2D xform; - xform.set_origin(E_cell->key()); - for (List<RID>::Element *E_occluder_id = q.occluders.front(); E_occluder_id; E_occluder_id = E_occluder_id->next()) { - RS::get_singleton()->canvas_light_occluder_set_transform(E_occluder_id->get(), p_tile_map->get_global_transform() * xform); - } - } - } - } break; - case CanvasItem::NOTIFICATION_DRAW: { - Ref<TileSet> tile_set = p_tile_map->get_tileset(); - if (tile_set.is_valid() || p_tile_map->is_y_sort_enabled()) { - RenderingServer::get_singleton()->canvas_item_set_sort_children_by_y(p_tile_map->get_canvas_item(), tile_set->is_y_sorting() || p_tile_map->is_y_sort_enabled()); - } - } break; - } -} - -void TileSetPluginAtlasRendering::draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, Color p_modulation) { - ERR_FAIL_COND(!p_tile_set.is_valid()); - ERR_FAIL_COND(!p_tile_set->has_source(p_atlas_source_id)); - ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_tile(p_atlas_coords)); - ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_alternative_tile(p_atlas_coords, p_alternative_tile)); - - TileSetSource *source = *p_tile_set->get_source(p_atlas_source_id); - TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); - if (atlas_source) { - // Get the texture. - Ref<Texture2D> tex = atlas_source->get_texture(); - if (!tex.is_valid()) { - return; - } - - // Check if we are in the texture, return otherwise. - Vector2i grid_size = atlas_source->get_atlas_grid_size(); - if (p_atlas_coords.x >= grid_size.x || p_atlas_coords.y >= grid_size.y) { - return; - } - - // Get tile data. - TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(p_atlas_coords, p_alternative_tile)); - - // Compute the offset - Rect2i source_rect = atlas_source->get_tile_texture_region(p_atlas_coords); - Vector2i tile_offset = atlas_source->get_tile_effective_texture_offset(p_atlas_coords, p_alternative_tile); - - // Compute the destination rectangle in the CanvasItem. - Rect2 dest_rect; - dest_rect.size = source_rect.size; - dest_rect.size.x += fp_adjust; - dest_rect.size.y += fp_adjust; - - bool transpose = tile_data->get_transpose(); - if (transpose) { - dest_rect.position = (p_position - Vector2(dest_rect.size.y, dest_rect.size.x) / 2 - tile_offset); - } else { - dest_rect.position = (p_position - dest_rect.size / 2 - tile_offset); - } - - if (tile_data->get_flip_h()) { - dest_rect.size.x = -dest_rect.size.x; - } - - if (tile_data->get_flip_v()) { - dest_rect.size.y = -dest_rect.size.y; - } - - // Get the tile modulation. - Color modulate = tile_data->get_modulate(); - modulate = Color(modulate.r * p_modulation.r, modulate.g * p_modulation.g, modulate.b * p_modulation.b, modulate.a * p_modulation.a); - - // Draw the tile. - tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping()); - } -} - -void TileSetPluginAtlasRendering::update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) { - ERR_FAIL_COND(!p_tile_map); - ERR_FAIL_COND(!p_tile_map->is_inside_tree()); - Ref<TileSet> tile_set = p_tile_map->get_tileset(); - ERR_FAIL_COND(!tile_set.is_valid()); - - bool visible = p_tile_map->is_visible_in_tree(); - - SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first(); - while (q_list_element) { - TileMapQuadrant &q = *q_list_element->self(); - - RenderingServer *rs = RenderingServer::get_singleton(); - - // Free the canvas items. - for (const RID &E : q.canvas_items) { - rs->free(E); - } - q.canvas_items.clear(); - - // Free the occluders. - for (const RID &E : q.occluders) { - rs->free(E); - } - q.occluders.clear(); - - // Those allow to group cell per material or z-index. - Ref<ShaderMaterial> prev_material; - int prev_z_index = 0; - RID prev_canvas_item; - - // 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(), true); - - TileSetSource *source; - if (tile_set->has_source(c.source_id)) { - source = *tile_set->get_source(c.source_id); - - if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { - continue; - } - - TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); - if (atlas_source) { - // Get the tile data. - TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile)); - Ref<ShaderMaterial> mat = tile_data->tile_get_material(); - int z_index = tile_data->get_z_index(); - - // Quandrant pos. - Vector2 position = p_tile_map->map_to_world(q.coords * p_tile_map->get_effective_quadrant_size()); - if (tile_set->is_y_sorting()) { - // When Y-sorting, the quandrant size is sure to be 1, we can thus offset the CanvasItem. - position.y += tile_data->get_y_sort_origin(); - } - - // --- CanvasItems --- - // Create two canvas items, for rendering and debug. - RID canvas_item; - - // Check if the material or the z_index changed. - if (prev_canvas_item == RID() || prev_material != mat || prev_z_index != z_index) { - // If so, create a new CanvasItem. - canvas_item = rs->canvas_item_create(); - if (mat.is_valid()) { - rs->canvas_item_set_material(canvas_item, mat->get_rid()); - } - rs->canvas_item_set_parent(canvas_item, p_tile_map->get_canvas_item()); - rs->canvas_item_set_use_parent_material(canvas_item, p_tile_map->get_use_parent_material() || p_tile_map->get_material().is_valid()); - - Transform2D xform; - xform.set_origin(position); - rs->canvas_item_set_transform(canvas_item, xform); - - rs->canvas_item_set_light_mask(canvas_item, p_tile_map->get_light_mask()); - rs->canvas_item_set_z_index(canvas_item, z_index); - - rs->canvas_item_set_default_texture_filter(canvas_item, RS::CanvasItemTextureFilter(p_tile_map->CanvasItem::get_texture_filter())); - rs->canvas_item_set_default_texture_repeat(canvas_item, RS::CanvasItemTextureRepeat(p_tile_map->CanvasItem::get_texture_repeat())); - - q.canvas_items.push_back(canvas_item); - - prev_canvas_item = canvas_item; - prev_material = mat; - prev_z_index = z_index; - - } else { - // Keep the same canvas_item to draw on. - canvas_item = prev_canvas_item; - } - - // Drawing the tile in the canvas item. - draw_tile(canvas_item, E_cell->key() - position, tile_set, c.source_id, c.get_atlas_coords(), c.alternative_tile, p_tile_map->get_self_modulate()); - - // --- Occluders --- - for (int i = 0; i < tile_set->get_occlusion_layers_count(); i++) { - Transform2D xform; - xform.set_origin(E_cell->key()); - if (tile_data->get_occluder(i).is_valid()) { - RID occluder_id = rs->canvas_light_occluder_create(); - rs->canvas_light_occluder_set_enabled(occluder_id, visible); - rs->canvas_light_occluder_set_transform(occluder_id, p_tile_map->get_global_transform() * xform); - rs->canvas_light_occluder_set_polygon(occluder_id, tile_data->get_occluder(i)->get_rid()); - rs->canvas_light_occluder_attach_to_canvas(occluder_id, p_tile_map->get_canvas()); - rs->canvas_light_occluder_set_light_mask(occluder_id, tile_set->get_occlusion_layer_light_mask(i)); - q.occluders.push_back(occluder_id); - } - } - } - } - } - - quadrant_order_dirty = true; - q_list_element = q_list_element->next(); - } - - // Reset the drawing indices - if (quadrant_order_dirty) { - int index = -(int64_t)0x80000000; //always must be drawn below children. - - // Sort the quadrants coords per world coordinates - Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator> world_to_map; - Map<Vector2i, TileMapQuadrant> quadrant_map = p_tile_map->get_quadrant_map(); - for (Map<Vector2i, TileMapQuadrant>::Element *E = quadrant_map.front(); E; E = E->next()) { - world_to_map[p_tile_map->map_to_world(E->key())] = E->key(); - } - - // Sort the quadrants - for (Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator>::Element *E = world_to_map.front(); E; E = E->next()) { - TileMapQuadrant &q = quadrant_map[E->value()]; - for (const RID &F : q.canvas_items) { - RS::get_singleton()->canvas_item_set_draw_index(F, index++); - } - } - - quadrant_order_dirty = false; - } -} - -void TileSetPluginAtlasRendering::create_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) { - Ref<TileSet> tile_set = p_tile_map->get_tileset(); - ERR_FAIL_COND(!tile_set.is_valid()); - - quadrant_order_dirty = true; -} - -void TileSetPluginAtlasRendering::cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) { - // Free the canvas items. - for (const RID &E : p_quadrant->canvas_items) { - RenderingServer::get_singleton()->free(E); - } - p_quadrant->canvas_items.clear(); - - // Free the occluders. - for (const RID &E : p_quadrant->occluders) { - RenderingServer::get_singleton()->free(E); - } - p_quadrant->occluders.clear(); -} - -void TileSetPluginAtlasRendering::draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) { - Ref<TileSet> tile_set = p_tile_map->get_tileset(); - ERR_FAIL_COND(!tile_set.is_valid()); - - if (!Engine::get_singleton()->is_editor_hint()) { - return; - } - - // Draw a placeholder for scenes needing one. - 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(), true); - - TileSetSource *source; - if (tile_set->has_source(c.source_id)) { - source = *tile_set->get_source(c.source_id); - - if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { - continue; - } - - TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); - if (atlas_source) { - Vector2i grid_size = atlas_source->get_atlas_grid_size(); - if (!atlas_source->get_texture().is_valid() || c.get_atlas_coords().x >= grid_size.x || c.get_atlas_coords().y >= grid_size.y) { - // Generate a random color from the hashed values of the tiles. - Array to_hash; - to_hash.push_back(c.source_id); - to_hash.push_back(c.get_atlas_coords()); - to_hash.push_back(c.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 a placeholder tile. - Transform2D xform; - xform.set_origin(p_tile_map->map_to_world(E_cell->get()) - quadrant_pos); - rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform); - rs->canvas_item_add_circle(p_quadrant->debug_canvas_item, Vector2(), MIN(tile_set->get_tile_size().x, tile_set->get_tile_size().y) / 4.0, color); - } - } - } - } -} - -/////////////////////////////// TileSetPluginAtlasPhysics ////////////////////////////////////// - -void TileSetPluginAtlasPhysics::tilemap_notification(TileMap *p_tile_map, int p_what) { - switch (p_what) { - case CanvasItem::NOTIFICATION_TRANSFORM_CHANGED: { - // Update the bodies transforms. - if (p_tile_map->is_inside_tree()) { - Map<Vector2i, TileMapQuadrant> quadrant_map = p_tile_map->get_quadrant_map(); - Transform2D global_transform = p_tile_map->get_global_transform(); - - for (Map<Vector2i, TileMapQuadrant>::Element *E = quadrant_map.front(); E; E = E->next()) { - TileMapQuadrant &q = E->get(); - - Transform2D xform; - xform.set_origin(p_tile_map->map_to_world(E->key() * p_tile_map->get_effective_quadrant_size())); - xform = global_transform * xform; - - for (int body_index = 0; body_index < q.bodies.size(); body_index++) { - PhysicsServer2D::get_singleton()->body_set_state(q.bodies[body_index], PhysicsServer2D::BODY_STATE_TRANSFORM, xform); - } - } - } - } break; - } -} - -void TileSetPluginAtlasPhysics::update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) { - ERR_FAIL_COND(!p_tile_map); - ERR_FAIL_COND(!p_tile_map->is_inside_tree()); - Ref<TileSet> tile_set = p_tile_map->get_tileset(); - ERR_FAIL_COND(!tile_set.is_valid()); - - Transform2D global_transform = p_tile_map->get_global_transform(); - PhysicsServer2D *ps = PhysicsServer2D::get_singleton(); - - SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first(); - while (q_list_element) { - TileMapQuadrant &q = *q_list_element->self(); - - Vector2 quadrant_pos = p_tile_map->map_to_world(q.coords * p_tile_map->get_effective_quadrant_size()); - - // Clear shapes. - for (int body_index = 0; body_index < q.bodies.size(); body_index++) { - ps->body_clear_shapes(q.bodies[body_index]); - - // Position the bodies. - Transform2D xform; - xform.set_origin(quadrant_pos); - xform = global_transform * xform; - ps->body_set_state(q.bodies[body_index], PhysicsServer2D::BODY_STATE_TRANSFORM, xform); - } - - 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(), true); - - TileSetSource *source; - if (tile_set->has_source(c.source_id)) { - source = *tile_set->get_source(c.source_id); - - if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { - continue; - } - - TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); - if (atlas_source) { - TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile)); - - for (int body_index = 0; body_index < q.bodies.size(); body_index++) { - // Add the shapes again. - for (int polygon_index = 0; polygon_index < tile_data->get_collision_polygons_count(body_index); polygon_index++) { - bool one_way_collision = tile_data->is_collision_polygon_one_way(body_index, polygon_index); - float one_way_collision_margin = tile_data->get_collision_polygon_one_way_margin(body_index, polygon_index); - - int shapes_count = tile_data->get_collision_polygon_shapes_count(body_index, polygon_index); - for (int shape_index = 0; shape_index < shapes_count; shape_index++) { - Transform2D xform = Transform2D(); - xform.set_origin(p_tile_map->map_to_world(E_cell->get()) - quadrant_pos); - - // Add decomposed convex shapes. - Ref<ConvexPolygonShape2D> shape = tile_data->get_collision_polygon_shape(body_index, polygon_index, shape_index); - ps->body_add_shape(q.bodies[body_index], shape->get_rid(), xform); - ps->body_set_shape_metadata(q.bodies[body_index], shape_index, E_cell->get()); - ps->body_set_shape_as_one_way_collision(q.bodies[body_index], shape_index, one_way_collision, one_way_collision_margin); - } - } - } - } - } - } - - q_list_element = q_list_element->next(); - } -} - -void TileSetPluginAtlasPhysics::create_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) { - Ref<TileSet> tile_set = p_tile_map->get_tileset(); - ERR_FAIL_COND(!tile_set.is_valid()); - - //Get the TileMap's gobla transform. - Transform2D global_transform; - if (p_tile_map->is_inside_tree()) { - global_transform = p_tile_map->get_global_transform(); - } - - // Clear all bodies. - p_quadrant->bodies.clear(); - - // Create the body and set its parameters. - for (int layer_index = 0; layer_index < tile_set->get_physics_layers_count(); layer_index++) { - RID body = PhysicsServer2D::get_singleton()->body_create(); - PhysicsServer2D::get_singleton()->body_set_mode(body, PhysicsServer2D::BODY_MODE_STATIC); - - PhysicsServer2D::get_singleton()->body_attach_object_instance_id(body, p_tile_map->get_instance_id()); - PhysicsServer2D::get_singleton()->body_set_collision_layer(body, tile_set->get_physics_layer_collision_layer(layer_index)); - PhysicsServer2D::get_singleton()->body_set_collision_mask(body, tile_set->get_physics_layer_collision_mask(layer_index)); - - Ref<PhysicsMaterial> physics_material = tile_set->get_physics_layer_physics_material(layer_index); - if (!physics_material.is_valid()) { - PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_BOUNCE, 0); - PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_FRICTION, 1); - } else { - PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_BOUNCE, physics_material->computed_bounce()); - PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_FRICTION, physics_material->computed_friction()); - } - - if (p_tile_map->is_inside_tree()) { - RID space = p_tile_map->get_world_2d()->get_space(); - PhysicsServer2D::get_singleton()->body_set_space(body, space); - - Transform2D xform; - xform.set_origin(p_tile_map->map_to_world(p_quadrant->coords * p_tile_map->get_effective_quadrant_size())); - xform = global_transform * xform; - PhysicsServer2D::get_singleton()->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform); - } - - p_quadrant->bodies.push_back(body); - } -} - -void TileSetPluginAtlasPhysics::cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) { - // Remove a quadrant. - for (int body_index = 0; body_index < p_quadrant->bodies.size(); body_index++) { - PhysicsServer2D::get_singleton()->free(p_quadrant->bodies[body_index]); - } - p_quadrant->bodies.clear(); -} - -void TileSetPluginAtlasPhysics::draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) { - // Draw the debug collision shapes. - Ref<TileSet> tile_set = p_tile_map->get_tileset(); - ERR_FAIL_COND(!tile_set.is_valid()); - - if (!p_tile_map->get_tree()) { - return; - } - - bool show_collision = false; - switch (p_tile_map->get_collision_visibility_mode()) { - case TileMap::VISIBILITY_MODE_DEFAULT: - show_collision = !Engine::get_singleton()->is_editor_hint() && (p_tile_map->get_tree() && p_tile_map->get_tree()->is_debugging_navigation_hint()); - break; - case TileMap::VISIBILITY_MODE_FORCE_HIDE: - show_collision = false; - break; - case TileMap::VISIBILITY_MODE_FORCE_SHOW: - show_collision = true; - break; - } - if (!show_collision) { - return; - } - - RenderingServer *rs = RenderingServer::get_singleton(); - - Vector2 quadrant_pos = p_tile_map->map_to_world(p_quadrant->coords * p_tile_map->get_effective_quadrant_size()); - - 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(), true); - - Transform2D xform; - xform.set_origin(p_tile_map->map_to_world(E_cell->get()) - quadrant_pos); - rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform); - - if (tile_set->has_source(c.source_id)) { - TileSetSource *source = *tile_set->get_source(c.source_id); - - if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { - continue; - } - - TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); - if (atlas_source) { - TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile)); - - for (int body_index = 0; body_index < p_quadrant->bodies.size(); body_index++) { - for (int polygon_index = 0; polygon_index < tile_data->get_collision_polygons_count(body_index); polygon_index++) { - // Draw the debug polygon. - Vector<Vector2> polygon = tile_data->get_collision_polygon_points(body_index, polygon_index); - if (polygon.size() >= 3) { - Vector<Color> color; - color.push_back(debug_collision_color); - rs->canvas_item_add_polygon(p_quadrant->debug_canvas_item, polygon, color); - } - } - } - } - } - rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, Transform2D()); - } -}; - -/////////////////////////////// TileSetPluginAtlasNavigation ////////////////////////////////////// - -void TileSetPluginAtlasNavigation::tilemap_notification(TileMap *p_tile_map, int p_what) { - switch (p_what) { - case CanvasItem::NOTIFICATION_TRANSFORM_CHANGED: { - if (p_tile_map->is_inside_tree()) { - Map<Vector2i, TileMapQuadrant> quadrant_map = p_tile_map->get_quadrant_map(); - Transform2D tilemap_xform = p_tile_map->get_global_transform(); - for (Map<Vector2i, TileMapQuadrant>::Element *E_quadrant = quadrant_map.front(); E_quadrant; E_quadrant = E_quadrant->next()) { - TileMapQuadrant &q = E_quadrant->get(); - for (Map<Vector2i, Vector<RID>>::Element *E_region = q.navigation_regions.front(); E_region; E_region = E_region->next()) { - for (int layer_index = 0; layer_index < E_region->get().size(); layer_index++) { - RID region = E_region->get()[layer_index]; - if (!region.is_valid()) { - continue; - } - Transform2D tile_transform; - tile_transform.set_origin(p_tile_map->map_to_world(E_region->key())); - NavigationServer2D::get_singleton()->region_set_transform(region, tilemap_xform * tile_transform); - } - } - } - } - } break; - } -} - -void TileSetPluginAtlasNavigation::update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) { - ERR_FAIL_COND(!p_tile_map); - ERR_FAIL_COND(!p_tile_map->is_inside_tree()); - Ref<TileSet> tile_set = p_tile_map->get_tileset(); - ERR_FAIL_COND(!tile_set.is_valid()); - - // Get colors for debug. - SceneTree *st = SceneTree::get_singleton(); - Color debug_navigation_color; - bool debug_navigation = st && st->is_debugging_navigation_hint(); - if (debug_navigation) { - debug_navigation_color = st->get_debug_navigation_color(); - } - - Transform2D tilemap_xform = p_tile_map->get_global_transform(); - SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first(); - while (q_list_element) { - TileMapQuadrant &q = *q_list_element->self(); - - // Clear navigation shapes in the quadrant. - for (Map<Vector2i, Vector<RID>>::Element *E = q.navigation_regions.front(); E; E = E->next()) { - for (int i = 0; i < E->get().size(); i++) { - RID region = E->get()[i]; - if (!region.is_valid()) { - continue; - } - NavigationServer2D::get_singleton()->region_set_map(region, RID()); - } - } - q.navigation_regions.clear(); - - // 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(), true); - - TileSetSource *source; - if (tile_set->has_source(c.source_id)) { - source = *tile_set->get_source(c.source_id); - - if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { - continue; - } - - TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); - if (atlas_source) { - TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile)); - q.navigation_regions[E_cell->get()].resize(tile_set->get_navigation_layers_count()); - - for (int layer_index = 0; layer_index < tile_set->get_navigation_layers_count(); layer_index++) { - Ref<NavigationPolygon> navpoly; - navpoly = tile_data->get_navigation_polygon(layer_index); - - if (navpoly.is_valid()) { - Transform2D tile_transform; - tile_transform.set_origin(p_tile_map->map_to_world(E_cell->get())); - - RID region = NavigationServer2D::get_singleton()->region_create(); - NavigationServer2D::get_singleton()->region_set_map(region, p_tile_map->get_world_2d()->get_navigation_map()); - NavigationServer2D::get_singleton()->region_set_transform(region, tilemap_xform * tile_transform); - NavigationServer2D::get_singleton()->region_set_navpoly(region, navpoly); - q.navigation_regions[E_cell->get()].write[layer_index] = region; - } - } - } - } - } - - q_list_element = q_list_element->next(); - } -} - -void TileSetPluginAtlasNavigation::cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) { - // Clear navigation shapes in the quadrant. - for (Map<Vector2i, Vector<RID>>::Element *E = p_quadrant->navigation_regions.front(); E; E = E->next()) { - for (int i = 0; i < E->get().size(); i++) { - RID region = E->get()[i]; - if (!region.is_valid()) { - continue; - } - NavigationServer2D::get_singleton()->free(region); - } - } - p_quadrant->navigation_regions.clear(); -} - -void TileSetPluginAtlasNavigation::draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) { - // Draw the debug collision shapes. - Ref<TileSet> tile_set = p_tile_map->get_tileset(); - ERR_FAIL_COND(!tile_set.is_valid()); - - if (!p_tile_map->get_tree()) { - return; - } - - bool show_navigation = false; - switch (p_tile_map->get_navigation_visibility_mode()) { - case TileMap::VISIBILITY_MODE_DEFAULT: - show_navigation = !Engine::get_singleton()->is_editor_hint() && (p_tile_map->get_tree() && p_tile_map->get_tree()->is_debugging_navigation_hint()); - break; - case TileMap::VISIBILITY_MODE_FORCE_HIDE: - show_navigation = false; - break; - case TileMap::VISIBILITY_MODE_FORCE_SHOW: - show_navigation = true; - break; - } - if (!show_navigation) { - return; - } - - RenderingServer *rs = RenderingServer::get_singleton(); - - Color color = p_tile_map->get_tree()->get_debug_navigation_color(); - RandomPCG rand; - - 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(), true); - - TileSetSource *source; - if (tile_set->has_source(c.source_id)) { - source = *tile_set->get_source(c.source_id); - - if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { - continue; - } - - TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); - if (atlas_source) { - TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile)); - - Transform2D xform; - xform.set_origin(p_tile_map->map_to_world(E_cell->get()) - quadrant_pos); - rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform); - - for (int layer_index = 0; layer_index < tile_set->get_navigation_layers_count(); layer_index++) { - Ref<NavigationPolygon> navpoly = tile_data->get_navigation_polygon(layer_index); - if (navpoly.is_valid()) { - PackedVector2Array navigation_polygon_vertices = navpoly->get_vertices(); - - for (int i = 0; i < navpoly->get_polygon_count(); i++) { - // An array of vertices for this polygon. - Vector<int> polygon = navpoly->get_polygon(i); - Vector<Vector2> vertices; - vertices.resize(polygon.size()); - for (int j = 0; j < polygon.size(); j++) { - ERR_FAIL_INDEX(polygon[j], navigation_polygon_vertices.size()); - vertices.write[j] = navigation_polygon_vertices[polygon[j]]; - } - - // Generate the polygon color, slightly randomly modified from the settings one. - Color random_variation_color; - random_variation_color.set_hsv(color.get_h() + rand.random(-1.0, 1.0) * 0.05, color.get_s(), color.get_v() + rand.random(-1.0, 1.0) * 0.1); - random_variation_color.a = color.a; - Vector<Color> colors; - colors.push_back(random_variation_color); - - rs->canvas_item_add_polygon(p_quadrant->debug_canvas_item, vertices, colors); - } - } - } - } - } - } -} - -/////////////////////////////// TileSetPluginScenesCollections ////////////////////////////////////// - -void TileSetPluginScenesCollections::update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) { - Ref<TileSet> tile_set = p_tile_map->get_tileset(); - ERR_FAIL_COND(!tile_set.is_valid()); - - SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first(); - while (q_list_element) { - TileMapQuadrant &q = *q_list_element->self(); - - // Clear the scenes. - for (Map<Vector2i, String>::Element *E = q.scenes.front(); E; E = E->next()) { - Node *node = p_tile_map->get_node(E->get()); - if (node) { - node->queue_delete(); - } - } - - q.scenes.clear(); - - // 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(), true); - - TileSetSource *source; - if (tile_set->has_source(c.source_id)) { - source = *tile_set->get_source(c.source_id); - - if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { - continue; - } - - TileSetScenesCollectionSource *scenes_collection_source = Object::cast_to<TileSetScenesCollectionSource>(source); - if (scenes_collection_source) { - Ref<PackedScene> packed_scene = scenes_collection_source->get_scene_tile_scene(c.alternative_tile); - if (packed_scene.is_valid()) { - Node *scene = packed_scene->instantiate(); - p_tile_map->add_child(scene); - Control *scene_as_control = Object::cast_to<Control>(scene); - Node2D *scene_as_node2d = Object::cast_to<Node2D>(scene); - if (scene_as_control) { - scene_as_control->set_position(p_tile_map->map_to_world(E_cell->get()) + scene_as_control->get_position()); - } else if (scene_as_node2d) { - Transform2D xform; - xform.set_origin(p_tile_map->map_to_world(E_cell->get())); - scene_as_node2d->set_transform(xform * scene_as_node2d->get_transform()); - } - q.scenes[E_cell->get()] = scene->get_name(); - } - } - } - } - - q_list_element = q_list_element->next(); - } -} - -void TileSetPluginScenesCollections::cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) { - // Clear the scenes. - for (Map<Vector2i, String>::Element *E = p_quadrant->scenes.front(); E; E = E->next()) { - Node *node = p_tile_map->get_node(E->get()); - if (node) { - node->queue_delete(); - } - } - - p_quadrant->scenes.clear(); -} - -void TileSetPluginScenesCollections::draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) { - Ref<TileSet> tile_set = p_tile_map->get_tileset(); - ERR_FAIL_COND(!tile_set.is_valid()); - - if (!Engine::get_singleton()->is_editor_hint()) { - return; - } - - // Draw a placeholder for scenes needing one. - 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(), true); - - TileSetSource *source; - if (tile_set->has_source(c.source_id)) { - source = *tile_set->get_source(c.source_id); - - if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { - continue; - } - - TileSetScenesCollectionSource *scenes_collection_source = Object::cast_to<TileSetScenesCollectionSource>(source); - if (scenes_collection_source) { - if (!scenes_collection_source->get_scene_tile_scene(c.alternative_tile).is_valid() || scenes_collection_source->get_scene_tile_display_placeholder(c.alternative_tile)) { - // Generate a random color from the hashed values of the tiles. - Array to_hash; - to_hash.push_back(c.source_id); - to_hash.push_back(c.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 a placeholder tile. - Transform2D xform; - xform.set_origin(p_tile_map->map_to_world(E_cell->get()) - quadrant_pos); - rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform); - rs->canvas_item_add_circle(p_quadrant->debug_canvas_item, Vector2(), MIN(tile_set->get_tile_size().x, tile_set->get_tile_size().y) / 4.0, color); - } - } - } - } -} diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h index 0a07981171..35e6999d13 100644 --- a/scene/resources/tile_set.h +++ b/scene/resources/tile_set.h @@ -191,7 +191,6 @@ private: Vector2 tile_skew = Vector2(0, 0); // Rendering. - bool y_sorting = false; bool uv_clipping = false; struct OcclusionLayer { uint32_t light_mask = 1; @@ -245,9 +244,6 @@ private: int next_source_id = 0; // --------------------- - // Plugins themselves. - Vector<TileSetPlugin *> tile_set_plugins_vector; - void _compute_next_source_id(); void _source_changed(); @@ -299,9 +295,6 @@ public: Ref<TileSetSource> get_source(int p_source_id) const; // Rendering - void set_y_sorting(bool p_y_sort); - bool is_y_sorting() const; - void set_uv_clipping(bool p_uv_clipping); bool is_uv_clipping() const; @@ -666,73 +659,6 @@ public: Variant get_custom_data_by_layer_id(int p_layer_id) const; }; -#include "scene/2d/tile_map.h" - -class TileSetPlugin : public Object { - GDCLASS(TileSetPlugin, Object); - -public: - // Tilemap updates. - virtual void tilemap_notification(TileMap *p_tile_map, int p_what){}; - virtual void update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list){}; - virtual void create_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant){}; - virtual void cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant){}; - - virtual void draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant){}; -}; - -class TileSetPluginAtlasRendering : public TileSetPlugin { - GDCLASS(TileSetPluginAtlasRendering, TileSetPlugin); - -private: - static constexpr float fp_adjust = 0.00001; - bool quadrant_order_dirty = false; - -public: - // Tilemap updates - virtual void tilemap_notification(TileMap *p_tile_map, int p_what) override; - virtual void update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) override; - virtual void create_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override; - virtual void cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override; - virtual void draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override; - - // Other. - static void draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, Color p_modulation = Color(1.0, 1.0, 1.0, 1.0)); -}; - -class TileSetPluginAtlasPhysics : public TileSetPlugin { - GDCLASS(TileSetPluginAtlasPhysics, TileSetPlugin); - -public: - // Tilemap updates - virtual void tilemap_notification(TileMap *p_tile_map, int p_what) override; - virtual void update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) override; - virtual void create_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override; - virtual void cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override; - virtual void draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override; -}; - -class TileSetPluginAtlasNavigation : public TileSetPlugin { - GDCLASS(TileSetPluginAtlasNavigation, TileSetPlugin); - -public: - // Tilemap updates - virtual void tilemap_notification(TileMap *p_tile_map, int p_what) override; - virtual void update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) override; - virtual void cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override; - virtual void draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override; -}; - -class TileSetPluginScenesCollections : public TileSetPlugin { - GDCLASS(TileSetPluginScenesCollections, TileSetPlugin); - -public: - // Tilemap updates - virtual void update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) override; - virtual void cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override; - virtual void draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override; -}; - VARIANT_ENUM_CAST(TileSet::CellNeighbor); VARIANT_ENUM_CAST(TileSet::TerrainMode); VARIANT_ENUM_CAST(TileSet::TileShape); diff --git a/servers/rendering/renderer_rd/effects_rd.cpp b/servers/rendering/renderer_rd/effects_rd.cpp index 47bb756d55..699f4a4b90 100644 --- a/servers/rendering/renderer_rd/effects_rd.cpp +++ b/servers/rendering/renderer_rd/effects_rd.cpp @@ -37,6 +37,10 @@ #include "servers/rendering/renderer_rd/renderer_compositor_rd.h" #include "thirdparty/misc/cubemap_coeffs.h" +bool EffectsRD::get_prefer_raster_effects() { + return prefer_raster_effects; +} + static _FORCE_INLINE_ void store_camera(const CameraMatrix &p_mtx, float *p_array) { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { @@ -454,7 +458,7 @@ void EffectsRD::gaussian_glow(RID p_source_rd_texture, RID p_back_texture, const } void EffectsRD::gaussian_glow_raster(RID p_source_rd_texture, RID p_framebuffer_half, RID p_rd_texture_half, RID p_dest_framebuffer, const Vector2 &p_pixel_size, float p_strength, bool p_high_quality, bool p_first_pass, float p_luminance_cap, float p_exposure, float p_bloom, float p_hdr_bleed_treshold, float p_hdr_bleed_scale, RID p_auto_exposure, float p_auto_exposure_grey) { - ERR_FAIL_COND_MSG(!prefer_raster_effects, "Can't use the fragment version of the gaussian glow with the clustered renderer."); + ERR_FAIL_COND_MSG(!prefer_raster_effects, "Can't use the raster version of the gaussian glow with the clustered renderer."); memset(&blur_raster.push_constant, 0, sizeof(BlurRasterPushConstant)); @@ -832,7 +836,7 @@ void EffectsRD::luminance_reduction(RID p_source_texture, const Size2i p_source_ } void EffectsRD::luminance_reduction_raster(RID p_source_texture, const Size2i p_source_size, const Vector<RID> p_reduce, Vector<RID> p_fb, RID p_prev_luminance, float p_min_luminance, float p_max_luminance, float p_adjust, bool p_set) { - ERR_FAIL_COND_MSG(!prefer_raster_effects, "Can't use fragment version of luminance reduction with the clustered renderer."); + ERR_FAIL_COND_MSG(!prefer_raster_effects, "Can't use raster version of luminance reduction with the clustered renderer."); ERR_FAIL_COND_MSG(p_reduce.size() != p_fb.size(), "Incorrect frame buffer account for luminance reduction."); luminance_reduce_raster.push_constant.max_luminance = p_max_luminance; @@ -1447,7 +1451,9 @@ void EffectsRD::roughness_limit(RID p_source_normal, RID p_roughness, const Size RD::get_singleton()->compute_list_end(); } -void EffectsRD::cubemap_roughness(RID p_source_rd_texture, RID p_dest_framebuffer, uint32_t p_face_id, uint32_t p_sample_count, float p_roughness, float p_size) { +void EffectsRD::cubemap_roughness(RID p_source_rd_texture, RID p_dest_texture, uint32_t p_face_id, uint32_t p_sample_count, float p_roughness, float p_size) { + ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use compute based cubemap roughness with the mobile renderer."); + memset(&roughness.push_constant, 0, sizeof(CubemapRoughnessPushConstant)); roughness.push_constant.face_id = p_face_id > 9 ? 0 : p_face_id; @@ -1457,10 +1463,10 @@ void EffectsRD::cubemap_roughness(RID p_source_rd_texture, RID p_dest_framebuffe roughness.push_constant.face_size = p_size; RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); - RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, roughness.pipeline); + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, roughness.compute_pipeline); RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_source_rd_texture), 0); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_dest_framebuffer), 1); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_dest_texture), 1); RD::get_singleton()->compute_list_set_push_constant(compute_list, &roughness.push_constant, sizeof(CubemapRoughnessPushConstant)); @@ -1472,11 +1478,37 @@ void EffectsRD::cubemap_roughness(RID p_source_rd_texture, RID p_dest_framebuffe RD::get_singleton()->compute_list_end(); } +void EffectsRD::cubemap_roughness_raster(RID p_source_rd_texture, RID p_dest_framebuffer, uint32_t p_face_id, uint32_t p_sample_count, float p_roughness, float p_size) { + ERR_FAIL_COND_MSG(!prefer_raster_effects, "Can't use raster based cubemap roughness with the clustered renderer."); + ERR_FAIL_COND_MSG(p_face_id >= 6, "Raster implementation of cubemap roughness must process one side at a time."); + + memset(&roughness.push_constant, 0, sizeof(CubemapRoughnessPushConstant)); + + roughness.push_constant.face_id = p_face_id; + roughness.push_constant.roughness = p_roughness; + roughness.push_constant.sample_count = p_sample_count; + roughness.push_constant.use_direct_write = p_roughness == 0.0; + roughness.push_constant.face_size = p_size; + + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); + RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, roughness.raster_pipeline.get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer))); + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_source_rd_texture), 0); + RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array); + + RD::get_singleton()->draw_list_set_push_constant(draw_list, &roughness.push_constant, sizeof(CubemapRoughnessPushConstant)); + + RD::get_singleton()->draw_list_draw(draw_list, true); + RD::get_singleton()->draw_list_end(); +} + void EffectsRD::cubemap_downsample(RID p_source_cubemap, RID p_dest_cubemap, const Size2i &p_size) { + ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use compute based cubemap downsample with the mobile renderer."); + cubemap_downsampler.push_constant.face_size = p_size.x; + cubemap_downsampler.push_constant.face_id = 0; // we render all 6 sides to each layer in one call RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); - RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, cubemap_downsampler.pipeline); + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, cubemap_downsampler.compute_pipeline); RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_source_cubemap), 0); RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_dest_cubemap), 1); @@ -1490,7 +1522,27 @@ void EffectsRD::cubemap_downsample(RID p_source_cubemap, RID p_dest_cubemap, con RD::get_singleton()->compute_list_end(); } +void EffectsRD::cubemap_downsample_raster(RID p_source_cubemap, RID p_dest_framebuffer, uint32_t p_face_id, const Size2i &p_size) { + ERR_FAIL_COND_MSG(!prefer_raster_effects, "Can't use raster based cubemap downsample with the clustered renderer."); + ERR_FAIL_COND_MSG(p_face_id >= 6, "Raster implementation of cubemap downsample must process one side at a time."); + + cubemap_downsampler.push_constant.face_size = p_size.x; + cubemap_downsampler.push_constant.face_id = p_face_id; + + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); + RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, cubemap_downsampler.raster_pipeline.get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer))); + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_source_cubemap), 0); + RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array); + + RD::get_singleton()->draw_list_set_push_constant(draw_list, &cubemap_downsampler.push_constant, sizeof(CubemapDownsamplerPushConstant)); + + RD::get_singleton()->draw_list_draw(draw_list, true); + RD::get_singleton()->draw_list_end(); +} + void EffectsRD::cubemap_filter(RID p_source_cubemap, Vector<RID> p_dest_cubemap, bool p_use_array) { + ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use compute based cubemap filter with the mobile renderer."); + Vector<RD::Uniform> uniforms; for (int i = 0; i < p_dest_cubemap.size(); i++) { RD::Uniform u; @@ -1502,12 +1554,12 @@ void EffectsRD::cubemap_filter(RID p_source_cubemap, Vector<RID> p_dest_cubemap, if (RD::get_singleton()->uniform_set_is_valid(filter.image_uniform_set)) { RD::get_singleton()->free(filter.image_uniform_set); } - filter.image_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, filter.shader.version_get_shader(filter.shader_version, 0), 2); + filter.image_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, filter.compute_shader.version_get_shader(filter.shader_version, 0), 2); int pipeline = p_use_array ? FILTER_MODE_HIGH_QUALITY_ARRAY : FILTER_MODE_HIGH_QUALITY; pipeline = filter.use_high_quality ? pipeline : pipeline + 1; RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); - RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, filter.pipelines[pipeline]); + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, filter.compute_pipelines[pipeline]); RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_source_cubemap, true), 0); RD::get_singleton()->compute_list_bind_uniform_set(compute_list, filter.uniform_set, 1); RD::get_singleton()->compute_list_bind_uniform_set(compute_list, filter.image_uniform_set, 2); @@ -1519,6 +1571,29 @@ void EffectsRD::cubemap_filter(RID p_source_cubemap, Vector<RID> p_dest_cubemap, RD::get_singleton()->compute_list_end(); } +void EffectsRD::cubemap_filter_raster(RID p_source_cubemap, RID p_dest_framebuffer, uint32_t p_face_id, uint32_t p_mip_level) { + ERR_FAIL_COND_MSG(!prefer_raster_effects, "Can't use raster based cubemap filter with the clustered renderer."); + ERR_FAIL_COND_MSG(p_face_id >= 6, "Raster implementation of cubemap filter must process one side at a time."); + + // TODO implement! + CubemapFilterRasterPushConstant push_constant; + push_constant.mip_level = p_mip_level; + push_constant.face_id = p_face_id; + + CubemapFilterMode mode = filter.use_high_quality ? FILTER_MODE_HIGH_QUALITY : FILTER_MODE_LOW_QUALITY; + + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); + RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, filter.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer))); + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_source_cubemap), 0); + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, filter.uniform_set, 1); + RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array); + + RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(CubemapFilterRasterPushConstant)); + + RD::get_singleton()->draw_list_draw(draw_list, true); + RD::get_singleton()->draw_list_end(); +} + void EffectsRD::resolve_gi(RID p_source_depth, RID p_source_normal_roughness, RID p_source_voxel_gi, RID p_dest_depth, RID p_dest_normal_roughness, RID p_dest_voxel_gi, Vector2i p_screen_size, int p_samples, uint32_t p_barrier) { ResolvePushConstant push_constant; push_constant.screen_size[0] = p_screen_size.x; @@ -1713,11 +1788,22 @@ EffectsRD::EffectsRD(bool p_prefer_raster_effects) { // Initialize roughness Vector<String> cubemap_roughness_modes; cubemap_roughness_modes.push_back(""); - roughness.shader.initialize(cubemap_roughness_modes); - roughness.shader_version = roughness.shader.version_create(); + if (prefer_raster_effects) { + roughness.raster_shader.initialize(cubemap_roughness_modes); + + roughness.shader_version = roughness.raster_shader.version_create(); + + roughness.raster_pipeline.setup(roughness.raster_shader.version_get_shader(roughness.shader_version, 0), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0); + + } else { + roughness.compute_shader.initialize(cubemap_roughness_modes); + + roughness.shader_version = roughness.compute_shader.version_create(); - roughness.pipeline = RD::get_singleton()->compute_pipeline_create(roughness.shader.version_get_shader(roughness.shader_version, 0)); + roughness.compute_pipeline = RD::get_singleton()->compute_pipeline_create(roughness.compute_shader.version_get_shader(roughness.shader_version, 0)); + roughness.raster_pipeline.clear(); + } } { @@ -1983,11 +2069,21 @@ EffectsRD::EffectsRD(bool p_prefer_raster_effects) { //Initialize cubemap downsampler Vector<String> cubemap_downsampler_modes; cubemap_downsampler_modes.push_back(""); - cubemap_downsampler.shader.initialize(cubemap_downsampler_modes); - cubemap_downsampler.shader_version = cubemap_downsampler.shader.version_create(); + if (prefer_raster_effects) { + cubemap_downsampler.raster_shader.initialize(cubemap_downsampler_modes); + + cubemap_downsampler.shader_version = cubemap_downsampler.raster_shader.version_create(); + + cubemap_downsampler.raster_pipeline.setup(cubemap_downsampler.raster_shader.version_get_shader(cubemap_downsampler.shader_version, 0), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0); + } else { + cubemap_downsampler.compute_shader.initialize(cubemap_downsampler_modes); - cubemap_downsampler.pipeline = RD::get_singleton()->compute_pipeline_create(cubemap_downsampler.shader.version_get_shader(cubemap_downsampler.shader_version, 0)); + cubemap_downsampler.shader_version = cubemap_downsampler.compute_shader.version_create(); + + cubemap_downsampler.compute_pipeline = RD::get_singleton()->compute_pipeline_create(cubemap_downsampler.compute_shader.version_get_shader(cubemap_downsampler.shader_version, 0)); + cubemap_downsampler.raster_pipeline.clear(); + } } { @@ -1999,12 +2095,6 @@ EffectsRD::EffectsRD(bool p_prefer_raster_effects) { cubemap_filter_modes.push_back("\n#define USE_LOW_QUALITY\n"); cubemap_filter_modes.push_back("\n#define USE_HIGH_QUALITY\n#define USE_TEXTURE_ARRAY\n"); cubemap_filter_modes.push_back("\n#define USE_LOW_QUALITY\n#define USE_TEXTURE_ARRAY\n"); - filter.shader.initialize(cubemap_filter_modes); - filter.shader_version = filter.shader.version_create(); - - for (int i = 0; i < FILTER_MODE_MAX; i++) { - filter.pipelines[i] = RD::get_singleton()->compute_pipeline_create(filter.shader.version_get_shader(filter.shader_version, i)); - } if (filter.use_high_quality) { filter.coefficient_buffer = RD::get_singleton()->storage_buffer_create(sizeof(high_quality_coeffs)); @@ -2014,15 +2104,50 @@ EffectsRD::EffectsRD(bool p_prefer_raster_effects) { RD::get_singleton()->buffer_update(filter.coefficient_buffer, 0, sizeof(low_quality_coeffs), &low_quality_coeffs[0]); } - Vector<RD::Uniform> uniforms; - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; - u.binding = 0; - u.ids.push_back(filter.coefficient_buffer); - uniforms.push_back(u); + if (prefer_raster_effects) { + filter.raster_shader.initialize(cubemap_filter_modes); + filter.shader_version = filter.raster_shader.version_create(); + + // array variants are not supported in raster + filter.raster_shader.set_variant_enabled(FILTER_MODE_HIGH_QUALITY_ARRAY, false); + filter.raster_shader.set_variant_enabled(FILTER_MODE_LOW_QUALITY_ARRAY, false); + + for (int i = 0; i < FILTER_MODE_MAX; i++) { + if (filter.raster_shader.is_variant_enabled(i)) { + filter.raster_pipelines[i].setup(filter.raster_shader.version_get_shader(filter.shader_version, i), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0); + } else { + filter.raster_pipelines[i].clear(); + } + } + + Vector<RD::Uniform> uniforms; + { + RD::Uniform u; + u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.binding = 0; + u.ids.push_back(filter.coefficient_buffer); + uniforms.push_back(u); + } + filter.uniform_set = RD::get_singleton()->uniform_set_create(uniforms, filter.raster_shader.version_get_shader(filter.shader_version, filter.use_high_quality ? 0 : 1), 1); + } else { + filter.compute_shader.initialize(cubemap_filter_modes); + filter.shader_version = filter.compute_shader.version_create(); + + for (int i = 0; i < FILTER_MODE_MAX; i++) { + filter.compute_pipelines[i] = RD::get_singleton()->compute_pipeline_create(filter.compute_shader.version_get_shader(filter.shader_version, i)); + filter.raster_pipelines[i].clear(); + } + + Vector<RD::Uniform> uniforms; + { + RD::Uniform u; + u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.binding = 0; + u.ids.push_back(filter.coefficient_buffer); + uniforms.push_back(u); + } + filter.uniform_set = RD::get_singleton()->uniform_set_create(uniforms, filter.compute_shader.version_get_shader(filter.shader_version, filter.use_high_quality ? 0 : 1), 1); } - filter.uniform_set = RD::get_singleton()->uniform_set_create(uniforms, filter.shader.version_get_shader(filter.shader_version, filter.use_high_quality ? 0 : 1), 1); } { @@ -2199,17 +2324,20 @@ EffectsRD::~EffectsRD() { if (prefer_raster_effects) { blur_raster.shader.version_free(blur_raster.shader_version); luminance_reduce_raster.shader.version_free(luminance_reduce_raster.shader_version); + roughness.raster_shader.version_free(roughness.shader_version); + cubemap_downsampler.raster_shader.version_free(cubemap_downsampler.shader_version); + filter.raster_shader.version_free(filter.shader_version); } else { bokeh.shader.version_free(bokeh.shader_version); luminance_reduce.shader.version_free(luminance_reduce.shader_version); + roughness.compute_shader.version_free(roughness.shader_version); + cubemap_downsampler.compute_shader.version_free(cubemap_downsampler.shader_version); + filter.compute_shader.version_free(filter.shader_version); } copy.shader.version_free(copy.shader_version); copy_to_fb.shader.version_free(copy_to_fb.shader_version); cube_to_dp.shader.version_free(cube_to_dp.shader_version); - cubemap_downsampler.shader.version_free(cubemap_downsampler.shader_version); - filter.shader.version_free(filter.shader_version); resolve.shader.version_free(resolve.shader_version); - roughness.shader.version_free(roughness.shader_version); roughness_limiter.shader.version_free(roughness_limiter.shader_version); sort.shader.version_free(sort.shader_version); specular_merge.shader.version_free(specular_merge.shader_version); diff --git a/servers/rendering/renderer_rd/effects_rd.h b/servers/rendering/renderer_rd/effects_rd.h index d072564c23..eff891571a 100644 --- a/servers/rendering/renderer_rd/effects_rd.h +++ b/servers/rendering/renderer_rd/effects_rd.h @@ -39,8 +39,11 @@ #include "servers/rendering/renderer_rd/shaders/copy_to_fb.glsl.gen.h" #include "servers/rendering/renderer_rd/shaders/cube_to_dp.glsl.gen.h" #include "servers/rendering/renderer_rd/shaders/cubemap_downsampler.glsl.gen.h" +#include "servers/rendering/renderer_rd/shaders/cubemap_downsampler_raster.glsl.gen.h" #include "servers/rendering/renderer_rd/shaders/cubemap_filter.glsl.gen.h" +#include "servers/rendering/renderer_rd/shaders/cubemap_filter_raster.glsl.gen.h" #include "servers/rendering/renderer_rd/shaders/cubemap_roughness.glsl.gen.h" +#include "servers/rendering/renderer_rd/shaders/cubemap_roughness_raster.glsl.gen.h" #include "servers/rendering/renderer_rd/shaders/luminance_reduce.glsl.gen.h" #include "servers/rendering/renderer_rd/shaders/luminance_reduce_raster.glsl.gen.h" #include "servers/rendering/renderer_rd/shaders/resolve.glsl.gen.h" @@ -62,6 +65,9 @@ #include "servers/rendering_server.h" class EffectsRD { +private: + bool prefer_raster_effects; + enum BlurRasterMode { BLUR_MODE_GAUSSIAN_BLUR, BLUR_MODE_GAUSSIAN_GLOW, @@ -220,9 +226,11 @@ class EffectsRD { struct CubemapRoughness { CubemapRoughnessPushConstant push_constant; - CubemapRoughnessShaderRD shader; + CubemapRoughnessShaderRD compute_shader; + CubemapRoughnessRasterShaderRD raster_shader; RID shader_version; - RID pipeline; + RID compute_pipeline; + PipelineCacheRD raster_pipeline; } roughness; enum TonemapMode { @@ -508,15 +516,17 @@ class EffectsRD { struct CubemapDownsamplerPushConstant { uint32_t face_size; - float pad[3]; + uint32_t face_id; + float pad[2]; }; struct CubemapDownsampler { CubemapDownsamplerPushConstant push_constant; - CubemapDownsamplerShaderRD shader; + CubemapDownsamplerShaderRD compute_shader; + CubemapDownsamplerRasterShaderRD raster_shader; RID shader_version; - RID pipeline; - + RID compute_pipeline; + PipelineCacheRD raster_pipeline; } cubemap_downsampler; enum CubemapFilterMode { @@ -527,10 +537,19 @@ class EffectsRD { FILTER_MODE_MAX, }; + struct CubemapFilterRasterPushConstant { + uint32_t mip_level; + uint32_t face_id; + float pad[2]; + }; + struct CubemapFilter { - CubemapFilterShaderRD shader; + CubemapFilterShaderRD compute_shader; + CubemapFilterRasterShaderRD raster_shader; RID shader_version; - RID pipelines[FILTER_MODE_MAX]; + RID compute_pipelines[FILTER_MODE_MAX]; + PipelineCacheRD raster_pipelines[FILTER_MODE_MAX]; + RID uniform_set; RID image_uniform_set; RID coefficient_buffer; @@ -738,9 +757,9 @@ class EffectsRD { RID _get_compute_uniform_set_from_texture_pair(RID p_texture, RID p_texture2, bool p_use_mipmaps = false); RID _get_compute_uniform_set_from_image_pair(RID p_texture, RID p_texture2); - bool prefer_raster_effects; - public: + bool get_prefer_raster_effects(); + void copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y = false, bool p_force_luminance = false, bool p_alpha_to_zero = false, bool p_srgb = false, RID p_secondary = RID()); void copy_to_rect(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y = false, bool p_force_luminance = false, bool p_all_source = false, bool p_8_bit_dst = false, bool p_alpha_to_one = false); void copy_cubemap_to_panorama(RID p_source_cube, RID p_dest_panorama, const Size2i &p_panorama_size, float p_lod, bool p_is_array); @@ -752,7 +771,8 @@ public: void gaussian_glow(RID p_source_rd_texture, RID p_back_texture, const Size2i &p_size, float p_strength = 1.0, bool p_high_quality = false, bool p_first_pass = false, float p_luminance_cap = 16.0, float p_exposure = 1.0, float p_bloom = 0.0, float p_hdr_bleed_treshold = 1.0, float p_hdr_bleed_scale = 1.0, RID p_auto_exposure = RID(), float p_auto_exposure_grey = 1.0); void gaussian_glow_raster(RID p_source_rd_texture, RID p_framebuffer_half, RID p_rd_texture_half, RID p_dest_framebuffer, const Vector2 &p_pixel_size, float p_strength = 1.0, bool p_high_quality = false, bool p_first_pass = false, float p_luminance_cap = 16.0, float p_exposure = 1.0, float p_bloom = 0.0, float p_hdr_bleed_treshold = 1.0, float p_hdr_bleed_scale = 1.0, RID p_auto_exposure = RID(), float p_auto_exposure_grey = 1.0); - void cubemap_roughness(RID p_source_rd_texture, RID p_dest_framebuffer, uint32_t p_face_id, uint32_t p_sample_count, float p_roughness, float p_size); + void cubemap_roughness(RID p_source_rd_texture, RID p_dest_texture, uint32_t p_face_id, uint32_t p_sample_count, float p_roughness, float p_size); + void cubemap_roughness_raster(RID p_source_rd_texture, RID p_dest_framebuffer, uint32_t p_face_id, uint32_t p_sample_count, float p_roughness, float p_size); void make_mipmap(RID p_source_rd_texture, RID p_dest_texture, const Size2i &p_size); void copy_cubemap_to_dp(RID p_source_rd_texture, RID p_dest_texture, const Rect2 &p_rect, float p_z_near, float p_z_far, bool p_dp_flip); void luminance_reduction(RID p_source_texture, const Size2i p_source_size, const Vector<RID> p_reduce, RID p_prev_luminance, float p_min_luminance, float p_max_luminance, float p_adjust, bool p_set = false); @@ -828,7 +848,9 @@ public: void roughness_limit(RID p_source_normal, RID p_roughness, const Size2i &p_size, float p_curve); void cubemap_downsample(RID p_source_cubemap, RID p_dest_cubemap, const Size2i &p_size); + void cubemap_downsample_raster(RID p_source_cubemap, RID p_dest_framebuffer, uint32_t p_face_id, const Size2i &p_size); void cubemap_filter(RID p_source_cubemap, Vector<RID> p_dest_cubemap, bool p_use_array); + void cubemap_filter_raster(RID p_source_cubemap, RID p_dest_framebuffer, uint32_t p_face_id, uint32_t p_mip_level); void screen_space_reflection(RID p_diffuse, RID p_normal_roughness, RS::EnvironmentSSRRoughnessQuality p_roughness_quality, RID p_blur_radius, RID p_blur_radius2, RID p_metallic, const Color &p_metallic_mask, RID p_depth, RID p_scale_depth, RID p_scale_normal, RID p_output, RID p_output_blur, const Size2i &p_screen_size, int p_max_steps, float p_fade_in, float p_fade_out, float p_tolerance, const CameraMatrix &p_camera); void merge_specular(RID p_dest_framebuffer, RID p_specular, RID p_base, RID p_reflection); diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp index 5f6d9465c7..4725599148 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp @@ -2325,6 +2325,8 @@ RenderForwardMobile::RenderForwardMobile(RendererStorageRD *p_storage) : RendererSceneRenderRD(p_storage) { singleton = this; + sky.set_texture_format(_render_buffers_get_color_format()); + String defines; defines += "\n#define MAX_ROUGHNESS_LOD " + itos(get_roughness_layers() - 1) + ".0\n"; diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp index 2e0e7c3c43..e3516df800 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp @@ -629,6 +629,8 @@ bool RendererSceneRenderRD::reflection_probe_instance_begin_render(RID p_instanc ReflectionProbeInstance *rpi = reflection_probe_instance_owner.getornull(p_instance); ERR_FAIL_COND_V(!rpi, false); + RD::get_singleton()->draw_command_begin_label("Reflection probe render"); + if (storage->reflection_probe_get_update_mode(rpi->probe) == RS::REFLECTION_PROBE_UPDATE_ALWAYS && atlas->reflection.is_valid() && atlas->size != 256) { WARN_PRINT("ReflectionProbes set to UPDATE_ALWAYS must have an atlas size of 256. Please update the atlas size in the ProjectSettings."); reflection_atlas_set_size(p_reflection_atlas, 256, atlas->count); @@ -675,7 +677,7 @@ bool RendererSceneRenderRD::reflection_probe_instance_begin_render(RID p_instanc } atlas->reflections.resize(atlas->count); for (int i = 0; i < atlas->count; i++) { - atlas->reflections.write[i].data.update_reflection_data(atlas->size, mipmaps, false, atlas->reflection, i * 6, storage->reflection_probe_get_update_mode(rpi->probe) == RS::REFLECTION_PROBE_UPDATE_ALWAYS, sky.roughness_layers); + atlas->reflections.write[i].data.update_reflection_data(storage, atlas->size, mipmaps, false, atlas->reflection, i * 6, storage->reflection_probe_get_update_mode(rpi->probe) == RS::REFLECTION_PROBE_UPDATE_ALWAYS, sky.roughness_layers, _render_buffers_get_color_format()); for (int j = 0; j < 6; j++) { Vector<RID> fb; fb.push_back(atlas->reflections.write[i].data.layers[0].mipmaps[0].views[j]); @@ -721,6 +723,8 @@ bool RendererSceneRenderRD::reflection_probe_instance_begin_render(RID p_instanc rpi->processing_layer = 1; rpi->processing_side = 0; + RD::get_singleton()->draw_command_end_label(); + return true; } diff --git a/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp index bc1603a219..cadf759ec3 100644 --- a/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp @@ -313,12 +313,16 @@ void RendererSceneSkyRD::ReflectionData::clear_reflection_data() { coefficient_buffer = RID(); } -void RendererSceneSkyRD::ReflectionData::update_reflection_data(int p_size, int p_mipmaps, bool p_use_array, RID p_base_cube, int p_base_layer, bool p_low_quality, int p_roughness_layers) { +void RendererSceneSkyRD::ReflectionData::update_reflection_data(RendererStorageRD *p_storage, int p_size, int p_mipmaps, bool p_use_array, RID p_base_cube, int p_base_layer, bool p_low_quality, int p_roughness_layers, RD::DataFormat p_texture_format) { //recreate radiance and all data int mipmaps = p_mipmaps; uint32_t w = p_size, h = p_size; + EffectsRD *effects = p_storage->get_effects(); + ERR_FAIL_NULL_MSG(effects, "Effects haven't been initialised"); + bool prefer_raster_effects = effects->get_prefer_raster_effects(); + if (p_use_array) { int num_layers = p_low_quality ? 8 : p_roughness_layers; @@ -377,9 +381,9 @@ void RendererSceneSkyRD::ReflectionData::update_reflection_data(int p_size, int } radiance_base_cubemap = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), p_base_cube, p_base_layer, 0, RD::TEXTURE_SLICE_CUBEMAP); - + RD::get_singleton()->set_resource_name(radiance_base_cubemap, "radiance base cubemap"); RD::TextureFormat tf; - tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; + tf.format = p_texture_format; tf.width = 64; // Always 64x64 tf.height = 64; tf.texture_type = RD::TEXTURE_TYPE_CUBE; @@ -388,6 +392,7 @@ void RendererSceneSkyRD::ReflectionData::update_reflection_data(int p_size, int tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; downsampled_radiance_cubemap = RD::get_singleton()->texture_create(tf, RD::TextureView()); + RD::get_singleton()->set_resource_name(downsampled_radiance_cubemap, "downsampled radiance cubemap"); { uint32_t mmw = 64; uint32_t mmh = 64; @@ -397,6 +402,18 @@ void RendererSceneSkyRD::ReflectionData::update_reflection_data(int p_size, int mm.size.width = mmw; mm.size.height = mmh; mm.view = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), downsampled_radiance_cubemap, 0, j, RD::TEXTURE_SLICE_CUBEMAP); + RD::get_singleton()->set_resource_name(mm.view, "Downsampled Radiance Cubemap Mip " + itos(j) + " "); + if (prefer_raster_effects) { + // we need a framebuffer for each side of our cubemap + + for (int k = 0; k < 6; k++) { + mm.views[k] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), downsampled_radiance_cubemap, k, j); + RD::get_singleton()->set_resource_name(mm.view, "Downsampled Radiance Cubemap Mip: " + itos(j) + " Face: " + itos(k) + " "); + Vector<RID> fbtex; + fbtex.push_back(mm.views[k]); + mm.framebuffers[k] = RD::get_singleton()->framebuffer_create(fbtex); + } + } mmw = MAX(1, mmw >> 1); mmh = MAX(1, mmh >> 1); @@ -405,50 +422,128 @@ void RendererSceneSkyRD::ReflectionData::update_reflection_data(int p_size, int } void RendererSceneSkyRD::ReflectionData::create_reflection_fast_filter(RendererStorageRD *p_storage, bool p_use_arrays) { - p_storage->get_effects()->cubemap_downsample(radiance_base_cubemap, downsampled_layer.mipmaps[0].view, downsampled_layer.mipmaps[0].size); + EffectsRD *effects = p_storage->get_effects(); + ERR_FAIL_NULL_MSG(effects, "Effects haven't been initialised"); + bool prefer_raster_effects = effects->get_prefer_raster_effects(); + + if (prefer_raster_effects) { + RD::get_singleton()->draw_command_begin_label("Downsample radiance map"); + for (int k = 0; k < 6; k++) { + effects->cubemap_downsample_raster(radiance_base_cubemap, downsampled_layer.mipmaps[0].framebuffers[k], k, downsampled_layer.mipmaps[0].size); + } - for (int i = 1; i < downsampled_layer.mipmaps.size(); i++) { - p_storage->get_effects()->cubemap_downsample(downsampled_layer.mipmaps[i - 1].view, downsampled_layer.mipmaps[i].view, downsampled_layer.mipmaps[i].size); - } + for (int i = 1; i < downsampled_layer.mipmaps.size(); i++) { + for (int k = 0; k < 6; k++) { + effects->cubemap_downsample_raster(downsampled_layer.mipmaps[i - 1].view, downsampled_layer.mipmaps[i].framebuffers[k], k, downsampled_layer.mipmaps[i].size); + } + } + RD::get_singleton()->draw_command_end_label(); // Downsample Radiance - Vector<RID> views; - if (p_use_arrays) { - for (int i = 1; i < layers.size(); i++) { - views.push_back(layers[i].views[0]); + if (p_use_arrays) { + RD::get_singleton()->draw_command_begin_label("filter radiance map into array heads"); + for (int i = 0; i < layers.size(); i++) { + for (int k = 0; k < 6; k++) { + effects->cubemap_filter_raster(downsampled_radiance_cubemap, layers[i].mipmaps[0].framebuffers[k], k, i); + } + } + } else { + RD::get_singleton()->draw_command_begin_label("filter radiance map into mipmaps directly"); + for (int j = 0; j < layers[0].mipmaps.size(); j++) { + for (int k = 0; k < 6; k++) { + effects->cubemap_filter_raster(downsampled_radiance_cubemap, layers[0].mipmaps[j].framebuffers[k], k, j); + } + } } + RD::get_singleton()->draw_command_end_label(); // Filter radiance } else { - for (int i = 1; i < layers[0].views.size(); i++) { - views.push_back(layers[0].views[i]); + effects->cubemap_downsample(radiance_base_cubemap, downsampled_layer.mipmaps[0].view, downsampled_layer.mipmaps[0].size); + + for (int i = 1; i < downsampled_layer.mipmaps.size(); i++) { + effects->cubemap_downsample(downsampled_layer.mipmaps[i - 1].view, downsampled_layer.mipmaps[i].view, downsampled_layer.mipmaps[i].size); + } + + Vector<RID> views; + if (p_use_arrays) { + for (int i = 1; i < layers.size(); i++) { + views.push_back(layers[i].views[0]); + } + } else { + for (int i = 1; i < layers[0].views.size(); i++) { + views.push_back(layers[0].views[i]); + } } - } - p_storage->get_effects()->cubemap_filter(downsampled_radiance_cubemap, views, p_use_arrays); + effects->cubemap_filter(downsampled_radiance_cubemap, views, p_use_arrays); + } } void RendererSceneSkyRD::ReflectionData::create_reflection_importance_sample(RendererStorageRD *p_storage, bool p_use_arrays, int p_cube_side, int p_base_layer, uint32_t p_sky_ggx_samples_quality) { - if (p_use_arrays) { - //render directly to the layers - p_storage->get_effects()->cubemap_roughness(radiance_base_cubemap, layers[p_base_layer].views[0], p_cube_side, p_sky_ggx_samples_quality, float(p_base_layer) / (layers.size() - 1.0), layers[p_base_layer].mipmaps[0].size.x); + EffectsRD *effects = p_storage->get_effects(); + ERR_FAIL_NULL_MSG(effects, "Effects haven't been initialised"); + bool prefer_raster_effects = effects->get_prefer_raster_effects(); + + if (prefer_raster_effects) { + // Need to ask clayjohn but p_cube_side is set to 10, looks like in the compute shader we're doing all 6 sides in one call + // here we need to do them one by one so ignoring p_cube_side + if (p_use_arrays) { + for (int k = 0; k < 6; k++) { + effects->cubemap_roughness_raster( + radiance_base_cubemap, + layers[p_base_layer].mipmaps[0].framebuffers[k], + k, + p_sky_ggx_samples_quality, + float(p_base_layer) / (layers.size() - 1.0), + layers[p_base_layer].mipmaps[0].size.x); + } + } else { + for (int k = 0; k < 6; k++) { + effects->cubemap_roughness_raster( + layers[0].views[p_base_layer - 1], + layers[0].mipmaps[p_base_layer].framebuffers[k], + k, + p_sky_ggx_samples_quality, + float(p_base_layer) / (layers[0].mipmaps.size() - 1.0), + layers[0].mipmaps[p_base_layer].size.x); + } + } } else { - p_storage->get_effects()->cubemap_roughness( - layers[0].views[p_base_layer - 1], - layers[0].views[p_base_layer], - p_cube_side, - p_sky_ggx_samples_quality, - float(p_base_layer) / (layers[0].mipmaps.size() - 1.0), - layers[0].mipmaps[p_base_layer].size.x); + if (p_use_arrays) { + //render directly to the layers + effects->cubemap_roughness(radiance_base_cubemap, layers[p_base_layer].views[0], p_cube_side, p_sky_ggx_samples_quality, float(p_base_layer) / (layers.size() - 1.0), layers[p_base_layer].mipmaps[0].size.x); + } else { + effects->cubemap_roughness( + layers[0].views[p_base_layer - 1], + layers[0].views[p_base_layer], + p_cube_side, + p_sky_ggx_samples_quality, + float(p_base_layer) / (layers[0].mipmaps.size() - 1.0), + layers[0].mipmaps[p_base_layer].size.x); + } } } void RendererSceneSkyRD::ReflectionData::update_reflection_mipmaps(RendererStorageRD *p_storage, int p_start, int p_end) { + EffectsRD *effects = p_storage->get_effects(); + ERR_FAIL_NULL_MSG(effects, "Effects haven't been initialised"); + bool prefer_raster_effects = effects->get_prefer_raster_effects(); + + RD::get_singleton()->draw_command_begin_label("Update Radiance Cubemap Array Mipmaps"); for (int i = p_start; i < p_end; i++) { for (int j = 0; j < layers[i].views.size() - 1; j++) { RID view = layers[i].views[j]; - RID texture = layers[i].views[j + 1]; Size2i size = layers[i].mipmaps[j + 1].size; - p_storage->get_effects()->cubemap_downsample(view, texture, size); + if (prefer_raster_effects) { + for (int k = 0; k < 6; k++) { + RID framebuffer = layers[i].mipmaps[j + 1].framebuffers[k]; + effects->cubemap_downsample_raster(view, framebuffer, k, size); + } + } else { + RID texture = layers[i].views[j + 1]; + effects->cubemap_downsample(view, texture, size); + } } } + RD::get_singleton()->draw_command_end_label(); } //////////////////////////////////////////////////////////////////////////////// @@ -902,6 +997,10 @@ void sky() { } } +void RendererSceneSkyRD::set_texture_format(RD::DataFormat p_texture_format) { + texture_format = p_texture_format; +} + RendererSceneSkyRD::~RendererSceneSkyRD() { // TODO cleanup anything created in init... @@ -1170,6 +1269,7 @@ void RendererSceneSkyRD::update(RendererSceneEnvironmentRD *p_env, const CameraM cm = correction * cm; if (shader_data->uses_quarter_res) { + RD::get_singleton()->draw_command_begin_label("Render Sky to Quarter Res Cubemap"); PipelineCacheRD *pipeline = &shader_data->pipelines[SKY_VERSION_CUBEMAP_QUARTER_RES]; Vector<Color> clear_colors; @@ -1185,9 +1285,11 @@ void RendererSceneSkyRD::update(RendererSceneEnvironmentRD *p_env, const CameraM _render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[2].framebuffers[i], pipeline, material->uniform_set, texture_uniform_set, 1, &cm, local_view.basis, multiplier, p_transform.origin); RD::get_singleton()->draw_list_end(); } + RD::get_singleton()->draw_command_end_label(); } if (shader_data->uses_half_res) { + RD::get_singleton()->draw_command_begin_label("Render Sky to Half Res Cubemap"); PipelineCacheRD *pipeline = &shader_data->pipelines[SKY_VERSION_CUBEMAP_HALF_RES]; Vector<Color> clear_colors; @@ -1203,11 +1305,13 @@ void RendererSceneSkyRD::update(RendererSceneEnvironmentRD *p_env, const CameraM _render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[1].framebuffers[i], pipeline, material->uniform_set, texture_uniform_set, 1, &cm, local_view.basis, multiplier, p_transform.origin); RD::get_singleton()->draw_list_end(); } + RD::get_singleton()->draw_command_end_label(); } RD::DrawListID cubemap_draw_list; PipelineCacheRD *pipeline = &shader_data->pipelines[SKY_VERSION_CUBEMAP]; + RD::get_singleton()->draw_command_begin_label("Render Sky Cubemap"); for (int i = 0; i < 6; i++) { Transform3D local_view; local_view.set_look_at(Vector3(0, 0, 0), view_normals[i], view_up[i]); @@ -1217,6 +1321,7 @@ void RendererSceneSkyRD::update(RendererSceneEnvironmentRD *p_env, const CameraM _render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[0].framebuffers[i], pipeline, material->uniform_set, texture_uniform_set, 1, &cm, local_view.basis, multiplier, p_transform.origin); RD::get_singleton()->draw_list_end(); } + RD::get_singleton()->draw_command_end_label(); if (sky_mode == RS::SKY_MODE_REALTIME) { sky->reflection.create_reflection_fast_filter(storage, sky_use_cubemap_array); @@ -1393,7 +1498,7 @@ void RendererSceneSkyRD::update_dirty_skys() { //array (higher quality, 6 times more memory) RD::TextureFormat tf; tf.array_layers = layers * 6; - tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; + tf.format = texture_format; tf.texture_type = RD::TEXTURE_TYPE_CUBE_ARRAY; tf.mipmaps = mipmaps; tf.width = w; @@ -1402,13 +1507,13 @@ void RendererSceneSkyRD::update_dirty_skys() { sky->radiance = RD::get_singleton()->texture_create(tf, RD::TextureView()); - sky->reflection.update_reflection_data(sky->radiance_size, mipmaps, true, sky->radiance, 0, sky->mode == RS::SKY_MODE_REALTIME, roughness_layers); + sky->reflection.update_reflection_data(storage, sky->radiance_size, mipmaps, true, sky->radiance, 0, sky->mode == RS::SKY_MODE_REALTIME, roughness_layers, texture_format); } else { //regular cubemap, lower quality (aliasing, less memory) RD::TextureFormat tf; tf.array_layers = 6; - tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; + tf.format = texture_format; tf.texture_type = RD::TEXTURE_TYPE_CUBE; tf.mipmaps = MIN(mipmaps, layers); tf.width = w; @@ -1417,7 +1522,7 @@ void RendererSceneSkyRD::update_dirty_skys() { sky->radiance = RD::get_singleton()->texture_create(tf, RD::TextureView()); - sky->reflection.update_reflection_data(sky->radiance_size, MIN(mipmaps, layers), false, sky->radiance, 0, sky->mode == RS::SKY_MODE_REALTIME, roughness_layers); + sky->reflection.update_reflection_data(storage, sky->radiance_size, MIN(mipmaps, layers), false, sky->radiance, 0, sky->mode == RS::SKY_MODE_REALTIME, roughness_layers, texture_format); } texture_set_dirty = true; } @@ -1425,7 +1530,7 @@ void RendererSceneSkyRD::update_dirty_skys() { // Create subpass buffers if they haven't been created already if (sky->half_res_pass.is_null() && !RD::get_singleton()->texture_is_valid(sky->half_res_pass) && sky->screen_size.x >= 4 && sky->screen_size.y >= 4) { RD::TextureFormat tformat; - tformat.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; + tformat.format = texture_format; tformat.width = sky->screen_size.x / 2; tformat.height = sky->screen_size.y / 2; tformat.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; @@ -1440,7 +1545,7 @@ void RendererSceneSkyRD::update_dirty_skys() { if (sky->quarter_res_pass.is_null() && !RD::get_singleton()->texture_is_valid(sky->quarter_res_pass) && sky->screen_size.x >= 4 && sky->screen_size.y >= 4) { RD::TextureFormat tformat; - tformat.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; + tformat.format = texture_format; tformat.width = sky->screen_size.x / 4; tformat.height = sky->screen_size.y / 4; tformat.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; diff --git a/servers/rendering/renderer_rd/renderer_scene_sky_rd.h b/servers/rendering/renderer_rd/renderer_scene_sky_rd.h index 4f852e55a7..7e0b01d58e 100644 --- a/servers/rendering/renderer_rd/renderer_scene_sky_rd.h +++ b/servers/rendering/renderer_rd/renderer_scene_sky_rd.h @@ -64,6 +64,7 @@ public: private: RendererStorageRD *storage; + RD::DataFormat texture_format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; RID index_buffer; RID index_array; @@ -190,6 +191,10 @@ public: struct Mipmap { RID view; Size2i size; + + // for mobile only + RID views[6]; + RID framebuffers[6]; }; Vector<Mipmap> mipmaps; }; @@ -204,7 +209,7 @@ public: Vector<Layer> layers; void clear_reflection_data(); - void update_reflection_data(int p_size, int p_mipmaps, bool p_use_array, RID p_base_cube, int p_base_layer, bool p_low_quality, int p_roughness_layers); + void update_reflection_data(RendererStorageRD *p_storage, int p_size, int p_mipmaps, bool p_use_array, RID p_base_cube, int p_base_layer, bool p_low_quality, int p_roughness_layers, RD::DataFormat p_texture_format); void create_reflection_fast_filter(RendererStorageRD *p_storage, bool p_use_arrays); void create_reflection_importance_sample(RendererStorageRD *p_storage, bool p_use_arrays, int p_cube_side, int p_base_layer, uint32_t p_sky_ggx_samples_quality); void update_reflection_mipmaps(RendererStorageRD *p_storage, int p_start, int p_end); @@ -284,6 +289,7 @@ public: RendererSceneSkyRD(); void init(RendererStorageRD *p_storage); + void set_texture_format(RD::DataFormat p_texture_format); ~RendererSceneSkyRD(); void setup(RendererSceneEnvironmentRD *p_env, RID p_render_buffers, const CameraMatrix &p_projection, const Transform3D &p_transform, const Size2i p_screen_size, RendererSceneRenderRD *p_scene_render); diff --git a/servers/rendering/renderer_rd/shaders/cubemap_downsampler.glsl b/servers/rendering/renderer_rd/shaders/cubemap_downsampler.glsl index 9fa84657d1..63f0ce690e 100644 --- a/servers/rendering/renderer_rd/shaders/cubemap_downsampler.glsl +++ b/servers/rendering/renderer_rd/shaders/cubemap_downsampler.glsl @@ -32,53 +32,7 @@ layout(set = 0, binding = 0) uniform samplerCube source_cubemap; layout(rgba16f, set = 1, binding = 0) uniform restrict writeonly imageCube dest_cubemap; -layout(push_constant, binding = 1, std430) uniform Params { - uint face_size; -} -params; - -#define M_PI 3.14159265359 - -void get_dir_0(out vec3 dir, in float u, in float v) { - dir[0] = 1.0; - dir[1] = v; - dir[2] = -u; -} - -void get_dir_1(out vec3 dir, in float u, in float v) { - dir[0] = -1.0; - dir[1] = v; - dir[2] = u; -} - -void get_dir_2(out vec3 dir, in float u, in float v) { - dir[0] = u; - dir[1] = 1.0; - dir[2] = -v; -} - -void get_dir_3(out vec3 dir, in float u, in float v) { - dir[0] = u; - dir[1] = -1.0; - dir[2] = v; -} - -void get_dir_4(out vec3 dir, in float u, in float v) { - dir[0] = u; - dir[1] = v; - dir[2] = 1.0; -} - -void get_dir_5(out vec3 dir, in float u, in float v) { - dir[0] = -u; - dir[1] = v; - dir[2] = -1.0; -} - -float calcWeight(float u, float v) { - float val = u * u + v * v + 1.0; - return val * sqrt(val); -} +#include "cubemap_downsampler_inc.glsl" void main() { uvec3 id = gl_GlobalInvocationID; diff --git a/servers/rendering/renderer_rd/shaders/cubemap_downsampler_inc.glsl b/servers/rendering/renderer_rd/shaders/cubemap_downsampler_inc.glsl new file mode 100644 index 0000000000..b329e67293 --- /dev/null +++ b/servers/rendering/renderer_rd/shaders/cubemap_downsampler_inc.glsl @@ -0,0 +1,48 @@ +layout(push_constant, binding = 1, std430) uniform Params { + uint face_size; + uint face_id; // only used in raster shader +} +params; + +#define M_PI 3.14159265359 + +void get_dir_0(out vec3 dir, in float u, in float v) { + dir[0] = 1.0; + dir[1] = v; + dir[2] = -u; +} + +void get_dir_1(out vec3 dir, in float u, in float v) { + dir[0] = -1.0; + dir[1] = v; + dir[2] = u; +} + +void get_dir_2(out vec3 dir, in float u, in float v) { + dir[0] = u; + dir[1] = 1.0; + dir[2] = -v; +} + +void get_dir_3(out vec3 dir, in float u, in float v) { + dir[0] = u; + dir[1] = -1.0; + dir[2] = v; +} + +void get_dir_4(out vec3 dir, in float u, in float v) { + dir[0] = u; + dir[1] = v; + dir[2] = 1.0; +} + +void get_dir_5(out vec3 dir, in float u, in float v) { + dir[0] = -u; + dir[1] = v; + dir[2] = -1.0; +} + +float calcWeight(float u, float v) { + float val = u * u + v * v + 1.0; + return val * sqrt(val); +} diff --git a/servers/rendering/renderer_rd/shaders/cubemap_downsampler_raster.glsl b/servers/rendering/renderer_rd/shaders/cubemap_downsampler_raster.glsl new file mode 100644 index 0000000000..0828ffd921 --- /dev/null +++ b/servers/rendering/renderer_rd/shaders/cubemap_downsampler_raster.glsl @@ -0,0 +1,163 @@ +// Copyright 2016 Activision Publishing, Inc. +// +// 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. + +/* clang-format off */ +#[vertex] + +#version 450 + +#VERSION_DEFINES + +#include "cubemap_downsampler_inc.glsl" + +layout(location = 0) out vec2 uv_interp; +/* clang-format on */ + +void main() { + vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0)); + uv_interp = base_arr[gl_VertexIndex] * float(params.face_size); + gl_Position = vec4(base_arr[gl_VertexIndex] * 2.0 - 1.0, 0.0, 1.0); +} + +/* clang-format off */ +#[fragment] + +#version 450 + +#VERSION_DEFINES + +#include "cubemap_downsampler_inc.glsl" + +layout(set = 0, binding = 0) uniform samplerCube source_cubemap; + +layout(location = 0) in vec2 uv_interp; +layout(location = 0) out vec4 frag_color; +/* clang-format on */ + +void main() { + // Converted from compute shader which uses absolute coordinates. + // Could possibly simplify this + float face_size = float(params.face_size); + + if (uv_interp.x < face_size && uv_interp.y < face_size) { + float inv_face_size = 1.0 / face_size; + + float u0 = (uv_interp.x * 2.0 + 1.0 - 0.75) * inv_face_size - 1.0; + float u1 = (uv_interp.x * 2.0 + 1.0 + 0.75) * inv_face_size - 1.0; + + float v0 = (uv_interp.y * 2.0 + 1.0 - 0.75) * -inv_face_size + 1.0; + float v1 = (uv_interp.y * 2.0 + 1.0 + 0.75) * -inv_face_size + 1.0; + + float weights[4]; + weights[0] = calcWeight(u0, v0); + weights[1] = calcWeight(u1, v0); + weights[2] = calcWeight(u0, v1); + weights[3] = calcWeight(u1, v1); + + const float wsum = 0.5 / (weights[0] + weights[1] + weights[2] + weights[3]); + for (int i = 0; i < 4; i++) { + weights[i] = weights[i] * wsum + .125; + } + + vec3 dir; + vec4 color; + switch (params.face_id) { + case 0: + get_dir_0(dir, u0, v0); + color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0]; + + get_dir_0(dir, u1, v0); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1]; + + get_dir_0(dir, u0, v1); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2]; + + get_dir_0(dir, u1, v1); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3]; + break; + case 1: + get_dir_1(dir, u0, v0); + color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0]; + + get_dir_1(dir, u1, v0); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1]; + + get_dir_1(dir, u0, v1); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2]; + + get_dir_1(dir, u1, v1); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3]; + break; + case 2: + get_dir_2(dir, u0, v0); + color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0]; + + get_dir_2(dir, u1, v0); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1]; + + get_dir_2(dir, u0, v1); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2]; + + get_dir_2(dir, u1, v1); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3]; + break; + case 3: + get_dir_3(dir, u0, v0); + color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0]; + + get_dir_3(dir, u1, v0); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1]; + + get_dir_3(dir, u0, v1); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2]; + + get_dir_3(dir, u1, v1); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3]; + break; + case 4: + get_dir_4(dir, u0, v0); + color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0]; + + get_dir_4(dir, u1, v0); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1]; + + get_dir_4(dir, u0, v1); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2]; + + get_dir_4(dir, u1, v1); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3]; + break; + default: + get_dir_5(dir, u0, v0); + color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0]; + + get_dir_5(dir, u1, v0); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1]; + + get_dir_5(dir, u0, v1); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2]; + + get_dir_5(dir, u1, v1); + color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3]; + break; + } + frag_color = color; + } +} diff --git a/servers/rendering/renderer_rd/shaders/cubemap_filter_raster.glsl b/servers/rendering/renderer_rd/shaders/cubemap_filter_raster.glsl new file mode 100644 index 0000000000..324d306218 --- /dev/null +++ b/servers/rendering/renderer_rd/shaders/cubemap_filter_raster.glsl @@ -0,0 +1,256 @@ +// Copyright 2016 Activision Publishing, Inc. +// +// 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. + +/* clang-format off */ +#[vertex] + +#version 450 + +#VERSION_DEFINES + +layout(push_constant, binding = 1, std430) uniform Params { + int mip_level; + uint face_id; +} +params; + +layout(location = 0) out vec2 uv_interp; +/* clang-format on */ + +void main() { + vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0)); + uv_interp = base_arr[gl_VertexIndex]; + gl_Position = vec4(base_arr[gl_VertexIndex] * 2.0 - 1.0, 0.0, 1.0); +} + +/* clang-format off */ +#[fragment] + +#version 450 + +#VERSION_DEFINES + +layout(push_constant, binding = 1, std430) uniform Params { + int mip_level; + uint face_id; +} +params; + +layout(set = 0, binding = 0) uniform samplerCube source_cubemap; + +layout(location = 0) in vec2 uv_interp; +layout(location = 0) out vec4 frag_color; + +/* clang-format on */ + +#ifdef USE_HIGH_QUALITY +#define NUM_TAPS 32 +#else +#define NUM_TAPS 8 +#endif + +#define BASE_RESOLUTION 128 + +#ifdef USE_HIGH_QUALITY +layout(set = 1, binding = 0, std430) buffer restrict readonly Data { + vec4[7][5][3][24] coeffs; +} +data; +#else +layout(set = 1, binding = 0, std430) buffer restrict readonly Data { + vec4[7][5][6] coeffs; +} +data; +#endif + +void get_dir(out vec3 dir, in vec2 uv, in uint face) { + switch (face) { + case 0: + dir = vec3(1.0, uv[1], -uv[0]); + break; + case 1: + dir = vec3(-1.0, uv[1], uv[0]); + break; + case 2: + dir = vec3(uv[0], 1.0, -uv[1]); + break; + case 3: + dir = vec3(uv[0], -1.0, uv[1]); + break; + case 4: + dir = vec3(uv[0], uv[1], 1.0); + break; + default: + dir = vec3(-uv[0], uv[1], -1.0); + break; + } +} + +void main() { + // determine dir / pos for the texel + vec3 dir, adir, frameZ; + { + vec2 uv; + uv.x = uv_interp.x; + uv.y = 1.0 - uv_interp.y; + uv = uv * 2.0 - 1.0; + + get_dir(dir, uv, params.face_id); + frameZ = normalize(dir); + + adir = abs(dir); + } + + // determine which texel this is + // NOTE (macOS/MoltenVK): Do not rename, "level" variable name conflicts with the Metal "level(float lod)" mipmap sampling function name. + int mip_level = 0; + + if (params.mip_level < 0) { + // return as is + frag_color.rgb = textureLod(source_cubemap, frameZ, 0.0).rgb; + frag_color.a = 1.0; + return; + } else if (params.mip_level > 6) { + // maximum level + mip_level = 6; + } else { + mip_level = params.mip_level; + } + + // GGX gather colors + vec4 color = vec4(0.0); + for (int axis = 0; axis < 3; axis++) { + const int otherAxis0 = 1 - (axis & 1) - (axis >> 1); + const int otherAxis1 = 2 - (axis >> 1); + + float frameweight = (max(adir[otherAxis0], adir[otherAxis1]) - .75) / .25; + if (frameweight > 0.0) { + // determine frame + vec3 UpVector; + switch (axis) { + case 0: + UpVector = vec3(1, 0, 0); + break; + case 1: + UpVector = vec3(0, 1, 0); + break; + default: + UpVector = vec3(0, 0, 1); + break; + } + + vec3 frameX = normalize(cross(UpVector, frameZ)); + vec3 frameY = cross(frameZ, frameX); + + // calculate parametrization for polynomial + float Nx = dir[otherAxis0]; + float Ny = dir[otherAxis1]; + float Nz = adir[axis]; + + float NmaxXY = max(abs(Ny), abs(Nx)); + Nx /= NmaxXY; + Ny /= NmaxXY; + + float theta; + if (Ny < Nx) { + if (Ny <= -0.999) + theta = Nx; + else + theta = Ny; + } else { + if (Ny >= 0.999) + theta = -Nx; + else + theta = -Ny; + } + + float phi; + if (Nz <= -0.999) + phi = -NmaxXY; + else if (Nz >= 0.999) + phi = NmaxXY; + else + phi = Nz; + + float theta2 = theta * theta; + float phi2 = phi * phi; + + // sample + for (int iSuperTap = 0; iSuperTap < NUM_TAPS / 4; iSuperTap++) { + const int index = (NUM_TAPS / 4) * axis + iSuperTap; + +#ifdef USE_HIGH_QUALITY + vec4 coeffsDir0[3]; + vec4 coeffsDir1[3]; + vec4 coeffsDir2[3]; + vec4 coeffsLevel[3]; + vec4 coeffsWeight[3]; + + for (int iCoeff = 0; iCoeff < 3; iCoeff++) { + coeffsDir0[iCoeff] = data.coeffs[mip_level][0][iCoeff][index]; + coeffsDir1[iCoeff] = data.coeffs[mip_level][1][iCoeff][index]; + coeffsDir2[iCoeff] = data.coeffs[mip_level][2][iCoeff][index]; + coeffsLevel[iCoeff] = data.coeffs[mip_level][3][iCoeff][index]; + coeffsWeight[iCoeff] = data.coeffs[mip_level][4][iCoeff][index]; + } + + for (int iSubTap = 0; iSubTap < 4; iSubTap++) { + // determine sample attributes (dir, weight, mip_level) + vec3 sample_dir = frameX * (coeffsDir0[0][iSubTap] + coeffsDir0[1][iSubTap] * theta2 + coeffsDir0[2][iSubTap] * phi2) + frameY * (coeffsDir1[0][iSubTap] + coeffsDir1[1][iSubTap] * theta2 + coeffsDir1[2][iSubTap] * phi2) + frameZ * (coeffsDir2[0][iSubTap] + coeffsDir2[1][iSubTap] * theta2 + coeffsDir2[2][iSubTap] * phi2); + + float sample_level = coeffsLevel[0][iSubTap] + coeffsLevel[1][iSubTap] * theta2 + coeffsLevel[2][iSubTap] * phi2; + + float sample_weight = coeffsWeight[0][iSubTap] + coeffsWeight[1][iSubTap] * theta2 + coeffsWeight[2][iSubTap] * phi2; +#else + vec4 coeffsDir0 = data.coeffs[mip_level][0][index]; + vec4 coeffsDir1 = data.coeffs[mip_level][1][index]; + vec4 coeffsDir2 = data.coeffs[mip_level][2][index]; + vec4 coeffsLevel = data.coeffs[mip_level][3][index]; + vec4 coeffsWeight = data.coeffs[mip_level][4][index]; + + for (int iSubTap = 0; iSubTap < 4; iSubTap++) { + // determine sample attributes (dir, weight, mip_level) + vec3 sample_dir = frameX * coeffsDir0[iSubTap] + frameY * coeffsDir1[iSubTap] + frameZ * coeffsDir2[iSubTap]; + + float sample_level = coeffsLevel[iSubTap]; + + float sample_weight = coeffsWeight[iSubTap]; +#endif + + sample_weight *= frameweight; + + // adjust for jacobian + sample_dir /= max(abs(sample_dir[0]), max(abs(sample_dir[1]), abs(sample_dir[2]))); + sample_level += 0.75 * log2(dot(sample_dir, sample_dir)); + // sample cubemap + color.xyz += textureLod(source_cubemap, normalize(sample_dir), sample_level).xyz * sample_weight; + color.w += sample_weight; + } + } + } + } + color /= color.w; + + // write color + color.xyz = max(vec3(0.0), color.xyz); + color.w = 1.0; + + frag_color = color; +} diff --git a/servers/rendering/renderer_rd/shaders/cubemap_roughness.glsl b/servers/rendering/renderer_rd/shaders/cubemap_roughness.glsl index ce7c03c1d4..28f4dc59ec 100644 --- a/servers/rendering/renderer_rd/shaders/cubemap_roughness.glsl +++ b/servers/rendering/renderer_rd/shaders/cubemap_roughness.glsl @@ -12,100 +12,7 @@ layout(set = 0, binding = 0) uniform samplerCube source_cube; layout(rgba16f, set = 1, binding = 0) uniform restrict writeonly imageCube dest_cubemap; -layout(push_constant, binding = 1, std430) uniform Params { - uint face_id; - uint sample_count; - float roughness; - bool use_direct_write; - float face_size; -} -params; - -#define M_PI 3.14159265359 - -vec3 texelCoordToVec(vec2 uv, uint faceID) { - mat3 faceUvVectors[6]; - - // -x - faceUvVectors[1][0] = vec3(0.0, 0.0, 1.0); // u -> +z - faceUvVectors[1][1] = vec3(0.0, -1.0, 0.0); // v -> -y - faceUvVectors[1][2] = vec3(-1.0, 0.0, 0.0); // -x face - - // +x - faceUvVectors[0][0] = vec3(0.0, 0.0, -1.0); // u -> -z - faceUvVectors[0][1] = vec3(0.0, -1.0, 0.0); // v -> -y - faceUvVectors[0][2] = vec3(1.0, 0.0, 0.0); // +x face - - // -y - faceUvVectors[3][0] = vec3(1.0, 0.0, 0.0); // u -> +x - faceUvVectors[3][1] = vec3(0.0, 0.0, -1.0); // v -> -z - faceUvVectors[3][2] = vec3(0.0, -1.0, 0.0); // -y face - - // +y - faceUvVectors[2][0] = vec3(1.0, 0.0, 0.0); // u -> +x - faceUvVectors[2][1] = vec3(0.0, 0.0, 1.0); // v -> +z - faceUvVectors[2][2] = vec3(0.0, 1.0, 0.0); // +y face - - // -z - faceUvVectors[5][0] = vec3(-1.0, 0.0, 0.0); // u -> -x - faceUvVectors[5][1] = vec3(0.0, -1.0, 0.0); // v -> -y - faceUvVectors[5][2] = vec3(0.0, 0.0, -1.0); // -z face - - // +z - faceUvVectors[4][0] = vec3(1.0, 0.0, 0.0); // u -> +x - faceUvVectors[4][1] = vec3(0.0, -1.0, 0.0); // v -> -y - faceUvVectors[4][2] = vec3(0.0, 0.0, 1.0); // +z face - - // out = u * s_faceUv[0] + v * s_faceUv[1] + s_faceUv[2]. - vec3 result = (faceUvVectors[faceID][0] * uv.x) + (faceUvVectors[faceID][1] * uv.y) + faceUvVectors[faceID][2]; - return normalize(result); -} - -vec3 ImportanceSampleGGX(vec2 Xi, float Roughness, vec3 N) { - float a = Roughness * Roughness; // DISNEY'S ROUGHNESS [see Burley'12 siggraph] - - // Compute distribution direction - float Phi = 2.0 * M_PI * Xi.x; - float CosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a * a - 1.0) * Xi.y)); - float SinTheta = sqrt(1.0 - CosTheta * CosTheta); - - // Convert to spherical direction - vec3 H; - H.x = SinTheta * cos(Phi); - H.y = SinTheta * sin(Phi); - H.z = CosTheta; - - vec3 UpVector = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); - vec3 TangentX = normalize(cross(UpVector, N)); - vec3 TangentY = cross(N, TangentX); - - // Tangent to world space - return TangentX * H.x + TangentY * H.y + N * H.z; -} - -// http://graphicrants.blogspot.com.au/2013/08/specular-brdf-reference.html -float GGX(float NdotV, float a) { - float k = a / 2.0; - return NdotV / (NdotV * (1.0 - k) + k); -} - -// http://graphicrants.blogspot.com.au/2013/08/specular-brdf-reference.html -float G_Smith(float a, float nDotV, float nDotL) { - return GGX(nDotL, a * a) * GGX(nDotV, a * a); -} - -float radicalInverse_VdC(uint bits) { - bits = (bits << 16u) | (bits >> 16u); - bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); - bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); - bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); - bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); - return float(bits) * 2.3283064365386963e-10; // / 0x100000000 -} - -vec2 Hammersley(uint i, uint N) { - return vec2(float(i) / float(N), radicalInverse_VdC(i)); -} +#include "cubemap_roughness_inc.glsl" void main() { uvec3 id = gl_GlobalInvocationID; diff --git a/servers/rendering/renderer_rd/shaders/cubemap_roughness_inc.glsl b/servers/rendering/renderer_rd/shaders/cubemap_roughness_inc.glsl new file mode 100644 index 0000000000..80c0ac4fb4 --- /dev/null +++ b/servers/rendering/renderer_rd/shaders/cubemap_roughness_inc.glsl @@ -0,0 +1,94 @@ +#define M_PI 3.14159265359 + +layout(push_constant, binding = 1, std430) uniform Params { + uint face_id; + uint sample_count; + float roughness; + bool use_direct_write; + float face_size; +} +params; + +vec3 texelCoordToVec(vec2 uv, uint faceID) { + mat3 faceUvVectors[6]; + + // -x + faceUvVectors[1][0] = vec3(0.0, 0.0, 1.0); // u -> +z + faceUvVectors[1][1] = vec3(0.0, -1.0, 0.0); // v -> -y + faceUvVectors[1][2] = vec3(-1.0, 0.0, 0.0); // -x face + + // +x + faceUvVectors[0][0] = vec3(0.0, 0.0, -1.0); // u -> -z + faceUvVectors[0][1] = vec3(0.0, -1.0, 0.0); // v -> -y + faceUvVectors[0][2] = vec3(1.0, 0.0, 0.0); // +x face + + // -y + faceUvVectors[3][0] = vec3(1.0, 0.0, 0.0); // u -> +x + faceUvVectors[3][1] = vec3(0.0, 0.0, -1.0); // v -> -z + faceUvVectors[3][2] = vec3(0.0, -1.0, 0.0); // -y face + + // +y + faceUvVectors[2][0] = vec3(1.0, 0.0, 0.0); // u -> +x + faceUvVectors[2][1] = vec3(0.0, 0.0, 1.0); // v -> +z + faceUvVectors[2][2] = vec3(0.0, 1.0, 0.0); // +y face + + // -z + faceUvVectors[5][0] = vec3(-1.0, 0.0, 0.0); // u -> -x + faceUvVectors[5][1] = vec3(0.0, -1.0, 0.0); // v -> -y + faceUvVectors[5][2] = vec3(0.0, 0.0, -1.0); // -z face + + // +z + faceUvVectors[4][0] = vec3(1.0, 0.0, 0.0); // u -> +x + faceUvVectors[4][1] = vec3(0.0, -1.0, 0.0); // v -> -y + faceUvVectors[4][2] = vec3(0.0, 0.0, 1.0); // +z face + + // out = u * s_faceUv[0] + v * s_faceUv[1] + s_faceUv[2]. + vec3 result = (faceUvVectors[faceID][0] * uv.x) + (faceUvVectors[faceID][1] * uv.y) + faceUvVectors[faceID][2]; + return normalize(result); +} + +vec3 ImportanceSampleGGX(vec2 Xi, float Roughness, vec3 N) { + float a = Roughness * Roughness; // DISNEY'S ROUGHNESS [see Burley'12 siggraph] + + // Compute distribution direction + float Phi = 2.0 * M_PI * Xi.x; + float CosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a * a - 1.0) * Xi.y)); + float SinTheta = sqrt(1.0 - CosTheta * CosTheta); + + // Convert to spherical direction + vec3 H; + H.x = SinTheta * cos(Phi); + H.y = SinTheta * sin(Phi); + H.z = CosTheta; + + vec3 UpVector = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); + vec3 TangentX = normalize(cross(UpVector, N)); + vec3 TangentY = cross(N, TangentX); + + // Tangent to world space + return TangentX * H.x + TangentY * H.y + N * H.z; +} + +// http://graphicrants.blogspot.com.au/2013/08/specular-brdf-reference.html +float GGX(float NdotV, float a) { + float k = a / 2.0; + return NdotV / (NdotV * (1.0 - k) + k); +} + +// http://graphicrants.blogspot.com.au/2013/08/specular-brdf-reference.html +float G_Smith(float a, float nDotV, float nDotL) { + return GGX(nDotL, a * a) * GGX(nDotV, a * a); +} + +float radicalInverse_VdC(uint bits) { + bits = (bits << 16u) | (bits >> 16u); + bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); + bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); + bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); + bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); + return float(bits) * 2.3283064365386963e-10; // / 0x100000000 +} + +vec2 Hammersley(uint i, uint N) { + return vec2(float(i) / float(N), radicalInverse_VdC(i)); +} diff --git a/servers/rendering/renderer_rd/shaders/cubemap_roughness_raster.glsl b/servers/rendering/renderer_rd/shaders/cubemap_roughness_raster.glsl new file mode 100644 index 0000000000..2570308816 --- /dev/null +++ b/servers/rendering/renderer_rd/shaders/cubemap_roughness_raster.glsl @@ -0,0 +1,63 @@ +/* clang-format off */ +#[vertex] + +#version 450 + +#VERSION_DEFINES + +#include "cubemap_roughness_inc.glsl" + +layout(location = 0) out vec2 uv_interp; +/* clang-format on */ + +void main() { + vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0)); + uv_interp = base_arr[gl_VertexIndex]; + gl_Position = vec4(uv_interp * 2.0 - 1.0, 0.0, 1.0); +} + +/* clang-format off */ +#[fragment] + +#version 450 + +#VERSION_DEFINES + +#include "cubemap_roughness_inc.glsl" + +layout(location = 0) in vec2 uv_interp; + +layout(set = 0, binding = 0) uniform samplerCube source_cube; + +layout(location = 0) out vec4 frag_color; +/* clang-format on */ + +void main() { + vec3 N = texelCoordToVec(uv_interp * 2.0 - 1.0, params.face_id); + + //vec4 color = color_interp; + + if (params.use_direct_write) { + frag_color = vec4(texture(source_cube, N).rgb, 1.0); + } else { + vec4 sum = vec4(0.0, 0.0, 0.0, 0.0); + + for (uint sampleNum = 0u; sampleNum < params.sample_count; sampleNum++) { + vec2 xi = Hammersley(sampleNum, params.sample_count); + + vec3 H = ImportanceSampleGGX(xi, params.roughness, N); + vec3 V = N; + vec3 L = (2.0 * dot(V, H) * H - V); + + float ndotl = clamp(dot(N, L), 0.0, 1.0); + + if (ndotl > 0.0) { + sum.rgb += textureLod(source_cube, L, 0.0).rgb * ndotl; + sum.a += ndotl; + } + } + sum /= sum.a; + + frag_color = vec4(sum.rgb, 1.0); + } +} |