diff options
Diffstat (limited to 'scene')
57 files changed, 858 insertions, 332 deletions
diff --git a/scene/2d/audio_stream_player_2d.h b/scene/2d/audio_stream_player_2d.h index 616d7fdb60..5bc9083488 100644 --- a/scene/2d/audio_stream_player_2d.h +++ b/scene/2d/audio_stream_player_2d.h @@ -82,7 +82,7 @@ private: float attenuation = 1.0; float panning_strength = 1.0f; - float cached_global_panning_strength = 1.0f; + float cached_global_panning_strength = 0.5f; protected: void _validate_property(PropertyInfo &p_property) const; diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp index e18f2bd1a1..c8f5d7f5a6 100644 --- a/scene/2d/gpu_particles_2d.cpp +++ b/scene/2d/gpu_particles_2d.cpp @@ -299,10 +299,6 @@ bool GPUParticles2D::get_interpolate() const { PackedStringArray GPUParticles2D::get_configuration_warnings() const { PackedStringArray warnings = Node2D::get_configuration_warnings(); - if (RenderingServer::get_singleton()->is_low_end()) { - warnings.push_back(RTR("GPU-based particles are not supported by the OpenGL video driver.\nUse the CPUParticles2D node instead. You can use the \"Convert to CPUParticles2D\" option for this purpose.")); - } - if (process_material.is_null()) { warnings.push_back(RTR("A material to process the particles is not assigned, so no behavior is imprinted.")); } else { diff --git a/scene/2d/gpu_particles_2d.h b/scene/2d/gpu_particles_2d.h index 7fba174357..94555e0bb0 100644 --- a/scene/2d/gpu_particles_2d.h +++ b/scene/2d/gpu_particles_2d.h @@ -64,7 +64,7 @@ private: #endif Ref<Material> process_material; - DrawOrder draw_order; + DrawOrder draw_order = DRAW_ORDER_LIFETIME; Ref<Texture2D> texture; diff --git a/scene/2d/joint_2d.cpp b/scene/2d/joint_2d.cpp index 6000508f36..8de4c281f4 100644 --- a/scene/2d/joint_2d.cpp +++ b/scene/2d/joint_2d.cpp @@ -133,7 +133,13 @@ void Joint2D::set_node_a(const NodePath &p_node_a) { } a = p_node_a; - _update_joint(); + if (Engine::get_singleton()->is_editor_hint()) { + // When in editor, the setter may be called as a result of node rename. + // It happens before the node actually changes its name, which triggers false warning. + callable_mp(this, &Joint2D::_update_joint).call_deferred(); + } else { + _update_joint(); + } } NodePath Joint2D::get_node_a() const { @@ -150,7 +156,11 @@ void Joint2D::set_node_b(const NodePath &p_node_b) { } b = p_node_b; - _update_joint(); + if (Engine::get_singleton()->is_editor_hint()) { + callable_mp(this, &Joint2D::_update_joint).call_deferred(); + } else { + _update_joint(); + } } NodePath Joint2D::get_node_b() const { diff --git a/scene/2d/navigation_link_2d.h b/scene/2d/navigation_link_2d.h index 2a5092216d..9d36f80dd6 100644 --- a/scene/2d/navigation_link_2d.h +++ b/scene/2d/navigation_link_2d.h @@ -37,11 +37,11 @@ class NavigationLink2D : public Node2D { GDCLASS(NavigationLink2D, Node2D); bool enabled = true; - RID link = RID(); + RID link; bool bidirectional = true; uint32_t navigation_layers = 1; - Vector2 end_location = Vector2(); - Vector2 start_location = Vector2(); + Vector2 end_location; + Vector2 start_location; real_t enter_cost = 0.0; real_t travel_cost = 1.0; diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp index 02d8e20ce8..62787d4488 100644 --- a/scene/2d/skeleton_2d.cpp +++ b/scene/2d/skeleton_2d.cpp @@ -213,7 +213,7 @@ void Bone2D::_notification(int p_what) { } // Undo scaling - Transform2D editor_gizmo_trans = Transform2D(); + Transform2D editor_gizmo_trans; editor_gizmo_trans.set_scale(Vector2(1, 1) / get_global_scale()); RenderingServer::get_singleton()->canvas_item_set_transform(editor_gizmo_rid, editor_gizmo_trans); diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h index 7f3e241871..eaf100631e 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -115,7 +115,7 @@ public: class TerrainConstraint { private: const TileMap *tile_map; - Vector2i base_cell_coords = Vector2i(); + Vector2i base_cell_coords; int bit = -1; int terrain = -1; diff --git a/scene/3d/audio_stream_player_3d.h b/scene/3d/audio_stream_player_3d.h index 913cc9fc00..806b250ba7 100644 --- a/scene/3d/audio_stream_player_3d.h +++ b/scene/3d/audio_stream_player_3d.h @@ -117,7 +117,7 @@ private: float _get_attenuation_db(float p_distance) const; float panning_strength = 1.0f; - float cached_global_panning_strength = 1.0f; + float cached_global_panning_strength = 0.5f; protected: void _validate_property(PropertyInfo &p_property) const; diff --git a/scene/3d/bone_attachment_3d.cpp b/scene/3d/bone_attachment_3d.cpp index d8524a7392..9cf10dbef1 100644 --- a/scene/3d/bone_attachment_3d.cpp +++ b/scene/3d/bone_attachment_3d.cpp @@ -375,7 +375,7 @@ void BoneAttachment3D::on_bone_pose_update(int p_bone_index) { } } #ifdef TOOLS_ENABLED -void BoneAttachment3D::_notify_skeleton_bones_renamed(Node *p_base_scene, Skeleton3D *p_skeleton, Ref<BoneMap> p_bone_map) { +void BoneAttachment3D::_notify_skeleton_bones_renamed(Node *p_base_scene, Skeleton3D *p_skeleton, Dictionary p_rename_map) { const Skeleton3D *parent = nullptr; if (use_external_skeleton) { if (external_skeleton_node_cache.is_valid()) { @@ -385,7 +385,7 @@ void BoneAttachment3D::_notify_skeleton_bones_renamed(Node *p_base_scene, Skelet parent = Object::cast_to<Skeleton3D>(get_parent()); } if (parent && parent == p_skeleton) { - StringName bn = p_bone_map->find_profile_bone_name(bone_name); + StringName bn = p_rename_map[bone_name]; if (bn) { set_bone_name(bn); } diff --git a/scene/3d/bone_attachment_3d.h b/scene/3d/bone_attachment_3d.h index 2db6ba6268..81338b30e9 100644 --- a/scene/3d/bone_attachment_3d.h +++ b/scene/3d/bone_attachment_3d.h @@ -72,7 +72,7 @@ protected: static void _bind_methods(); #ifdef TOOLS_ENABLED - virtual void _notify_skeleton_bones_renamed(Node *p_base_scene, Skeleton3D *p_skeleton, Ref<BoneMap> p_bone_map); + virtual void _notify_skeleton_bones_renamed(Node *p_base_scene, Skeleton3D *p_skeleton, Dictionary p_rename_map); #endif // TOOLS_ENABLED public: diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp index 7de685b469..5ac8535bb6 100644 --- a/scene/3d/cpu_particles_3d.cpp +++ b/scene/3d/cpu_particles_3d.cpp @@ -867,7 +867,7 @@ void CPUParticles3D::_particles_process(double p_delta) { real_t ring_random_angle = Math::randf() * Math_TAU; real_t ring_random_radius = Math::randf() * (emission_ring_radius - emission_ring_inner_radius) + emission_ring_inner_radius; Vector3 axis = emission_ring_axis.normalized(); - Vector3 ortho_axis = Vector3(); + Vector3 ortho_axis; if (axis == Vector3(1.0, 0.0, 0.0)) { ortho_axis = Vector3(0.0, 1.0, 0.0).cross(axis); } else { diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp index 9d04202c2d..17dfe2610e 100644 --- a/scene/3d/gpu_particles_3d.cpp +++ b/scene/3d/gpu_particles_3d.cpp @@ -272,10 +272,6 @@ bool GPUParticles3D::get_interpolate() const { PackedStringArray GPUParticles3D::get_configuration_warnings() const { PackedStringArray warnings = GeometryInstance3D::get_configuration_warnings(); - if (RenderingServer::get_singleton()->is_low_end()) { - warnings.push_back(RTR("GPU-based particles are not supported by the OpenGL video driver.\nUse the CPUParticles3D node instead. You can use the \"Convert to CPUParticles3D\" option for this purpose.")); - } - bool meshes_found = false; bool anim_material_found = false; diff --git a/scene/3d/gpu_particles_3d.h b/scene/3d/gpu_particles_3d.h index 835d71862a..d1768436e5 100644 --- a/scene/3d/gpu_particles_3d.h +++ b/scene/3d/gpu_particles_3d.h @@ -82,7 +82,7 @@ private: Ref<Material> process_material; - DrawOrder draw_order; + DrawOrder draw_order = DRAW_ORDER_INDEX; Vector<Ref<Mesh>> draw_passes; Ref<Skin> skin; diff --git a/scene/3d/navigation_link_3d.h b/scene/3d/navigation_link_3d.h index 1fc03546fa..46bc6318b8 100644 --- a/scene/3d/navigation_link_3d.h +++ b/scene/3d/navigation_link_3d.h @@ -37,11 +37,11 @@ class NavigationLink3D : public Node3D { GDCLASS(NavigationLink3D, Node3D); bool enabled = true; - RID link = RID(); + RID link; bool bidirectional = true; uint32_t navigation_layers = 1; - Vector3 end_location = Vector3(); - Vector3 start_location = Vector3(); + Vector3 end_location; + Vector3 start_location; real_t enter_cost = 0.0; real_t travel_cost = 1.0; diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp index a1f962c690..b205c2cde0 100644 --- a/scene/3d/skeleton_3d.cpp +++ b/scene/3d/skeleton_3d.cpp @@ -541,7 +541,7 @@ void Skeleton3D::set_bone_name(int p_bone, const String &p_name) { for (int i = 0; i < bone_size; i++) { if (i != p_bone) { - ERR_FAIL_COND(bones[i].name == p_name); + ERR_FAIL_COND_MSG(bones[i].name == p_name, "Skeleton3D: '" + get_name() + "', bone name: '" + p_name + "' is already exist."); } } diff --git a/scene/debugger/scene_debugger.cpp b/scene/debugger/scene_debugger.cpp index b4bdda9ecb..599a759b08 100644 --- a/scene/debugger/scene_debugger.cpp +++ b/scene/debugger/scene_debugger.cpp @@ -39,87 +39,10 @@ #include "scene/main/window.h" #include "scene/resources/packed_scene.h" -Array SceneDebugger::RPCProfilerFrame::serialize() { - Array arr; - arr.push_back(infos.size() * 4); - for (int i = 0; i < infos.size(); ++i) { - arr.push_back(uint64_t(infos[i].node)); - arr.push_back(infos[i].node_path); - arr.push_back(infos[i].incoming_rpc); - arr.push_back(infos[i].outgoing_rpc); - } - return arr; -} - -bool SceneDebugger::RPCProfilerFrame::deserialize(const Array &p_arr) { - ERR_FAIL_COND_V(p_arr.size() < 1, false); - uint32_t size = p_arr[0]; - ERR_FAIL_COND_V(size % 4, false); - ERR_FAIL_COND_V((uint32_t)p_arr.size() != size + 1, false); - infos.resize(size / 4); - int idx = 1; - for (uint32_t i = 0; i < size / 4; ++i) { - infos.write[i].node = uint64_t(p_arr[idx]); - infos.write[i].node_path = p_arr[idx + 1]; - infos.write[i].incoming_rpc = p_arr[idx + 2]; - infos.write[i].outgoing_rpc = p_arr[idx + 3]; - } - return true; -} - -class SceneDebugger::RPCProfiler : public EngineProfiler { - HashMap<ObjectID, RPCNodeInfo> rpc_node_data; - uint64_t last_profile_time = 0; - - void init_node(const ObjectID p_node) { - if (rpc_node_data.has(p_node)) { - return; - } - rpc_node_data.insert(p_node, RPCNodeInfo()); - rpc_node_data[p_node].node = p_node; - rpc_node_data[p_node].node_path = Object::cast_to<Node>(ObjectDB::get_instance(p_node))->get_path(); - rpc_node_data[p_node].incoming_rpc = 0; - rpc_node_data[p_node].outgoing_rpc = 0; - } - -public: - void toggle(bool p_enable, const Array &p_opts) { - rpc_node_data.clear(); - } - - void add(const Array &p_data) { - ERR_FAIL_COND(p_data.size() < 2); - const ObjectID id = p_data[0]; - const String what = p_data[1]; - init_node(id); - RPCNodeInfo &info = rpc_node_data[id]; - if (what == "rpc_in") { - info.incoming_rpc++; - } else if (what == "rpc_out") { - info.outgoing_rpc++; - } - } - - void tick(double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time) { - uint64_t pt = OS::get_singleton()->get_ticks_msec(); - if (pt - last_profile_time > 100) { - last_profile_time = pt; - RPCProfilerFrame frame; - for (const KeyValue<ObjectID, RPCNodeInfo> &E : rpc_node_data) { - frame.infos.push_back(E.value); - } - rpc_node_data.clear(); - EngineDebugger::get_singleton()->send_message("multiplayer:rpc", frame.serialize()); - } - } -}; - SceneDebugger *SceneDebugger::singleton = nullptr; SceneDebugger::SceneDebugger() { singleton = this; - rpc_profiler.instantiate(); - rpc_profiler->bind("rpc"); #ifdef DEBUG_ENABLED LiveEditor::singleton = memnew(LiveEditor); EngineDebugger::register_message_capture("scene", EngineDebugger::Capture(nullptr, SceneDebugger::parse_message)); @@ -264,7 +187,7 @@ Error SceneDebugger::parse_message(void *p_user, const String &p_msg, const Arra ERR_FAIL_COND_V(p_args.size() < 3, ERR_INVALID_DATA); live_editor->_create_node_func(p_args[0], p_args[1], p_args[2]); - } else if (p_msg == "live_instance_node") { + } else if (p_msg == "live_instantiate_node") { ERR_FAIL_COND_V(p_args.size() < 3, ERR_INVALID_DATA); live_editor->_instance_node_func(p_args[0], p_args[1], p_args[2]); diff --git a/scene/debugger/scene_debugger.h b/scene/debugger/scene_debugger.h index fe35446aae..0428bfcc2e 100644 --- a/scene/debugger/scene_debugger.h +++ b/scene/debugger/scene_debugger.h @@ -42,28 +42,9 @@ class Node; class SceneDebugger { public: - // RPC profiler - struct RPCNodeInfo { - ObjectID node; - String node_path; - int incoming_rpc = 0; - int outgoing_rpc = 0; - }; - - struct RPCProfilerFrame { - Vector<RPCNodeInfo> infos; - - Array serialize(); - bool deserialize(const Array &p_arr); - }; - private: - class RPCProfiler; - static SceneDebugger *singleton; - Ref<RPCProfiler> rpc_profiler; - SceneDebugger(); public: diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp index c2b82e01d1..0e7bc5c306 100644 --- a/scene/gui/button.cpp +++ b/scene/gui/button.cpp @@ -231,7 +231,7 @@ void Button::_notification(int p_what) { _icon = icon; } - Rect2 icon_region = Rect2(); + Rect2 icon_region; HorizontalAlignment icon_align_rtl_checked = icon_alignment; HorizontalAlignment align_rtl_checked = alignment; // Swap icon and text alignment sides if right-to-left layout is set. diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp index ea310f5a12..9e0dc049e5 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -1425,7 +1425,10 @@ bool CodeEdit::is_line_numbers_zero_padded() const { } void CodeEdit::_line_number_draw_callback(int p_line, int p_gutter, const Rect2 &p_region) { - String fc = TS->format_number(String::num(p_line + 1).lpad(line_number_digits, line_number_padding)); + String fc = String::num(p_line + 1).lpad(line_number_digits, line_number_padding); + if (is_localizing_numeral_system()) { + fc = TS->format_number(fc); + } Ref<TextLine> tl; tl.instantiate(); tl->add_string(fc, font, font_size); diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index c5cb7157e8..4e76f72921 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -2773,6 +2773,20 @@ bool Control::is_layout_rtl() const { return data.is_rtl; } +void Control::set_localize_numeral_system(bool p_enable) { + if (p_enable == data.localize_numeral_system) { + return; + } + + data.localize_numeral_system = p_enable; + + notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED); +} + +bool Control::is_localizing_numeral_system() const { + return data.localize_numeral_system; +} + void Control::set_auto_translate(bool p_enable) { if (p_enable == data.auto_translate) { return; @@ -3154,6 +3168,9 @@ void Control::_bind_methods() { ClassDB::bind_method(D_METHOD("set_auto_translate", "enable"), &Control::set_auto_translate); ClassDB::bind_method(D_METHOD("is_auto_translating"), &Control::is_auto_translating); + ClassDB::bind_method(D_METHOD("set_localize_numeral_system", "enable"), &Control::set_localize_numeral_system); + ClassDB::bind_method(D_METHOD("is_localizing_numeral_system"), &Control::is_localizing_numeral_system); + ADD_GROUP("Layout", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_contents"), "set_clip_contents", "is_clipping_contents"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "custom_minimum_size", PROPERTY_HINT_NONE, "suffix:px"), "set_custom_minimum_size", "get_custom_minimum_size"); @@ -3198,8 +3215,9 @@ void Control::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "size_flags_vertical", PROPERTY_HINT_FLAGS, "Fill:1,Expand:2,Shrink Center:4,Shrink End:8"), "set_v_size_flags", "get_v_size_flags"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size_flags_stretch_ratio", PROPERTY_HINT_RANGE, "0,20,0.01,or_greater"), "set_stretch_ratio", "get_stretch_ratio"); - ADD_GROUP("Auto Translate", ""); + ADD_GROUP("Localization", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_translate"), "set_auto_translate", "is_auto_translating"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "localize_numeral_system"), "set_localize_numeral_system", "is_localizing_numeral_system"); ADD_GROUP("Tooltip", "tooltip_"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "tooltip_text", PROPERTY_HINT_MULTILINE_TEXT), "set_tooltip_text", "get_tooltip_text"); diff --git a/scene/gui/control.h b/scene/gui/control.h index 72e870930d..3e9bb48a4a 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -249,6 +249,7 @@ private: bool is_rtl = false; bool auto_translate = true; + bool localize_numeral_system = true; // Extra properties. @@ -595,6 +596,9 @@ public: LayoutDirection get_layout_direction() const; virtual bool is_layout_rtl() const; + void set_localize_numeral_system(bool p_enable); + bool is_localizing_numeral_system() const; + void set_auto_translate(bool p_enable); bool is_auto_translating() const; _FORCE_INLINE_ String atr(const String p_string) const { diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index cade65108c..11a3803b35 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -632,8 +632,11 @@ void FileDialog::update_file_list() { files.pop_front(); } - if (tree->get_root() && tree->get_root()->get_first_child() && tree->get_selected() == nullptr) { - tree->get_root()->get_first_child()->select(0); + if (mode != FILE_MODE_SAVE_FILE) { + // Select the first file from list if nothing is selected. + if (tree->get_root() && tree->get_root()->get_first_child() && tree->get_selected() == nullptr) { + tree->get_root()->get_first_child()->select(0); + } } } diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp index 0940b4c07b..6d0bbdd6af 100644 --- a/scene/gui/option_button.cpp +++ b/scene/gui/option_button.cpp @@ -491,9 +491,9 @@ void OptionButton::show_popup() { return; } - Size2 size = get_size() * get_viewport()->get_canvas_transform().get_scale(); - popup->set_position(get_screen_position() + Size2(0, size.height * get_global_transform().get_scale().y)); - popup->set_size(Size2(size.width, 0)); + Size2 button_size = get_global_transform_with_canvas().get_scale() * get_size(); + popup->set_position(get_screen_position() + Size2(0, button_size.height)); + popup->set_size(Size2i(button_size.width, 0)); // If not triggered by the mouse, start the popup with the checked item (or the first enabled one) focused. if (current != NONE_SELECTED && !popup->is_item_disabled(current)) { diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index 82f56a56c9..ab74979777 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -558,7 +558,7 @@ void PopupMenu::_draw_items() { check_ofs += theme_cache.h_separation; } - Point2 ofs = Point2(); + Point2 ofs; // Loop through all items and draw each. for (int i = 0; i < items.size(); i++) { diff --git a/scene/gui/progress_bar.cpp b/scene/gui/progress_bar.cpp index 8369eaa227..50bcfa6a0c 100644 --- a/scene/gui/progress_bar.cpp +++ b/scene/gui/progress_bar.cpp @@ -103,7 +103,12 @@ void ProgressBar::_notification(int p_what) { } if (show_percentage) { - String txt = TS->format_number(itos(int(get_as_ratio() * 100))) + TS->percent_sign(); + String txt = itos(int(get_as_ratio() * 100)); + if (is_localizing_numeral_system()) { + txt = TS->format_number(txt) + TS->percent_sign(); + } else { + txt += String("%"); + } TextLine tl = TextLine(txt, theme_cache.font, theme_cache.font_size); Vector2 text_pos = (Point2(get_size().width - tl.get_size().x, get_size().height - tl.get_size().y) / 2).round(); diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 7f487175dc..889610e071 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -752,7 +752,10 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o prefix = _prefix; break; } else if (list_items[i]->list_type == LIST_NUMBERS) { - segment = TS->format_number(itos(list_index[i]), _find_language(l.from)); + segment = itos(list_index[i]); + if (is_localizing_numeral_system()) { + segment = TS->format_number(segment, _find_language(l.from)); + } } else if (list_items[i]->list_type == LIST_LETTERS) { segment = _letters(list_index[i], list_items[i]->capitalize); } else if (list_items[i]->list_type == LIST_ROMAN) { @@ -2686,6 +2689,7 @@ bool RichTextLabel::_validate_line_caches() { int ctrl_height = get_size().height; // Update fonts. + float old_scroll = vscroll->get_value(); if (main->first_invalid_font_line.load() != (int)main->lines.size()) { for (int i = main->first_invalid_font_line.load(); i < (int)main->lines.size(); i++) { _update_line_font(main, i, theme_cache.normal_font, theme_cache.normal_font_size); @@ -2695,6 +2699,7 @@ bool RichTextLabel::_validate_line_caches() { } if (main->first_resized_line.load() == (int)main->lines.size()) { + vscroll->set_value(old_scroll); return true; } @@ -2733,6 +2738,8 @@ bool RichTextLabel::_validate_line_caches() { vscroll->set_page(text_rect.size.height); if (scroll_follow && scroll_following) { vscroll->set_value(total_height); + } else { + vscroll->set_value(old_scroll); } updating_scroll = false; @@ -4057,7 +4064,7 @@ void RichTextLabel::append_text(const String &p_bbcode) { Color color = theme_cache.default_color; Color outline_color = theme_cache.font_outline_color; int outline_size = theme_cache.outline_size; - Rect2 dropcap_margins = Rect2(); + Rect2 dropcap_margins; for (int i = 0; i < subtag.size(); i++) { Vector<String> subtag_a = subtag[i].split("="); @@ -5716,11 +5723,11 @@ Ref<RichTextEffect> RichTextLabel::_get_custom_effect_by_code(String p_bbcode_id } Dictionary RichTextLabel::parse_expressions_for_values(Vector<String> p_expressions) { - Dictionary d = Dictionary(); + Dictionary d; for (int i = 0; i < p_expressions.size(); i++) { String expression = p_expressions[i]; - Array a = Array(); + Array a; Vector<String> parts = expression.split("=", true); String key = parts[0]; if (parts.size() != 2) { diff --git a/scene/gui/scroll_bar.h b/scene/gui/scroll_bar.h index 13ca62d7ff..d62acd52af 100644 --- a/scene/gui/scroll_bar.h +++ b/scene/gui/scroll_bar.h @@ -72,7 +72,7 @@ class ScrollBar : public Range { NodePath drag_node_path; bool drag_node_enabled = true; - Vector2 drag_node_speed = Vector2(); + Vector2 drag_node_speed; Vector2 drag_node_accum; Vector2 drag_node_from; Vector2 last_drag_node_accum; diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp index c4000120c8..e15b3b7bd4 100644 --- a/scene/gui/spin_box.cpp +++ b/scene/gui/spin_box.cpp @@ -40,7 +40,10 @@ Size2 SpinBox::get_minimum_size() const { } void SpinBox::_value_changed(double p_value) { - String value = TS->format_number(String::num(get_value(), Math::range_step_decimals(get_step()))); + String value = String::num(get_value(), Math::range_step_decimals(get_step())); + if (is_localizing_numeral_system()) { + value = TS->format_number(value); + } if (!line_edit->has_focus()) { if (!prefix.is_empty()) { diff --git a/scene/gui/texture_button.cpp b/scene/gui/texture_button.cpp index d9ab1c2c55..ad12757921 100644 --- a/scene/gui/texture_button.cpp +++ b/scene/gui/texture_button.cpp @@ -64,7 +64,7 @@ Size2 TextureButton::get_minimum_size() const { bool TextureButton::has_point(const Point2 &p_point) const { if (click_mask.is_valid()) { Point2 point = p_point; - Rect2 rect = Rect2(); + Rect2 rect; Size2 mask_size = click_mask->get_size(); if (!_position_rect.has_area()) { diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 1c96858da7..2da76883b4 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -4338,6 +4338,12 @@ TreeItem *Tree::get_selected() const { return selected_item; } +void Tree::set_selected(TreeItem *p_item, int p_column) { + ERR_FAIL_INDEX(p_column, columns.size()); + ERR_FAIL_COND(!p_item); + select_single_item(p_item, get_root(), p_column); +} + int Tree::get_selected_column() const { return selected_col; } @@ -4547,6 +4553,7 @@ void Tree::ensure_cursor_is_visible() { return; // Nothing under cursor. } + // Note: Code below similar to Tree::scroll_to_item(), in case of bug fix both. const Size2 area_size = get_size() - theme_cache.panel_style->get_minimum_size(); int y_offset = get_item_offset(selected_item); @@ -4555,7 +4562,10 @@ void Tree::ensure_cursor_is_visible() { y_offset -= tbh; const int cell_h = compute_item_height(selected_item) + theme_cache.vseparation; - const int screen_h = area_size.height - h_scroll->get_combined_minimum_size().height - tbh; + int screen_h = area_size.height - tbh; + if (h_scroll->is_visible()) { + screen_h -= h_scroll->get_combined_minimum_size().height; + } if (cell_h > screen_h) { // Screen size is too small, maybe it was not resized yet. v_scroll->set_value(y_offset); @@ -4706,26 +4716,32 @@ Point2 Tree::get_scroll() const { void Tree::scroll_to_item(TreeItem *p_item, bool p_center_on_item) { ERR_FAIL_NULL(p_item); - if (!is_visible_in_tree() || !p_item->is_visible()) { - return; // Hack to work around crash in get_item_rect() if Tree is not in tree. - } update_scrollbars(); - const real_t tree_height = get_size().y; - const Rect2 item_rect = get_item_rect(p_item); - const real_t item_y = item_rect.position.y; - const real_t item_height = item_rect.size.y + theme_cache.vseparation; + // Note: Code below similar to Tree::ensure_cursor_is_visible(), in case of bug fix both. + const Size2 area_size = get_size() - theme_cache.panel_style->get_minimum_size(); - if (p_center_on_item) { - v_scroll->set_value(item_y - (tree_height - item_height) / 2.0f); - } else { - if (item_y < v_scroll->get_value()) { - v_scroll->set_value(item_y); + int y_offset = get_item_offset(p_item); + if (y_offset != -1) { + const int tbh = _get_title_button_height(); + y_offset -= tbh; + + const int cell_h = compute_item_height(p_item) + theme_cache.vseparation; + int screen_h = area_size.height - tbh; + if (h_scroll->is_visible()) { + screen_h -= h_scroll->get_combined_minimum_size().height; + } + + if (p_center_on_item) { + v_scroll->set_value(y_offset - (screen_h - cell_h) / 2.0f); } else { - const real_t new_position = item_y + item_height - tree_height; - if (new_position > v_scroll->get_value()) { - v_scroll->set_value(new_position); + if (cell_h > screen_h) { // Screen size is too small, maybe it was not resized yet. + v_scroll->set_value(y_offset); + } else if (y_offset + cell_h > v_scroll->get_value() + screen_h) { + v_scroll->set_value(y_offset - screen_h + cell_h); + } else if (y_offset < v_scroll->get_value()) { + v_scroll->set_value(y_offset); } } } @@ -5156,6 +5172,7 @@ void Tree::_bind_methods() { ClassDB::bind_method(D_METHOD("is_root_hidden"), &Tree::is_root_hidden); ClassDB::bind_method(D_METHOD("get_next_selected", "from"), &Tree::get_next_selected); ClassDB::bind_method(D_METHOD("get_selected"), &Tree::get_selected); + ClassDB::bind_method(D_METHOD("set_selected", "item", "column"), &Tree::set_selected); ClassDB::bind_method(D_METHOD("get_selected_column"), &Tree::get_selected_column); ClassDB::bind_method(D_METHOD("get_pressed_button"), &Tree::get_pressed_button); ClassDB::bind_method(D_METHOD("set_select_mode", "mode"), &Tree::set_select_mode); diff --git a/scene/gui/tree.h b/scene/gui/tree.h index f994a5cec1..77a62e1d6a 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -666,6 +666,7 @@ public: bool is_root_hidden() const; TreeItem *get_next_selected(TreeItem *p_item); TreeItem *get_selected() const; + void set_selected(TreeItem *p_item, int p_column = 0); int get_selected_column() const; int get_pressed_button() const; void set_select_mode(SelectMode p_mode); diff --git a/scene/main/multiplayer_api.cpp b/scene/main/multiplayer_api.cpp index 946929aa89..8b4b98c172 100644 --- a/scene/main/multiplayer_api.cpp +++ b/scene/main/multiplayer_api.cpp @@ -39,7 +39,7 @@ #include "core/os/os.h" #endif -StringName MultiplayerAPI::default_interface = StringName(); +StringName MultiplayerAPI::default_interface; void MultiplayerAPI::set_default_interface(const StringName &p_interface) { ERR_FAIL_COND_MSG(!ClassDB::is_parent_class(p_interface, MultiplayerAPI::get_class_static()), vformat("Can't make %s the default multiplayer interface since it does not extend MultiplayerAPI.", p_interface)); diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 304504a82e..f88fdb3ac1 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -1759,11 +1759,11 @@ void Node::add_to_group(const StringName &p_identifier, bool p_persistent) { } void Node::remove_from_group(const StringName &p_identifier) { - ERR_FAIL_COND(!data.grouped.has(p_identifier)); - HashMap<StringName, GroupData>::Iterator E = data.grouped.find(p_identifier); - ERR_FAIL_COND(!E); + if (!E) { + return; + } if (data.tree) { data.tree->remove_from_group(E->key, this); @@ -2060,7 +2060,7 @@ Node *Node::_duplicate(int p_flags, HashMap<const Node *, Node *> *r_duplimap) c nip->set_instance_path(ip->get_instance_path()); node = nip; - } else if ((p_flags & DUPLICATE_USE_INSTANCING) && !get_scene_file_path().is_empty()) { + } else if ((p_flags & DUPLICATE_USE_INSTANTIATION) && !get_scene_file_path().is_empty()) { Ref<PackedScene> res = ResourceLoader::load(get_scene_file_path()); ERR_FAIL_COND_V(res.is_null(), nullptr); PackedScene::GenEditState ges = PackedScene::GEN_EDIT_STATE_DISABLED; @@ -2246,7 +2246,7 @@ Node *Node::duplicate_from_editor(HashMap<const Node *, Node *> &r_duplimap) con } Node *Node::duplicate_from_editor(HashMap<const Node *, Node *> &r_duplimap, const HashMap<Ref<Resource>, Ref<Resource>> &p_resource_remap) const { - Node *dupe = _duplicate(DUPLICATE_SIGNALS | DUPLICATE_GROUPS | DUPLICATE_SCRIPTS | DUPLICATE_USE_INSTANCING | DUPLICATE_FROM_EDITOR, &r_duplimap); + Node *dupe = _duplicate(DUPLICATE_SIGNALS | DUPLICATE_GROUPS | DUPLICATE_SCRIPTS | DUPLICATE_USE_INSTANTIATION | DUPLICATE_FROM_EDITOR, &r_duplimap); // This is used by SceneTreeDock's paste functionality. When pasting to foreign scene, resources are duplicated. if (!p_resource_remap.is_empty()) { @@ -2581,10 +2581,14 @@ void Node::print_orphan_nodes() { } void Node::queue_free() { + // There are users which instantiate multiple scene trees for their games. + // Use the node's own tree to handle its deletion when relevant. if (is_inside_tree()) { get_tree()->queue_delete(this); } else { - SceneTree::get_singleton()->queue_delete(this); + SceneTree *tree = SceneTree::get_singleton(); + ERR_FAIL_NULL_MSG(tree, "Can't queue free a node when no SceneTree is available."); + tree->queue_delete(this); } } @@ -2652,7 +2656,7 @@ PackedStringArray Node::get_configuration_warnings() const { String Node::get_configuration_warnings_as_string() const { PackedStringArray warnings = get_configuration_warnings(); - String all_warnings = String(); + String all_warnings; for (int i = 0; i < warnings.size(); i++) { if (i > 0) { all_warnings += "\n\n"; @@ -2812,7 +2816,7 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("get_tree"), &Node::get_tree); ClassDB::bind_method(D_METHOD("create_tween"), &Node::create_tween); - ClassDB::bind_method(D_METHOD("duplicate", "flags"), &Node::duplicate, DEFVAL(DUPLICATE_USE_INSTANCING | DUPLICATE_SIGNALS | DUPLICATE_GROUPS | DUPLICATE_SCRIPTS)); + ClassDB::bind_method(D_METHOD("duplicate", "flags"), &Node::duplicate, DEFVAL(DUPLICATE_USE_INSTANTIATION | DUPLICATE_SIGNALS | DUPLICATE_GROUPS | DUPLICATE_SCRIPTS)); ClassDB::bind_method(D_METHOD("replace_by", "node", "keep_groups"), &Node::replace_by, DEFVAL(false)); ClassDB::bind_method(D_METHOD("set_scene_instance_load_placeholder", "load_placeholder"), &Node::set_scene_instance_load_placeholder); @@ -2918,7 +2922,7 @@ void Node::_bind_methods() { BIND_ENUM_CONSTANT(DUPLICATE_SIGNALS); BIND_ENUM_CONSTANT(DUPLICATE_GROUPS); BIND_ENUM_CONSTANT(DUPLICATE_SCRIPTS); - BIND_ENUM_CONSTANT(DUPLICATE_USE_INSTANCING); + BIND_ENUM_CONSTANT(DUPLICATE_USE_INSTANTIATION); BIND_ENUM_CONSTANT(INTERNAL_MODE_DISABLED); BIND_ENUM_CONSTANT(INTERNAL_MODE_FRONT); diff --git a/scene/main/node.h b/scene/main/node.h index 4a3ec253b1..5e51ec6b0e 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -57,7 +57,7 @@ public: DUPLICATE_SIGNALS = 1, DUPLICATE_GROUPS = 2, DUPLICATE_SCRIPTS = 4, - DUPLICATE_USE_INSTANCING = 8, + DUPLICATE_USE_INSTANTIATION = 8, #ifdef TOOLS_ENABLED DUPLICATE_FROM_EDITOR = 16, #endif diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index 41eefe0f85..81a4e3073b 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -123,6 +123,9 @@ void SceneTree::tree_changed() { void SceneTree::node_added(Node *p_node) { emit_signal(node_added_name, p_node); + if (call_lock > 0) { + call_skip.erase(p_node->get_instance_id()); + } } void SceneTree::node_removed(Node *p_node) { @@ -131,7 +134,7 @@ void SceneTree::node_removed(Node *p_node) { } emit_signal(node_removed_name, p_node); if (call_lock > 0) { - call_skip.insert(p_node); + call_skip.insert(p_node->get_instance_id()); } } @@ -261,7 +264,7 @@ void SceneTree::call_group_flagsp(uint32_t p_call_flags, const StringName &p_gro if (p_call_flags & GROUP_CALL_REVERSE) { for (int i = gr_node_count - 1; i >= 0; i--) { - if (call_lock && call_skip.has(gr_nodes[i])) { + if (call_lock && call_skip.has(gr_nodes[i]->get_instance_id())) { continue; } @@ -275,7 +278,7 @@ void SceneTree::call_group_flagsp(uint32_t p_call_flags, const StringName &p_gro } else { for (int i = 0; i < gr_node_count; i++) { - if (call_lock && call_skip.has(gr_nodes[i])) { + if (call_lock && call_skip.has(gr_nodes[i]->get_instance_id())) { continue; } @@ -314,7 +317,7 @@ void SceneTree::notify_group_flags(uint32_t p_call_flags, const StringName &p_gr if (p_call_flags & GROUP_CALL_REVERSE) { for (int i = gr_node_count - 1; i >= 0; i--) { - if (call_lock && call_skip.has(gr_nodes[i])) { + if (call_lock && call_skip.has(gr_nodes[i]->get_instance_id())) { continue; } @@ -327,7 +330,7 @@ void SceneTree::notify_group_flags(uint32_t p_call_flags, const StringName &p_gr } else { for (int i = 0; i < gr_node_count; i++) { - if (call_lock && call_skip.has(gr_nodes[i])) { + if (call_lock && call_skip.has(gr_nodes[i]->get_instance_id())) { continue; } @@ -365,7 +368,7 @@ void SceneTree::set_group_flags(uint32_t p_call_flags, const StringName &p_group if (p_call_flags & GROUP_CALL_REVERSE) { for (int i = gr_node_count - 1; i >= 0; i--) { - if (call_lock && call_skip.has(gr_nodes[i])) { + if (call_lock && call_skip.has(gr_nodes[i]->get_instance_id())) { continue; } @@ -378,7 +381,7 @@ void SceneTree::set_group_flags(uint32_t p_call_flags, const StringName &p_group } else { for (int i = 0; i < gr_node_count; i++) { - if (call_lock && call_skip.has(gr_nodes[i])) { + if (call_lock && call_skip.has(gr_nodes[i]->get_instance_id())) { continue; } @@ -854,7 +857,7 @@ void SceneTree::_notify_group_pause(const StringName &p_group, int p_notificatio for (int i = 0; i < gr_node_count; i++) { Node *n = gr_nodes[i]; - if (call_lock && call_skip.has(n)) { + if (call_lock && call_skip.has(n->get_instance_id())) { continue; } @@ -904,7 +907,7 @@ void SceneTree::_call_input_pause(const StringName &p_group, CallInputType p_cal } Node *n = gr_nodes[i]; - if (call_lock && call_skip.has(n)) { + if (call_lock && call_skip.has(n->get_instance_id())) { continue; } diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h index a460e40597..d4fcb288ae 100644 --- a/scene/main/scene_tree.h +++ b/scene/main/scene_tree.h @@ -135,7 +135,7 @@ private: // Safety for when a node is deleted while a group is being called. int call_lock = 0; - HashSet<Node *> call_skip; // Skip erased nodes. + HashSet<ObjectID> call_skip; // Skip erased nodes. Store ID instead of pointer to avoid false positives when node is freed and a new node is allocated at the pointed address. List<ObjectID> delete_queue; diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 43a2c9473e..4c2a761138 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -1669,6 +1669,9 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { _gui_cancel_tooltip(); if (over) { + if (!gui.mouse_over) { + _drop_physics_mouseover(); + } _gui_call_notification(over, Control::NOTIFICATION_MOUSE_ENTER); gui.mouse_over = over; } @@ -2660,6 +2663,11 @@ bool Viewport::_sub_windows_forward_input(const Ref<InputEvent> &p_event) { } else { gui.subwindow_resize_mode = _sub_window_get_resize_margin(sw.window, mb->get_position()); if (gui.subwindow_resize_mode != SUB_WINDOW_RESIZE_DISABLED) { + if (gui.subwindow_focused != sw.window) { + // Refocus. + _sub_window_grab_focus(sw.window); + } + gui.subwindow_resize_from_rect = r; gui.subwindow_drag_from = mb->get_position(); gui.subwindow_drag = SUB_WINDOW_DRAG_RESIZE; @@ -3039,8 +3047,6 @@ bool Viewport::gui_is_drag_successful() const { } void Viewport::set_input_as_handled() { - _drop_physics_mouseover(); - if (!handle_input_locally) { ERR_FAIL_COND(!is_inside_tree()); Viewport *vp = this; @@ -4159,7 +4165,7 @@ DisplayServer::WindowID SubViewport::get_window_id() const { } Transform2D SubViewport::_stretch_transform() { - Transform2D transform = Transform2D(); + Transform2D transform; Size2i view_size_2d_override = _get_size_2d_override(); if (size_2d_override_stretch && view_size_2d_override.width > 0 && view_size_2d_override.height > 0) { Size2 scale = Size2(_get_size()) / Size2(view_size_2d_override); @@ -4170,7 +4176,7 @@ Transform2D SubViewport::_stretch_transform() { } Transform2D SubViewport::get_screen_transform() const { - Transform2D container_transform = Transform2D(); + Transform2D container_transform; SubViewportContainer *c = Object::cast_to<SubViewportContainer>(get_parent()); if (c) { if (c->is_stretch_enabled()) { diff --git a/scene/main/window.cpp b/scene/main/window.cpp index ebb11ecee8..aff2c594d9 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -1633,7 +1633,7 @@ void Window::_validate_property(PropertyInfo &p_property) const { } Transform2D Window::get_screen_transform() const { - Transform2D embedder_transform = Transform2D(); + Transform2D embedder_transform; if (_get_embedder()) { embedder_transform.translate_local(get_position()); embedder_transform = _get_embedder()->get_screen_transform() * embedder_transform; diff --git a/scene/property_utils.h b/scene/property_utils.h index 5ada35fdd7..5b0200b491 100644 --- a/scene/property_utils.h +++ b/scene/property_utils.h @@ -42,7 +42,7 @@ public: // Gets the instance/inheritance states of this node, in order of precedence, // that is, from the topmost (the most able to override values) to the lowermost - // (Note that in nested instancing the one with the greatest precedence is the furthest + // (Note that in nested instantiation, the one with the greatest precedence is the furthest // in the tree, since every owner found while traversing towards the root gets a chance // to override property values.) static Vector<SceneState::PackState> get_node_states_stack(const Node *p_node, const Node *p_owner = nullptr, bool *r_instantiated_by_owner = nullptr); diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 0f55c7b3b8..ee45a8ea6f 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -960,6 +960,7 @@ void register_scene_types() { ClassDB::add_compatibility_class("Navigation3D", "Node3D"); ClassDB::add_compatibility_class("Navigation2D", "Node2D"); ClassDB::add_compatibility_class("OpenSimplexNoise", "FastNoiseLite"); + ClassDB::add_compatibility_class("ProximityGroup", "Node3D"); ClassDB::add_compatibility_class("ToolButton", "Button"); ClassDB::add_compatibility_class("YSort", "Node2D"); // Portal and room occlusion was replaced by raster occlusion (OccluderInstance3D node). diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index 1eb78f0160..ef619c9893 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -4964,7 +4964,7 @@ void Animation::compress(uint32_t p_page_size, uint32_t p_fps, float p_split_tol if (rollback || best_frame == FRAME_MAX) { // Commit the page if had to rollback or if no track was found - print_animc("\tCommiting page.."); + print_animc("\tCommiting page..."); // The end frame for the page depends entirely on whether its valid or // no more keys were found. diff --git a/scene/resources/bit_map.cpp b/scene/resources/bit_map.cpp index 1b06e09bb8..4afc82576d 100644 --- a/scene/resources/bit_map.cpp +++ b/scene/resources/bit_map.cpp @@ -169,7 +169,15 @@ Dictionary BitMap::_get_data() const { return d; } -Vector<Vector2> BitMap::_march_square(const Rect2i &p_rect, const Point2i &p_start) const { +struct CrossStackEntry { + Point2i cross; + Vector<int> ranges; + + _FORCE_INLINE_ bool operator==(const CrossStackEntry &p_other) const { return cross == p_other.cross; } + _FORCE_INLINE_ bool operator!=(const CrossStackEntry &p_other) const { return cross != p_other.cross; } +}; + +Vector<Vector<Vector2>> BitMap::_march_square(const Rect2i &p_rect, const Point2i &p_start) const { int stepx = 0; int stepy = 0; int prevx = 0; @@ -179,9 +187,20 @@ Vector<Vector2> BitMap::_march_square(const Rect2i &p_rect, const Point2i &p_sta int curx = startx; int cury = starty; unsigned int count = 0; - HashSet<Point2i> case9s; - HashSet<Point2i> case6s; - Vector<Vector2> _points; + + Vector<CrossStackEntry> cross_stack; + int cross_stack_size = 0; + + // Add starting point to stack as the default entry. + cross_stack.push_back({ Point2i(-1, -1), Vector<int>({ 0 }) }); + cross_stack_size++; + + Vector<Point2i> _points; + Vector<Vector<Vector2>> ret; + + // Add starting entry at start of return. + ret.resize(1); + do { int sv = 0; { // Square value @@ -202,7 +221,7 @@ Vector<Vector2> BitMap::_march_square(const Rect2i &p_rect, const Point2i &p_sta sv += (p_rect.has_point(bl) && get_bitv(bl)) ? 4 : 0; Point2i br = Point2i(curx, cury); sv += (p_rect.has_point(br) && get_bitv(br)) ? 8 : 0; - ERR_FAIL_COND_V(sv == 0 || sv == 15, Vector<Vector2>()); + ERR_FAIL_COND_V(sv == 0 || sv == 15, Vector<Vector<Vector2>>()); } switch (sv) { @@ -266,70 +285,139 @@ Vector<Vector2> BitMap::_march_square(const Rect2i &p_rect, const Point2i &p_sta stepy = 0; break; case 9: - /* + /* Going DOWN if coming from the LEFT, otherwise go UP. + 9 +---+---+ | 1 | | +---+---+ | | 8 | +---+---+ - this should normally go UP, but if we already been here, we go down */ - if (case9s.has(Point2i(curx, cury))) { - //found, so we go down, and delete from case9s; + + if (prevx == 1) { stepx = 0; stepy = 1; - case9s.erase(Point2i(curx, cury)); } else { - //not found, we go up, and add to case9s; stepx = 0; stepy = -1; - case9s.insert(Point2i(curx, cury)); } break; case 6: - /* + /* Going RIGHT if coming from BELOW, otherwise go LEFT. 6 +---+---+ | | 2 | +---+---+ | 4 | | +---+---+ - this normally go RIGHT, but if it's coming from RIGHT, it should go LEFT */ - if (case6s.has(Point2i(curx, cury))) { - //found, so we go left, and delete from case6s; - stepx = -1; + + if (prevy == -1) { + stepx = 1; stepy = 0; - case6s.erase(Point2i(curx, cury)); } else { - //not found, we go right, and add to case6s; - stepx = 1; + stepx = -1; stepy = 0; - case6s.insert(Point2i(curx, cury)); } break; default: ERR_PRINT("this shouldn't happen."); } + + // Handle crossing points. + if (sv == 6 || sv == 9) { + const int new_index = _points.size() - 1; + + // Add previous point to last stack entry. + cross_stack.write[cross_stack_size - 1].ranges.push_back(new_index); + + // Create temporary entry to maybe insert, for searching. + const CrossStackEntry new_entry = { _points[new_index], Vector<int>({ new_index }) }; + + // Attempt to find matching entry. + const int found = cross_stack.rfind(new_entry, cross_stack_size - 1); + + if (found != -1) { + Vector<Vector2> tmp; + + // Iterate over entries between end of stack and found, adding ranges to result. + for (int i = found; i < cross_stack_size; i++) { + const Vector<int> &ranges = cross_stack[i].ranges; + + for (int j = 0; j < ranges.size() / 2; j++) { + int first = ranges[2 * j]; + const int last = ranges[2 * j + 1]; + + int new_pos = tmp.size(); + + tmp.resize(tmp.size() + (last - first)); + + Vector2 *tmp_ptrw = tmp.ptrw(); + + for (; first < last; first++, new_pos++) { + tmp_ptrw[new_pos].x = (float)(_points[first].x - p_rect.position.x); + tmp_ptrw[new_pos].y = (float)(_points[first].y - p_rect.position.y); + } + } + } + + ret.push_back(tmp); + + // Shrink stack. + cross_stack_size = found; + + // Add previous point to last stack entry. + cross_stack.write[cross_stack_size - 1].ranges.push_back(new_index); + } else { + cross_stack.resize(MAX(cross_stack_size + 1, cross_stack.size())); + cross_stack.set(cross_stack_size, new_entry); + cross_stack_size++; + } + } + // Small optimization: // If the previous direction is same as the current direction, // then we should modify the last vector to current. curx += stepx; cury += stepy; if (stepx == prevx && stepy == prevy) { - _points.write[_points.size() - 1].x = (float)(curx - p_rect.position.x); - _points.write[_points.size() - 1].y = (float)(cury + p_rect.position.y); + _points.write[_points.size() - 1].x = curx; + _points.write[_points.size() - 1].y = cury; } else { - _points.push_back(Vector2((float)(curx - p_rect.position.x), (float)(cury + p_rect.position.y))); + _points.push_back(Point2i(curx, cury)); } count++; prevx = stepx; prevy = stepy; - ERR_FAIL_COND_V((int)count > width * height, _points); + ERR_FAIL_COND_V((int)count > width * height, Vector<Vector<Vector2>>()); } while (curx != startx || cury != starty); - return _points; + + // Add last position to last stack entry. + cross_stack.write[cross_stack_size - 1].ranges.push_back(_points.size()); + + for (int i = 0; i < cross_stack_size; i++) { + const Vector<int> &ranges = cross_stack[i].ranges; + + for (int j = 0; j < ranges.size() / 2; j++) { + int first = ranges[2 * j]; + const int last = ranges[2 * j + 1]; + + int new_pos = ret[0].size(); + + ret.write[0].resize(ret[0].size() + (last - first)); + + Vector2 *tmp_ptrw = ret.write[0].ptrw(); + + for (; first < last; first++, new_pos++) { + tmp_ptrw[new_pos].x = (float)(_points[first].x - p_rect.position.x); + tmp_ptrw[new_pos].y = (float)(_points[first].y - p_rect.position.y); + } + } + } + + return ret; } static float perpendicular_distance(const Vector2 &i, const Vector2 &start, const Vector2 &end) { @@ -442,7 +530,7 @@ static void fill_bits(const BitMap *p_src, Ref<BitMap> &p_map, const Point2i &p_ for (int j = next_j; j <= pos.y + 1; j++) { if (popped) { // The next loop over j must start normally. - next_j = pos.y; + next_j = pos.y - 1; popped = false; // Skip because an iteration was already executed with current counter values. continue; @@ -486,13 +574,10 @@ static void fill_bits(const BitMap *p_src, Ref<BitMap> &p_map, const Point2i &p_ } } } while (reenter || popped); - - print_verbose("BitMap: Max stack size: " + itos(stack.size())); } Vector<Vector<Vector2>> BitMap::clip_opaque_to_polygons(const Rect2i &p_rect, float p_epsilon) const { Rect2i r = Rect2i(0, 0, width, height).intersection(p_rect); - print_verbose("BitMap: Rect: " + r); Point2i from; Ref<BitMap> fill; @@ -505,17 +590,16 @@ Vector<Vector<Vector2>> BitMap::clip_opaque_to_polygons(const Rect2i &p_rect, fl if (!fill->get_bit(j, i) && get_bit(j, i)) { fill_bits(this, fill, Point2i(j, i), r); - Vector<Vector2> polygon = _march_square(r, Point2i(j, i)); - print_verbose("BitMap: Pre reduce: " + itos(polygon.size())); - polygon = reduce(polygon, r, p_epsilon); - print_verbose("BitMap: Post reduce: " + itos(polygon.size())); + for (Vector<Vector2> polygon : _march_square(r, Point2i(j, i))) { + polygon = reduce(polygon, r, p_epsilon); - if (polygon.size() < 3) { - print_verbose("Invalid polygon, skipped"); - continue; - } + if (polygon.size() < 3) { + print_verbose("Invalid polygon, skipped"); + continue; + } - polygons.push_back(polygon); + polygons.push_back(polygon); + } } } } diff --git a/scene/resources/bit_map.h b/scene/resources/bit_map.h index 291ed8c4d0..0ec5772fd1 100644 --- a/scene/resources/bit_map.h +++ b/scene/resources/bit_map.h @@ -46,7 +46,7 @@ class BitMap : public Resource { int width = 0; int height = 0; - Vector<Vector2> _march_square(const Rect2i &p_rect, const Point2i &p_start) const; + Vector<Vector<Vector2>> _march_square(const Rect2i &p_rect, const Point2i &p_start) const; TypedArray<PackedVector2Array> _opaque_to_polygons_bind(const Rect2i &p_rect, float p_epsilon) const; diff --git a/scene/resources/bone_map.cpp b/scene/resources/bone_map.cpp index dfaf82f36a..5698e61004 100644 --- a/scene/resources/bone_map.cpp +++ b/scene/resources/bone_map.cpp @@ -93,7 +93,7 @@ void BoneMap::set_skeleton_bone_name(StringName p_profile_bone_name, const Strin } StringName BoneMap::find_profile_bone_name(StringName p_skeleton_bone_name) const { - StringName profile_bone_name = StringName(); + StringName profile_bone_name; HashMap<StringName, StringName>::ConstIterator E = bone_map.begin(); while (E) { if (E->value == p_skeleton_bone_name) { diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp index 185b6f4bdc..84814d939b 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -1396,6 +1396,9 @@ Error FontFile::load_bitmap_font(const String &p_path) { case 1: /* info */ { ERR_FAIL_COND_V_MSG(block_size < 15, ERR_CANT_CREATE, RTR("Invalid BMFont info block size.")); base_size = f->get_16(); + if (base_size <= 0) { + base_size = 16; + } uint8_t flags = f->get_8(); if (flags & (1 << 3)) { st_flags.set_flag(TextServer::FONT_BOLD); @@ -1681,7 +1684,6 @@ Error FontFile::load_bitmap_font(const String &p_path) { if (type == "info") { if (keys.has("size")) { base_size = keys["size"].to_int(); - set_fixed_size(base_size); } if (keys.has("outline")) { outline = keys["outline"].to_int(); @@ -1730,6 +1732,7 @@ Error FontFile::load_bitmap_font(const String &p_path) { encoding = 2; } } + set_fixed_size(base_size); } else if (type == "common") { if (keys.has("lineHeight")) { height = keys["lineHeight"].to_int(); @@ -1788,7 +1791,10 @@ Error FontFile::load_bitmap_font(const String &p_path) { ERR_FAIL_V_MSG(ERR_CANT_CREATE, RTR("Unsupported BMFont texture format.")); } } else { - if ((ch[0] == 0) && (ch[1] == 0) && (ch[2] == 0) && (ch[3] == 0)) { // RGBA8 color, no outline + if ((ch[3] == 0) && (ch[0] == 4) && (ch[1] == 4) && (ch[2] == 4) && img->get_format() == Image::FORMAT_RGBA8) { // might be RGBA8 color, no outline (color part of the image should be sold white, but some apps designed for Godot 3 generate color fonts with this config) + outline = 0; + set_texture_image(0, Vector2i(base_size, 0), page, img); + } else if ((ch[0] == 0) && (ch[1] == 0) && (ch[2] == 0) && (ch[3] == 0)) { // RGBA8 color, no outline outline = 0; ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format.")); set_texture_image(0, Vector2i(base_size, 0), page, img); diff --git a/scene/resources/importer_mesh.cpp b/scene/resources/importer_mesh.cpp index b728c24e0d..cec5569345 100644 --- a/scene/resources/importer_mesh.cpp +++ b/scene/resources/importer_mesh.cpp @@ -255,7 +255,7 @@ void ImporterMesh::set_surface_material(int p_surface, const Ref<Material> &p_ma } #define VERTEX_SKIN_FUNC(bone_count, vert_idx, read_array, write_array, transform_array, bone_array, weight_array) \ - Vector3 transformed_vert = Vector3(); \ + Vector3 transformed_vert; \ for (unsigned int weight_idx = 0; weight_idx < bone_count; weight_idx++) { \ int bone_idx = bone_array[vert_idx * bone_count + weight_idx]; \ float w = weight_array[vert_idx * bone_count + weight_idx]; \ diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index 1c99fa5554..f46faa1013 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -71,7 +71,7 @@ static Array _sanitize_node_pinned_properties(Node *p_node) { } Node *SceneState::instantiate(GenEditState p_edit_state) const { - // nodes where instancing failed (because something is missing) + // Nodes where instantiation failed (because something is missing.) List<Node *> stray_instances; #define NODE_FROM_ID(p_name, p_id) \ @@ -122,7 +122,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { NODE_FROM_ID(nparent, n.parent); #ifdef DEBUG_ENABLED if (!nparent && (n.parent & FLAG_ID_IS_PATH)) { - WARN_PRINT(String("Parent path '" + String(node_paths[n.parent & FLAG_MASK]) + "' for node '" + String(snames[n.name]) + "' has vanished when instancing: '" + get_path() + "'.").ascii().get_data()); + WARN_PRINT(String("Parent path '" + String(node_paths[n.parent & FLAG_MASK]) + "' for node '" + String(snames[n.name]) + "' has vanished when instantiating: '" + get_path() + "'.").ascii().get_data()); old_parent_path = String(node_paths[n.parent & FLAG_MASK]).trim_prefix("./").replace("/", "@"); nparent = ret_nodes[0]; } @@ -131,7 +131,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { } else { // i == 0 is root node. ERR_FAIL_COND_V_MSG(n.parent != -1, nullptr, vformat("Invalid scene: root node %s cannot specify a parent node.", snames[n.name])); - ERR_FAIL_COND_V_MSG(n.type == TYPE_INSTANCED && base_scene_idx < 0, nullptr, vformat("Invalid scene: root node %s in an instance, but there's no base scene.", snames[n.name])); + ERR_FAIL_COND_V_MSG(n.type == TYPE_INSTANTIATED && base_scene_idx < 0, nullptr, vformat("Invalid scene: root node %s in an instance, but there's no base scene.", snames[n.name])); } Node *node = nullptr; @@ -169,7 +169,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { ERR_FAIL_COND_V(!node, nullptr); } - } else if (n.type == TYPE_INSTANCED) { + } else if (n.type == TYPE_INSTANTIATED) { //get the node from somewhere, it likely already exists from another instance if (parent) { node = parent->_get_child_by_name(snames[n.name]); @@ -345,7 +345,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { node->add_to_group(snames[n.groups[j]], true); } - if (n.instance >= 0 || n.type != TYPE_INSTANCED || i == 0) { + if (n.instance >= 0 || n.type != TYPE_INSTANTIATED || i == 0) { //if node was not part of instance, must set its name, parenthood and ownership if (i > 0) { if (parent) { @@ -382,7 +382,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { } } - // we only want to deal with pinned flag if instancing as pure main (no instance, no inheriting) + // We only want to deal with pinned flag if instantiating as pure main (no instance, no inheriting.) if (p_edit_state == GEN_EDIT_STATE_MAIN) { _sanitize_node_pinned_properties(node); } else { @@ -665,7 +665,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Has // Save the right type. If this node was created by an instance // then flag that the node should not be created but reused if (states_stack.is_empty() && !is_editable_instance) { - //this node is not part of an instancing process, so save the type + //This node is not part of an instantiation process, so save the type. if (missing_node != nullptr) { // It's a missing node (type non existent on load). nd.type = _nm_get_string(missing_node->get_original_class(), name_map); @@ -675,7 +675,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Has } else { // this node is part of an instantiated process, so do not save the type. // instead, save that it was instantiated - nd.type = TYPE_INSTANCED; + nd.type = TYPE_INSTANTIATED; } // determine whether to save this node or not @@ -1351,7 +1351,7 @@ int SceneState::get_node_count() const { StringName SceneState::get_node_type(int p_idx) const { ERR_FAIL_INDEX_V(p_idx, nodes.size(), StringName()); - if (nodes[p_idx].type == TYPE_INSTANCED) { + if (nodes[p_idx].type == TYPE_INSTANTIATED) { return StringName(); } return names[nodes[p_idx].type]; diff --git a/scene/resources/packed_scene.h b/scene/resources/packed_scene.h index c6f82ddd5e..a30ec54d85 100644 --- a/scene/resources/packed_scene.h +++ b/scene/resources/packed_scene.h @@ -108,7 +108,7 @@ protected: public: enum { FLAG_ID_IS_PATH = (1 << 30), - TYPE_INSTANCED = 0x7FFFFFFF, + TYPE_INSTANTIATED = 0x7FFFFFFF, FLAG_INSTANCE_IS_PLACEHOLDER = (1 << 30), FLAG_PATH_PROPERTY_IS_NODE = (1 << 30), FLAG_PROP_NAME_MASK = FLAG_PATH_PROPERTY_IS_NODE - 1, diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp index eb83a37c7b..4c6d533c72 100644 --- a/scene/resources/primitive_meshes.cpp +++ b/scene/resources/primitive_meshes.cpp @@ -30,6 +30,7 @@ #include "primitive_meshes.h" +#include "core/config/project_settings.h" #include "core/core_string_names.h" #include "scene/resources/theme.h" #include "scene/theme/theme_db.h" @@ -37,6 +38,8 @@ #include "thirdparty/misc/clipper.hpp" #include "thirdparty/misc/polypartition.h" +#define PADDING_REF_SIZE 1024.0 + /** PrimitiveMesh */ @@ -94,6 +97,26 @@ void PrimitiveMesh::_update() const { } } + if (add_uv2) { + // _create_mesh_array should populate our UV2, this is a fallback in case it doesn't. + // As we don't know anything about the geometry we only pad the right and bottom edge + // of our texture. + Vector<Vector2> uv = arr[RS::ARRAY_TEX_UV]; + Vector<Vector2> uv2 = arr[RS::ARRAY_TEX_UV2]; + + if (uv.size() > 0 && uv2.size() == 0) { + Vector2 uv2_scale = get_uv2_scale(); + uv2.resize(uv.size()); + + Vector2 *uv2w = uv2.ptrw(); + for (int i = 0; i < uv.size(); i++) { + uv2w[i] = uv[i] * uv2_scale; + } + } + + arr[RS::ARRAY_TEX_UV2] = uv2; + } + array_len = pc; index_array_len = indices.size(); // in with the new @@ -160,7 +183,12 @@ TypedArray<Array> PrimitiveMesh::surface_get_blend_shape_arrays(int p_surface) c uint32_t PrimitiveMesh::surface_get_format(int p_idx) const { ERR_FAIL_INDEX_V(p_idx, 1, 0); - return RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_NORMAL | RS::ARRAY_FORMAT_TANGENT | RS::ARRAY_FORMAT_TEX_UV | RS::ARRAY_FORMAT_INDEX; + uint32_t mesh_format = RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_NORMAL | RS::ARRAY_FORMAT_TANGENT | RS::ARRAY_FORMAT_TEX_UV | RS::ARRAY_FORMAT_INDEX; + if (add_uv2) { + mesh_format |= RS::ARRAY_FORMAT_TEX_UV2; + } + + return mesh_format; } Mesh::PrimitiveType PrimitiveMesh::surface_get_primitive_type(int p_idx) const { @@ -219,9 +247,17 @@ void PrimitiveMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_flip_faces", "flip_faces"), &PrimitiveMesh::set_flip_faces); ClassDB::bind_method(D_METHOD("get_flip_faces"), &PrimitiveMesh::get_flip_faces); + ClassDB::bind_method(D_METHOD("set_add_uv2", "add_uv2"), &PrimitiveMesh::set_add_uv2); + ClassDB::bind_method(D_METHOD("get_add_uv2"), &PrimitiveMesh::get_add_uv2); + + ClassDB::bind_method(D_METHOD("set_uv2_padding", "uv2_padding"), &PrimitiveMesh::set_uv2_padding); + ClassDB::bind_method(D_METHOD("get_uv2_padding"), &PrimitiveMesh::get_uv2_padding); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial"), "set_material", "get_material"); ADD_PROPERTY(PropertyInfo(Variant::AABB, "custom_aabb", PROPERTY_HINT_NONE, "suffix:m"), "set_custom_aabb", "get_custom_aabb"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_faces"), "set_flip_faces", "get_flip_faces"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "add_uv2"), "set_add_uv2", "get_add_uv2"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "uv2_padding"), "set_uv2_padding", "get_uv2_padding"); GDVIRTUAL_BIND(_create_mesh_array); } @@ -233,7 +269,7 @@ void PrimitiveMesh::set_material(const Ref<Material> &p_material) { RenderingServer::get_singleton()->mesh_surface_set_material(mesh, 0, material.is_null() ? RID() : material->get_rid()); notify_property_list_changed(); emit_changed(); - }; + } } Ref<Material> PrimitiveMesh::get_material() const { @@ -263,6 +299,42 @@ bool PrimitiveMesh::get_flip_faces() const { return flip_faces; } +void PrimitiveMesh::set_add_uv2(bool p_enable) { + add_uv2 = p_enable; + _update_lightmap_size(); + _request_update(); +} + +void PrimitiveMesh::set_uv2_padding(float p_padding) { + uv2_padding = p_padding; + _update_lightmap_size(); + _request_update(); +} + +Vector2 PrimitiveMesh::get_uv2_scale(Vector2 p_margin_scale) const { + Vector2 uv2_scale; + Vector2 lightmap_size = get_lightmap_size_hint(); + + // Calculate it as a margin, if no lightmap size hint is given we assume "PADDING_REF_SIZE" as our texture size. + uv2_scale.x = p_margin_scale.x * uv2_padding / (lightmap_size.x == 0.0 ? PADDING_REF_SIZE : lightmap_size.x); + uv2_scale.y = p_margin_scale.y * uv2_padding / (lightmap_size.y == 0.0 ? PADDING_REF_SIZE : lightmap_size.y); + + // Inverse it to turn our margin into a scale + uv2_scale = Vector2(1.0, 1.0) - uv2_scale; + + return uv2_scale; +} + +float PrimitiveMesh::get_lightmap_texel_size() const { + float texel_size = GLOBAL_GET("rendering/lightmapping/primitive_meshes/texel_size"); + + if (texel_size <= 0.0) { + texel_size = 0.2; + } + + return texel_size; +} + PrimitiveMesh::PrimitiveMesh() { mesh = RenderingServer::get_singleton()->mesh_create(); } @@ -275,22 +347,52 @@ PrimitiveMesh::~PrimitiveMesh() { CapsuleMesh */ +void CapsuleMesh::_update_lightmap_size() { + if (get_add_uv2()) { + // size must have changed, update lightmap size hint + Size2i _lightmap_size_hint; + float texel_size = get_lightmap_texel_size(); + float padding = get_uv2_padding(); + + float radial_length = radius * Math_PI * 0.5; // circumference of 90 degree bend + float vertical_length = radial_length * 2 + (height - 2.0 * radius); // total vertical length + + _lightmap_size_hint.x = MAX(1.0, 4.0 * radial_length / texel_size) + padding; + _lightmap_size_hint.y = MAX(1.0, vertical_length / texel_size) + padding; + + set_lightmap_size_hint(_lightmap_size_hint); + } +} + void CapsuleMesh::_create_mesh_array(Array &p_arr) const { - create_mesh_array(p_arr, radius, height, radial_segments, rings); + bool _add_uv2 = get_add_uv2(); + float texel_size = get_lightmap_texel_size(); + float _uv2_padding = get_uv2_padding() * texel_size; + + create_mesh_array(p_arr, radius, height, radial_segments, rings, _add_uv2, _uv2_padding); } -void CapsuleMesh::create_mesh_array(Array &p_arr, const float radius, const float height, const int radial_segments, const int rings) { +void CapsuleMesh::create_mesh_array(Array &p_arr, const float radius, const float height, const int radial_segments, const int rings, bool p_add_uv2, const float p_uv2_padding) { int i, j, prevrow, thisrow, point; float x, y, z, u, v, w; float onethird = 1.0 / 3.0; float twothirds = 2.0 / 3.0; + // Only used if we calculate UV2 + float radial_width = 2.0 * radius * Math_PI; + float radial_h = radial_width / (radial_width + p_uv2_padding); + float radial_length = radius * Math_PI * 0.5; // circumference of 90 degree bend + float vertical_length = radial_length * 2 + (height - 2.0 * radius) + p_uv2_padding; // total vertical length + float radial_v = radial_length / vertical_length; // v size of top and bottom section + float height_v = (height - 2.0 * radius) / vertical_length; // v size of height section + // note, this has been aligned with our collision shape but I've left the descriptions as top/middle/bottom Vector<Vector3> points; Vector<Vector3> normals; Vector<float> tangents; Vector<Vector2> uvs; + Vector<Vector2> uv2s; Vector<int> indices; point = 0; @@ -322,6 +424,9 @@ void CapsuleMesh::create_mesh_array(Array &p_arr, const float radius, const floa normals.push_back(p.normalized()); ADD_TANGENT(-z, 0.0, -x, 1.0) uvs.push_back(Vector2(u, v * onethird)); + if (p_add_uv2) { + uv2s.push_back(Vector2(u * radial_h, v * radial_v)); + } point++; if (i > 0 && j > 0) { @@ -332,12 +437,12 @@ void CapsuleMesh::create_mesh_array(Array &p_arr, const float radius, const floa indices.push_back(prevrow + i); indices.push_back(thisrow + i); indices.push_back(thisrow + i - 1); - }; - }; + } + } prevrow = thisrow; thisrow = point; - }; + } /* cylinder */ thisrow = point; @@ -361,6 +466,9 @@ void CapsuleMesh::create_mesh_array(Array &p_arr, const float radius, const floa normals.push_back(Vector3(x, 0.0, -z)); ADD_TANGENT(-z, 0.0, -x, 1.0) uvs.push_back(Vector2(u, onethird + (v * onethird))); + if (p_add_uv2) { + uv2s.push_back(Vector2(u * radial_h, radial_v + (v * height_v))); + } point++; if (i > 0 && j > 0) { @@ -371,12 +479,12 @@ void CapsuleMesh::create_mesh_array(Array &p_arr, const float radius, const floa indices.push_back(prevrow + i); indices.push_back(thisrow + i); indices.push_back(thisrow + i - 1); - }; - }; + } + } prevrow = thisrow; thisrow = point; - }; + } /* bottom hemisphere */ thisrow = point; @@ -390,17 +498,20 @@ void CapsuleMesh::create_mesh_array(Array &p_arr, const float radius, const floa y = radius * cos(0.5 * Math_PI * v); for (i = 0; i <= radial_segments; i++) { - float u2 = i; - u2 /= radial_segments; + u = i; + u /= radial_segments; - x = -sin(u2 * Math_TAU); - z = cos(u2 * Math_TAU); + x = -sin(u * Math_TAU); + z = cos(u * Math_TAU); Vector3 p = Vector3(x * radius * w, y, -z * radius * w); points.push_back(p + Vector3(0.0, -0.5 * height + radius, 0.0)); normals.push_back(p.normalized()); ADD_TANGENT(-z, 0.0, -x, 1.0) - uvs.push_back(Vector2(u2, twothirds + ((v - 1.0) * onethird))); + uvs.push_back(Vector2(u, twothirds + ((v - 1.0) * onethird))); + if (p_add_uv2) { + uv2s.push_back(Vector2(u * radial_h, radial_v + height_v + ((v - 1.0) * radial_v))); + } point++; if (i > 0 && j > 0) { @@ -411,17 +522,20 @@ void CapsuleMesh::create_mesh_array(Array &p_arr, const float radius, const floa indices.push_back(prevrow + i); indices.push_back(thisrow + i); indices.push_back(thisrow + i - 1); - }; - }; + } + } prevrow = thisrow; thisrow = point; - }; + } p_arr[RS::ARRAY_VERTEX] = points; p_arr[RS::ARRAY_NORMAL] = normals; p_arr[RS::ARRAY_TANGENT] = tangents; p_arr[RS::ARRAY_TEX_UV] = uvs; + if (p_add_uv2) { + p_arr[RS::ARRAY_TEX_UV2] = uv2s; + } p_arr[RS::ARRAY_INDEX] = indices; } @@ -450,6 +564,7 @@ void CapsuleMesh::set_radius(const float p_radius) { if (radius > height * 0.5) { height = radius * 2.0; } + _update_lightmap_size(); _request_update(); } @@ -462,6 +577,7 @@ void CapsuleMesh::set_height(const float p_height) { if (radius > height * 0.5) { radius = height * 0.5; } + _update_lightmap_size(); _request_update(); } @@ -493,16 +609,53 @@ CapsuleMesh::CapsuleMesh() {} BoxMesh */ +void BoxMesh::_update_lightmap_size() { + if (get_add_uv2()) { + // size must have changed, update lightmap size hint + Size2i _lightmap_size_hint; + float texel_size = get_lightmap_texel_size(); + float padding = get_uv2_padding(); + + float width = (size.x + size.z) / texel_size; + float length = (size.y + size.y + MAX(size.x, size.z)) / texel_size; + + _lightmap_size_hint.x = MAX(1.0, width) + 2.0 * padding; + _lightmap_size_hint.y = MAX(1.0, length) + 3.0 * padding; + + set_lightmap_size_hint(_lightmap_size_hint); + } +} + void BoxMesh::_create_mesh_array(Array &p_arr) const { - BoxMesh::create_mesh_array(p_arr, size, subdivide_w, subdivide_h, subdivide_d); + // Note about padding, with our box each face of the box faces a different direction so we want a seam + // around every face. We thus add our padding to the right and bottom of each face. + // With 3 faces along the width and 2 along the height of the texture we need to adjust our scale + // accordingly. + bool _add_uv2 = get_add_uv2(); + float texel_size = get_lightmap_texel_size(); + float _uv2_padding = get_uv2_padding() * texel_size; + + BoxMesh::create_mesh_array(p_arr, size, subdivide_w, subdivide_h, subdivide_d, _add_uv2, _uv2_padding); } -void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int subdivide_h, int subdivide_d) { +void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int subdivide_h, int subdivide_d, bool p_add_uv2, const float p_uv2_padding) { int i, j, prevrow, thisrow, point; float x, y, z; float onethird = 1.0 / 3.0; float twothirds = 2.0 / 3.0; + // Only used if we calculate UV2 + // TODO this could be improved by changing the order depending on which side is the longest (basically the below works best if size.y is the longest) + float total_h = (size.x + size.z + (2.0 * p_uv2_padding)); + float padding_h = p_uv2_padding / total_h; + float width_h = size.x / total_h; + float depth_h = size.z / total_h; + float total_v = (size.y + size.y + MAX(size.x, size.z) + (3.0 * p_uv2_padding)); + float padding_v = p_uv2_padding / total_v; + float width_v = size.x / total_v; + float height_v = size.y / total_v; + float depth_v = size.z / total_v; + Vector3 start_pos = size * -0.5; // set our bounding box @@ -511,6 +664,7 @@ void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int Vector<Vector3> normals; Vector<float> tangents; Vector<Vector2> uvs; + Vector<Vector2> uv2s; Vector<int> indices; point = 0; @@ -525,18 +679,24 @@ void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int thisrow = point; prevrow = 0; for (j = 0; j <= subdivide_h + 1; j++) { + float v = j; + float v2 = v / (subdivide_w + 1.0); + v /= (2.0 * (subdivide_h + 1.0)); + x = start_pos.x; for (i = 0; i <= subdivide_w + 1; i++) { float u = i; - float v = j; + float u2 = u / (subdivide_w + 1.0); u /= (3.0 * (subdivide_w + 1.0)); - v /= (2.0 * (subdivide_h + 1.0)); // front points.push_back(Vector3(x, -y, -start_pos.z)); // double negative on the Z! normals.push_back(Vector3(0.0, 0.0, 1.0)); ADD_TANGENT(1.0, 0.0, 0.0, 1.0); uvs.push_back(Vector2(u, v)); + if (p_add_uv2) { + uv2s.push_back(Vector2(u2 * width_h, v2 * height_v)); + } point++; // back @@ -544,6 +704,9 @@ void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int normals.push_back(Vector3(0.0, 0.0, -1.0)); ADD_TANGENT(-1.0, 0.0, 0.0, 1.0); uvs.push_back(Vector2(twothirds + u, v)); + if (p_add_uv2) { + uv2s.push_back(Vector2(u2 * width_h, height_v + padding_v + (v2 * height_v))); + } point++; if (i > 0 && j > 0) { @@ -564,33 +727,39 @@ void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int indices.push_back(prevrow + i2 + 1); indices.push_back(thisrow + i2 + 1); indices.push_back(thisrow + i2 - 1); - }; + } x += size.x / (subdivide_w + 1.0); - }; + } y += size.y / (subdivide_h + 1.0); prevrow = thisrow; thisrow = point; - }; + } // left + right y = start_pos.y; thisrow = point; prevrow = 0; for (j = 0; j <= (subdivide_h + 1); j++) { + float v = j; + float v2 = v / (subdivide_h + 1.0); + v /= (2.0 * (subdivide_h + 1.0)); + z = start_pos.z; for (i = 0; i <= (subdivide_d + 1); i++) { float u = i; - float v = j; + float u2 = u / (subdivide_d + 1.0); u /= (3.0 * (subdivide_d + 1.0)); - v /= (2.0 * (subdivide_h + 1.0)); // right points.push_back(Vector3(-start_pos.x, -y, -z)); normals.push_back(Vector3(1.0, 0.0, 0.0)); ADD_TANGENT(0.0, 0.0, -1.0, 1.0); uvs.push_back(Vector2(onethird + u, v)); + if (p_add_uv2) { + uv2s.push_back(Vector2(width_h + padding_h + (u2 * depth_h), v2 * height_v)); + } point++; // left @@ -598,6 +767,9 @@ void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int normals.push_back(Vector3(-1.0, 0.0, 0.0)); ADD_TANGENT(0.0, 0.0, 1.0, 1.0); uvs.push_back(Vector2(u, 0.5 + v)); + if (p_add_uv2) { + uv2s.push_back(Vector2(width_h + padding_h + (u2 * depth_h), height_v + padding_v + (v2 * height_v))); + } point++; if (i > 0 && j > 0) { @@ -618,33 +790,39 @@ void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int indices.push_back(prevrow + i2 + 1); indices.push_back(thisrow + i2 + 1); indices.push_back(thisrow + i2 - 1); - }; + } z += size.z / (subdivide_d + 1.0); - }; + } y += size.y / (subdivide_h + 1.0); prevrow = thisrow; thisrow = point; - }; + } // top + bottom z = start_pos.z; thisrow = point; prevrow = 0; for (j = 0; j <= (subdivide_d + 1); j++) { + float v = j; + float v2 = v / (subdivide_d + 1.0); + v /= (2.0 * (subdivide_d + 1.0)); + x = start_pos.x; for (i = 0; i <= (subdivide_w + 1); i++) { float u = i; - float v = j; + float u2 = u / (subdivide_w + 1.0); u /= (3.0 * (subdivide_w + 1.0)); - v /= (2.0 * (subdivide_d + 1.0)); // top points.push_back(Vector3(-x, -start_pos.y, -z)); normals.push_back(Vector3(0.0, 1.0, 0.0)); ADD_TANGENT(-1.0, 0.0, 0.0, 1.0); uvs.push_back(Vector2(onethird + u, 0.5 + v)); + if (p_add_uv2) { + uv2s.push_back(Vector2(u2 * width_h, ((height_v + padding_v) * 2.0) + (v2 * depth_v))); + } point++; // bottom @@ -652,6 +830,9 @@ void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int normals.push_back(Vector3(0.0, -1.0, 0.0)); ADD_TANGENT(1.0, 0.0, 0.0, 1.0); uvs.push_back(Vector2(twothirds + u, 0.5 + v)); + if (p_add_uv2) { + uv2s.push_back(Vector2(width_h + padding_h + (u2 * depth_h), ((height_v + padding_v) * 2.0) + (v2 * width_v))); + } point++; if (i > 0 && j > 0) { @@ -672,20 +853,23 @@ void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int indices.push_back(prevrow + i2 + 1); indices.push_back(thisrow + i2 + 1); indices.push_back(thisrow + i2 - 1); - }; + } x += size.x / (subdivide_w + 1.0); - }; + } z += size.z / (subdivide_d + 1.0); prevrow = thisrow; thisrow = point; - }; + } p_arr[RS::ARRAY_VERTEX] = points; p_arr[RS::ARRAY_NORMAL] = normals; p_arr[RS::ARRAY_TANGENT] = tangents; p_arr[RS::ARRAY_TEX_UV] = uvs; + if (p_add_uv2) { + p_arr[RS::ARRAY_TEX_UV2] = uv2s; + } p_arr[RS::ARRAY_INDEX] = indices; } @@ -708,6 +892,7 @@ void BoxMesh::_bind_methods() { void BoxMesh::set_size(const Vector3 &p_size) { size = p_size; + _update_lightmap_size(); _request_update(); } @@ -748,18 +933,58 @@ BoxMesh::BoxMesh() {} CylinderMesh */ +void CylinderMesh::_update_lightmap_size() { + if (get_add_uv2()) { + // size must have changed, update lightmap size hint + Size2i _lightmap_size_hint; + float texel_size = get_lightmap_texel_size(); + float padding = get_uv2_padding(); + + float top_circumference = top_radius * Math_PI * 2.0; + float bottom_circumference = bottom_radius * Math_PI * 2.0; + + float _width = MAX(top_circumference, bottom_circumference) / texel_size + padding; + _width = MAX(_width, (((top_radius + bottom_radius) / texel_size) + padding) * 2.0); // this is extremely unlikely to be larger, will only happen if padding is larger then our diameter. + _lightmap_size_hint.x = MAX(1.0, _width); + + float _height = ((height + (MAX(top_radius, bottom_radius) * 2.0)) / texel_size) + (2.0 * padding); + + _lightmap_size_hint.y = MAX(1.0, _height); + + set_lightmap_size_hint(_lightmap_size_hint); + } +} + void CylinderMesh::_create_mesh_array(Array &p_arr) const { - create_mesh_array(p_arr, top_radius, bottom_radius, height, radial_segments, rings, cap_top, cap_bottom); + bool _add_uv2 = get_add_uv2(); + float texel_size = get_lightmap_texel_size(); + float _uv2_padding = get_uv2_padding() * texel_size; + + create_mesh_array(p_arr, top_radius, bottom_radius, height, radial_segments, rings, cap_top, cap_bottom, _add_uv2, _uv2_padding); } -void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float bottom_radius, float height, int radial_segments, int rings, bool cap_top, bool cap_bottom) { +void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float bottom_radius, float height, int radial_segments, int rings, bool cap_top, bool cap_bottom, bool p_add_uv2, const float p_uv2_padding) { int i, j, prevrow, thisrow, point; - float x, y, z, u, v, radius; + float x, y, z, u, v, radius, radius_h; + + // Only used if we calculate UV2 + float top_circumference = top_radius * Math_PI * 2.0; + float bottom_circumference = bottom_radius * Math_PI * 2.0; + float vertical_length = height + MAX(2.0 * top_radius, 2.0 * bottom_radius) + (2.0 * p_uv2_padding); + float height_v = height / vertical_length; + float padding_v = p_uv2_padding / vertical_length; + + float horizonal_length = MAX(MAX(2.0 * (top_radius + bottom_radius + p_uv2_padding), top_circumference + p_uv2_padding), bottom_circumference + p_uv2_padding); + float center_h = 0.5 * (horizonal_length - p_uv2_padding) / horizonal_length; + float top_h = top_circumference / horizonal_length; + float bottom_h = bottom_circumference / horizonal_length; + float padding_h = p_uv2_padding / horizonal_length; Vector<Vector3> points; Vector<Vector3> normals; Vector<float> tangents; Vector<Vector2> uvs; + Vector<Vector2> uv2s; Vector<int> indices; point = 0; @@ -777,6 +1002,7 @@ void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float botto v /= (rings + 1); radius = top_radius + ((bottom_radius - top_radius) * v); + radius_h = top_h + ((bottom_h - top_h) * v); y = height * v; y = (height * 0.5) - y; @@ -793,6 +1019,9 @@ void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float botto normals.push_back(Vector3(x, side_normal_y, z).normalized()); ADD_TANGENT(z, 0.0, -x, 1.0) uvs.push_back(Vector2(u, v * 0.5)); + if (p_add_uv2) { + uv2s.push_back(Vector2(center_h + (u - 0.5) * radius_h, v * height_v)); + } point++; if (i > 0 && j > 0) { @@ -803,14 +1032,20 @@ void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float botto indices.push_back(prevrow + i); indices.push_back(thisrow + i); indices.push_back(thisrow + i - 1); - }; - }; + } + } prevrow = thisrow; thisrow = point; - }; + } - // add top + // Adjust for bottom section, only used if we calculate UV2s. + top_h = top_radius / horizonal_length; + float top_v = top_radius / vertical_length; + bottom_h = bottom_radius / horizonal_length; + float bottom_v = bottom_radius / vertical_length; + + // Add top. if (cap_top && top_radius > 0.0) { y = height * 0.5; @@ -819,6 +1054,9 @@ void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float botto normals.push_back(Vector3(0.0, 1.0, 0.0)); ADD_TANGENT(1.0, 0.0, 0.0, 1.0) uvs.push_back(Vector2(0.25, 0.75)); + if (p_add_uv2) { + uv2s.push_back(Vector2(top_h, height_v + padding_v + MAX(top_v, bottom_v))); + } point++; for (i = 0; i <= radial_segments; i++) { @@ -836,17 +1074,20 @@ void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float botto normals.push_back(Vector3(0.0, 1.0, 0.0)); ADD_TANGENT(1.0, 0.0, 0.0, 1.0) uvs.push_back(Vector2(u, v)); + if (p_add_uv2) { + uv2s.push_back(Vector2(top_h + (x * top_h), height_v + padding_v + MAX(top_v, bottom_v) + (z * top_v))); + } point++; if (i > 0) { indices.push_back(thisrow); indices.push_back(point - 1); indices.push_back(point - 2); - }; - }; - }; + } + } + } - // add bottom + // Add bottom. if (cap_bottom && bottom_radius > 0.0) { y = height * -0.5; @@ -855,6 +1096,9 @@ void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float botto normals.push_back(Vector3(0.0, -1.0, 0.0)); ADD_TANGENT(1.0, 0.0, 0.0, 1.0) uvs.push_back(Vector2(0.75, 0.75)); + if (p_add_uv2) { + uv2s.push_back(Vector2(top_h + top_h + padding_h + bottom_h, height_v + padding_v + MAX(top_v, bottom_v))); + } point++; for (i = 0; i <= radial_segments; i++) { @@ -872,20 +1116,26 @@ void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float botto normals.push_back(Vector3(0.0, -1.0, 0.0)); ADD_TANGENT(1.0, 0.0, 0.0, 1.0) uvs.push_back(Vector2(u, v)); + if (p_add_uv2) { + uv2s.push_back(Vector2(top_h + top_h + padding_h + bottom_h + (x * bottom_h), height_v + padding_v + MAX(top_v, bottom_v) - (z * bottom_v))); + } point++; if (i > 0) { indices.push_back(thisrow); indices.push_back(point - 2); indices.push_back(point - 1); - }; - }; - }; + } + } + } p_arr[RS::ARRAY_VERTEX] = points; p_arr[RS::ARRAY_NORMAL] = normals; p_arr[RS::ARRAY_TANGENT] = tangents; p_arr[RS::ARRAY_TEX_UV] = uvs; + if (p_add_uv2) { + p_arr[RS::ARRAY_TEX_UV2] = uv2s; + } p_arr[RS::ARRAY_INDEX] = indices; } @@ -919,6 +1169,7 @@ void CylinderMesh::_bind_methods() { void CylinderMesh::set_top_radius(const float p_radius) { top_radius = p_radius; + _update_lightmap_size(); _request_update(); } @@ -928,6 +1179,7 @@ float CylinderMesh::get_top_radius() const { void CylinderMesh::set_bottom_radius(const float p_radius) { bottom_radius = p_radius; + _update_lightmap_size(); _request_update(); } @@ -937,6 +1189,7 @@ float CylinderMesh::get_bottom_radius() const { void CylinderMesh::set_height(const float p_height) { height = p_height; + _update_lightmap_size(); _request_update(); } @@ -986,10 +1239,26 @@ CylinderMesh::CylinderMesh() {} PlaneMesh */ +void PlaneMesh::_update_lightmap_size() { + if (get_add_uv2()) { + // size must have changed, update lightmap size hint + Size2i _lightmap_size_hint; + float texel_size = get_lightmap_texel_size(); + float padding = get_uv2_padding(); + + _lightmap_size_hint.x = MAX(1.0, (size.x / texel_size) + padding); + _lightmap_size_hint.y = MAX(1.0, (size.y / texel_size) + padding); + + set_lightmap_size_hint(_lightmap_size_hint); + } +} + void PlaneMesh::_create_mesh_array(Array &p_arr) const { int i, j, prevrow, thisrow, point; float x, z; + // Plane mesh can use default UV2 calculation as implemented in Primitive Mesh + Size2 start_pos = size * -0.5; Vector3 normal = Vector3(0.0, 1.0, 0.0); @@ -1043,15 +1312,15 @@ void PlaneMesh::_create_mesh_array(Array &p_arr) const { indices.push_back(prevrow + i); indices.push_back(thisrow + i); indices.push_back(thisrow + i - 1); - }; + } x += size.x / (subdivide_w + 1.0); - }; + } z += size.y / (subdivide_d + 1.0); prevrow = thisrow; thisrow = point; - }; + } p_arr[RS::ARRAY_VERTEX] = points; p_arr[RS::ARRAY_NORMAL] = normals; @@ -1088,6 +1357,7 @@ void PlaneMesh::_bind_methods() { void PlaneMesh::set_size(const Size2 &p_size) { size = p_size; + _update_lightmap_size(); _request_update(); } @@ -1137,12 +1407,49 @@ PlaneMesh::PlaneMesh() {} PrismMesh */ +void PrismMesh::_update_lightmap_size() { + if (get_add_uv2()) { + // size must have changed, update lightmap size hint + Size2i _lightmap_size_hint; + float texel_size = get_lightmap_texel_size(); + float padding = get_uv2_padding(); + + // left_to_right does not effect the surface area of the prism so we ignore that. + // TODO we could combine the two triangles and save some space but we need to re-align the uv1 and adjust the tangent. + + float width = (size.x + size.z) / texel_size; + float length = (size.y + size.y + size.z) / texel_size; + + _lightmap_size_hint.x = MAX(1.0, width) + 2.0 * padding; + _lightmap_size_hint.y = MAX(1.0, length) + 3.0 * padding; + + set_lightmap_size_hint(_lightmap_size_hint); + } +} + void PrismMesh::_create_mesh_array(Array &p_arr) const { int i, j, prevrow, thisrow, point; float x, y, z; float onethird = 1.0 / 3.0; float twothirds = 2.0 / 3.0; + // Only used if we calculate UV2 + bool _add_uv2 = get_add_uv2(); + float texel_size = get_lightmap_texel_size(); + float _uv2_padding = get_uv2_padding() * texel_size; + + float horizontal_total = size.x + size.z + 2.0 * _uv2_padding; + float width_h = size.x / horizontal_total; + float depth_h = size.z / horizontal_total; + float padding_h = _uv2_padding / horizontal_total; + + float vertical_total = (size.y + size.y + size.z) + (3.0 * _uv2_padding); + float height_v = size.y / vertical_total; + float depth_v = size.z / vertical_total; + float padding_v = _uv2_padding / vertical_total; + + // and start building + Vector3 start_pos = size * -0.5; // set our bounding box @@ -1151,6 +1458,7 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const { Vector<Vector3> normals; Vector<float> tangents; Vector<Vector2> uvs; + Vector<Vector2> uv2s; Vector<int> indices; point = 0; @@ -1171,12 +1479,15 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const { float offset_front = (1.0 - scale) * onethird * left_to_right; float offset_back = (1.0 - scale) * onethird * (1.0 - left_to_right); + float v = j; + float v2 = j / (subdivide_h + 1.0); + v /= (2.0 * (subdivide_h + 1.0)); + x = 0.0; for (i = 0; i <= (subdivide_w + 1); i++) { float u = i; - float v = j; + float u2 = i / (subdivide_w + 1.0); u /= (3.0 * (subdivide_w + 1.0)); - v /= (2.0 * (subdivide_h + 1.0)); u *= scale; @@ -1185,6 +1496,9 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const { normals.push_back(Vector3(0.0, 0.0, 1.0)); ADD_TANGENT(1.0, 0.0, 0.0, 1.0); uvs.push_back(Vector2(offset_front + u, v)); + if (_add_uv2) { + uv2s.push_back(Vector2(u2 * scale * width_h, v2 * height_v)); + } point++; /* back */ @@ -1192,6 +1506,9 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const { normals.push_back(Vector3(0.0, 0.0, -1.0)); ADD_TANGENT(-1.0, 0.0, 0.0, 1.0); uvs.push_back(Vector2(twothirds + offset_back + u, v)); + if (_add_uv2) { + uv2s.push_back(Vector2(u2 * scale * width_h, height_v + padding_v + v2 * height_v)); + } point++; if (i > 0 && j == 1) { @@ -1224,15 +1541,15 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const { indices.push_back(prevrow + i2 + 1); indices.push_back(thisrow + i2 + 1); indices.push_back(thisrow + i2 - 1); - }; + } x += scale * size.x / (subdivide_w + 1.0); - }; + } y += size.y / (subdivide_h + 1.0); prevrow = thisrow; thisrow = point; - }; + } /* left + right */ Vector3 normal_left, normal_right; @@ -1246,6 +1563,10 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const { thisrow = point; prevrow = 0; for (j = 0; j <= (subdivide_h + 1); j++) { + float v = j; + float v2 = j / (subdivide_h + 1.0); + v /= (2.0 * (subdivide_h + 1.0)); + float left, right; float scale = (y - start_pos.y) / size.y; @@ -1255,15 +1576,17 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const { z = start_pos.z; for (i = 0; i <= (subdivide_d + 1); i++) { float u = i; - float v = j; + float u2 = u / (subdivide_d + 1.0); u /= (3.0 * (subdivide_d + 1.0)); - v /= (2.0 * (subdivide_h + 1.0)); /* right */ points.push_back(Vector3(right, -y, -z)); normals.push_back(normal_right); ADD_TANGENT(0.0, 0.0, -1.0, 1.0); uvs.push_back(Vector2(onethird + u, v)); + if (_add_uv2) { + uv2s.push_back(Vector2(width_h + padding_h + u2 * depth_h, v2 * height_v)); + } point++; /* left */ @@ -1271,6 +1594,9 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const { normals.push_back(normal_left); ADD_TANGENT(0.0, 0.0, 1.0, 1.0); uvs.push_back(Vector2(u, 0.5 + v)); + if (_add_uv2) { + uv2s.push_back(Vector2(width_h + padding_h + u2 * depth_h, height_v + padding_v + v2 * height_v)); + } point++; if (i > 0 && j > 0) { @@ -1291,33 +1617,39 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const { indices.push_back(prevrow + i2 + 1); indices.push_back(thisrow + i2 + 1); indices.push_back(thisrow + i2 - 1); - }; + } z += size.z / (subdivide_d + 1.0); - }; + } y += size.y / (subdivide_h + 1.0); prevrow = thisrow; thisrow = point; - }; + } /* bottom */ z = start_pos.z; thisrow = point; prevrow = 0; for (j = 0; j <= (subdivide_d + 1); j++) { + float v = j; + float v2 = v / (subdivide_d + 1.0); + v /= (2.0 * (subdivide_d + 1.0)); + x = start_pos.x; for (i = 0; i <= (subdivide_w + 1); i++) { float u = i; - float v = j; + float u2 = u / (subdivide_w + 1.0); u /= (3.0 * (subdivide_w + 1.0)); - v /= (2.0 * (subdivide_d + 1.0)); /* bottom */ points.push_back(Vector3(x, start_pos.y, -z)); normals.push_back(Vector3(0.0, -1.0, 0.0)); ADD_TANGENT(1.0, 0.0, 0.0, 1.0); uvs.push_back(Vector2(twothirds + u, 0.5 + v)); + if (_add_uv2) { + uv2s.push_back(Vector2(u2 * width_h, 2.0 * (height_v + padding_v) + v2 * depth_v)); + } point++; if (i > 0 && j > 0) { @@ -1328,20 +1660,23 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const { indices.push_back(prevrow + i); indices.push_back(thisrow + i); indices.push_back(thisrow + i - 1); - }; + } x += size.x / (subdivide_w + 1.0); - }; + } z += size.z / (subdivide_d + 1.0); prevrow = thisrow; thisrow = point; - }; + } p_arr[RS::ARRAY_VERTEX] = points; p_arr[RS::ARRAY_NORMAL] = normals; p_arr[RS::ARRAY_TANGENT] = tangents; p_arr[RS::ARRAY_TEX_UV] = uvs; + if (_add_uv2) { + p_arr[RS::ARRAY_TEX_UV2] = uv2s; + } p_arr[RS::ARRAY_INDEX] = indices; } @@ -1377,6 +1712,7 @@ float PrismMesh::get_left_to_right() const { void PrismMesh::set_size(const Vector3 &p_size) { size = p_size; + _update_lightmap_size(); _request_update(); } @@ -1417,22 +1753,50 @@ PrismMesh::PrismMesh() {} SphereMesh */ +void SphereMesh::_update_lightmap_size() { + if (get_add_uv2()) { + // size must have changed, update lightmap size hint + Size2i _lightmap_size_hint; + float texel_size = get_lightmap_texel_size(); + float padding = get_uv2_padding(); + + float _width = radius * Math_TAU; + _lightmap_size_hint.x = MAX(1.0, (_width / texel_size) + padding); + float _height = (is_hemisphere ? 1.0 : 0.5) * height * Math_PI; // note, with hemisphere height is our radius, while with a full sphere it is the diameter.. + _lightmap_size_hint.y = MAX(1.0, (_height / texel_size) + padding); + + set_lightmap_size_hint(_lightmap_size_hint); + } +} + void SphereMesh::_create_mesh_array(Array &p_arr) const { - create_mesh_array(p_arr, radius, height, radial_segments, rings, is_hemisphere); + bool _add_uv2 = get_add_uv2(); + float texel_size = get_lightmap_texel_size(); + float _uv2_padding = get_uv2_padding() * texel_size; + + create_mesh_array(p_arr, radius, height, radial_segments, rings, is_hemisphere, _add_uv2, _uv2_padding); } -void SphereMesh::create_mesh_array(Array &p_arr, float radius, float height, int radial_segments, int rings, bool is_hemisphere) { +void SphereMesh::create_mesh_array(Array &p_arr, float radius, float height, int radial_segments, int rings, bool is_hemisphere, bool p_add_uv2, const float p_uv2_padding) { int i, j, prevrow, thisrow, point; float x, y, z; float scale = height * (is_hemisphere ? 1.0 : 0.5); + // Only used if we calculate UV2 + float circumference = radius * Math_TAU; + float horizontal_length = circumference + p_uv2_padding; + float center_h = 0.5 * circumference / horizontal_length; + + float height_v = scale * Math_PI / ((scale * Math_PI) + p_uv2_padding); + // set our bounding box Vector<Vector3> points; Vector<Vector3> normals; Vector<float> tangents; Vector<Vector2> uvs; + Vector<Vector2> uv2s; Vector<int> indices; point = 0; @@ -1467,9 +1831,13 @@ void SphereMesh::create_mesh_array(Array &p_arr, float radius, float height, int points.push_back(p); Vector3 normal = Vector3(x * w * scale, radius * (y / scale), z * w * scale); normals.push_back(normal.normalized()); - }; + } ADD_TANGENT(z, 0.0, -x, 1.0) uvs.push_back(Vector2(u, v)); + if (p_add_uv2) { + float w_h = w * 2.0 * center_h; + uv2s.push_back(Vector2(center_h + ((u - 0.5) * w_h), v * height_v)); + } point++; if (i > 0 && j > 0) { @@ -1480,17 +1848,20 @@ void SphereMesh::create_mesh_array(Array &p_arr, float radius, float height, int indices.push_back(prevrow + i); indices.push_back(thisrow + i); indices.push_back(thisrow + i - 1); - }; - }; + } + } prevrow = thisrow; thisrow = point; - }; + } p_arr[RS::ARRAY_VERTEX] = points; p_arr[RS::ARRAY_NORMAL] = normals; p_arr[RS::ARRAY_TANGENT] = tangents; p_arr[RS::ARRAY_TEX_UV] = uvs; + if (p_add_uv2) { + p_arr[RS::ARRAY_TEX_UV2] = uv2s; + } p_arr[RS::ARRAY_INDEX] = indices; } @@ -1517,6 +1888,7 @@ void SphereMesh::_bind_methods() { void SphereMesh::set_radius(const float p_radius) { radius = p_radius; + _update_lightmap_size(); _request_update(); } @@ -1526,6 +1898,7 @@ float SphereMesh::get_radius() const { void SphereMesh::set_height(const float p_height) { height = p_height; + _update_lightmap_size(); _request_update(); } @@ -1553,6 +1926,7 @@ int SphereMesh::get_rings() const { void SphereMesh::set_is_hemisphere(const bool p_is_hemisphere) { is_hemisphere = p_is_hemisphere; + _update_lightmap_size(); _request_update(); } @@ -1566,6 +1940,31 @@ SphereMesh::SphereMesh() {} TorusMesh */ +void TorusMesh::_update_lightmap_size() { + if (get_add_uv2()) { + // size must have changed, update lightmap size hint + Size2i _lightmap_size_hint; + float texel_size = get_lightmap_texel_size(); + float padding = get_uv2_padding(); + + float min_radius = inner_radius; + float max_radius = outer_radius; + + if (min_radius > max_radius) { + SWAP(min_radius, max_radius); + } + + float radius = (max_radius - min_radius) * 0.5; + + float _width = max_radius * Math_TAU; + _lightmap_size_hint.x = MAX(1.0, (_width / texel_size) + padding); + float _height = radius * Math_TAU; + _lightmap_size_hint.y = MAX(1.0, (_height / texel_size) + padding); + + set_lightmap_size_hint(_lightmap_size_hint); + } +} + void TorusMesh::_create_mesh_array(Array &p_arr) const { // set our bounding box @@ -1573,6 +1972,7 @@ void TorusMesh::_create_mesh_array(Array &p_arr) const { Vector<Vector3> normals; Vector<float> tangents; Vector<Vector2> uvs; + Vector<Vector2> uv2s; Vector<int> indices; #define ADD_TANGENT(m_x, m_y, m_z, m_d) \ @@ -1592,6 +1992,17 @@ void TorusMesh::_create_mesh_array(Array &p_arr) const { float radius = (max_radius - min_radius) * 0.5; + // Only used if we calculate UV2 + bool _add_uv2 = get_add_uv2(); + float texel_size = get_lightmap_texel_size(); + float _uv2_padding = get_uv2_padding() * texel_size; + + float horizontal_total = max_radius * Math_TAU + _uv2_padding; + float max_h = max_radius * Math_TAU / horizontal_total; + float delta_h = (max_radius - min_radius) * Math_TAU / horizontal_total; + + float height_v = radius * Math_TAU / (radius * Math_TAU + _uv2_padding); + for (int i = 0; i <= rings; i++) { int prevrow = (i - 1) * (ring_segments + 1); int thisrow = i * (ring_segments + 1); @@ -1607,10 +2018,17 @@ void TorusMesh::_create_mesh_array(Array &p_arr) const { Vector2 normalj = Vector2(-Math::cos(angj), Math::sin(angj)); Vector2 normalk = normalj * radius + Vector2(min_radius + radius, 0); + float offset_h = 0.5 * (1.0 - normalj.x) * delta_h; + float adj_h = max_h - offset_h; + offset_h *= 0.5; + points.push_back(Vector3(normali.x * normalk.x, normalk.y, normali.y * normalk.x)); normals.push_back(Vector3(normali.x * normalj.x, normalj.y, normali.y * normalj.x)); ADD_TANGENT(-Math::cos(angi), 0.0, Math::sin(angi), 1.0); uvs.push_back(Vector2(inci, incj)); + if (_add_uv2) { + uv2s.push_back(Vector2(offset_h + inci * adj_h, incj * height_v)); + } if (i > 0 && j > 0) { indices.push_back(thisrow + j - 1); @@ -1628,6 +2046,9 @@ void TorusMesh::_create_mesh_array(Array &p_arr) const { p_arr[RS::ARRAY_NORMAL] = normals; p_arr[RS::ARRAY_TANGENT] = tangents; p_arr[RS::ARRAY_TEX_UV] = uvs; + if (_add_uv2) { + p_arr[RS::ARRAY_TEX_UV2] = uv2s; + } p_arr[RS::ARRAY_INDEX] = indices; } @@ -1785,6 +2206,8 @@ Transform3D TubeTrailMesh::get_builtin_bind_pose(int p_index) const { } void TubeTrailMesh::_create_mesh_array(Array &p_arr) const { + // Seeing use case for TubeTrailMesh, no need to do anything more then default UV2 calculation + PackedVector3Array points; PackedVector3Array normals; PackedFloat32Array tangents; @@ -1920,9 +2343,9 @@ void TubeTrailMesh::_create_mesh_array(Array &p_arr) const { indices.push_back(thisrow); indices.push_back(point - 1); indices.push_back(point - 2); - }; - }; - }; + } + } + } float scale_neg = 1.0; if (curve.is_valid() && curve->get_point_count() > 0) { @@ -1983,9 +2406,9 @@ void TubeTrailMesh::_create_mesh_array(Array &p_arr) const { indices.push_back(thisrow); indices.push_back(point - 2); indices.push_back(point - 1); - }; - }; - }; + } + } + } p_arr[RS::ARRAY_VERTEX] = points; p_arr[RS::ARRAY_NORMAL] = normals; @@ -2109,6 +2532,8 @@ Transform3D RibbonTrailMesh::get_builtin_bind_pose(int p_index) const { } void RibbonTrailMesh::_create_mesh_array(Array &p_arr) const { + // Seeing use case of ribbon trail mesh, no need to implement special UV2 calculation + PackedVector3Array points; PackedVector3Array normals; PackedFloat32Array tangents; diff --git a/scene/resources/primitive_meshes.h b/scene/resources/primitive_meshes.h index ee61f0ac55..06f9781b84 100644 --- a/scene/resources/primitive_meshes.h +++ b/scene/resources/primitive_meshes.h @@ -56,6 +56,9 @@ private: Ref<Material> material; bool flip_faces = false; + bool add_uv2 = false; + float uv2_padding = 2.0; + // make sure we do an update after we've finished constructing our object mutable bool pending_request = true; void _update() const; @@ -70,6 +73,10 @@ protected: void _request_update(); GDVIRTUAL0RC(Array, _create_mesh_array) + Vector2 get_uv2_scale(Vector2 p_margin_scale = Vector2(1.0, 1.0)) const; + float get_lightmap_texel_size() const; + virtual void _update_lightmap_size(){}; + public: virtual int get_surface_count() const override; virtual int surface_get_array_len(int p_idx) const override; @@ -98,6 +105,12 @@ public: void set_flip_faces(bool p_enable); bool get_flip_faces() const; + void set_add_uv2(bool p_enable); + bool get_add_uv2() const { return add_uv2; } + + void set_uv2_padding(float p_padding); + float get_uv2_padding() const { return uv2_padding; } + PrimitiveMesh(); ~PrimitiveMesh(); }; @@ -118,8 +131,10 @@ protected: static void _bind_methods(); virtual void _create_mesh_array(Array &p_arr) const override; + virtual void _update_lightmap_size() override; + public: - static void create_mesh_array(Array &p_arr, float radius, float height, int radial_segments = 64, int rings = 8); + static void create_mesh_array(Array &p_arr, float radius, float height, int radial_segments = 64, int rings = 8, bool p_add_uv2 = false, const float p_uv2_padding = 1.0); void set_radius(const float p_radius); float get_radius() const; @@ -152,8 +167,10 @@ protected: static void _bind_methods(); virtual void _create_mesh_array(Array &p_arr) const override; + virtual void _update_lightmap_size() override; + public: - static void create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w = 0, int subdivide_h = 0, int subdivide_d = 0); + static void create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w = 0, int subdivide_h = 0, int subdivide_d = 0, bool p_add_uv2 = false, const float p_uv2_padding = 1.0); void set_size(const Vector3 &p_size); Vector3 get_size() const; @@ -190,8 +207,10 @@ protected: static void _bind_methods(); virtual void _create_mesh_array(Array &p_arr) const override; + virtual void _update_lightmap_size() override; + public: - static void create_mesh_array(Array &p_arr, float top_radius, float bottom_radius, float height, int radial_segments = 64, int rings = 4, bool cap_top = true, bool cap_bottom = true); + static void create_mesh_array(Array &p_arr, float top_radius, float bottom_radius, float height, int radial_segments = 64, int rings = 4, bool cap_top = true, bool cap_bottom = true, bool p_add_uv2 = false, const float p_uv2_padding = 1.0); void set_top_radius(const float p_radius); float get_top_radius() const; @@ -241,6 +260,8 @@ protected: static void _bind_methods(); virtual void _create_mesh_array(Array &p_arr) const override; + virtual void _update_lightmap_size() override; + public: void set_size(const Size2 &p_size); Size2 get_size() const; @@ -292,6 +313,8 @@ protected: static void _bind_methods(); virtual void _create_mesh_array(Array &p_arr) const override; + virtual void _update_lightmap_size() override; + public: void set_left_to_right(const float p_left_to_right); float get_left_to_right() const; @@ -328,8 +351,10 @@ protected: static void _bind_methods(); virtual void _create_mesh_array(Array &p_arr) const override; + virtual void _update_lightmap_size() override; + public: - static void create_mesh_array(Array &p_arr, float radius, float height, int radial_segments = 64, int rings = 32, bool is_hemisphere = false); + static void create_mesh_array(Array &p_arr, float radius, float height, int radial_segments = 64, int rings = 32, bool is_hemisphere = false, bool p_add_uv2 = false, const float p_uv2_padding = 1.0); void set_radius(const float p_radius); float get_radius() const; @@ -365,6 +390,8 @@ protected: static void _bind_methods(); virtual void _create_mesh_array(Array &p_arr) const override; + virtual void _update_lightmap_size() override; + public: void set_inner_radius(const float p_inner_radius); float get_inner_radius() const; diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp index 85b538b1d9..712a67547b 100644 --- a/scene/resources/resource_format_text.cpp +++ b/scene/resources/resource_format_text.cpp @@ -217,7 +217,7 @@ Ref<PackedScene> ResourceLoaderText::_parse_node_tag(VariantParser::ResourcePars if (next_tag.fields.has("type")) { type = packed_scene->get_state()->add_name(next_tag.fields["type"]); } else { - type = SceneState::TYPE_INSTANCED; //no type? assume this was instantiated + type = SceneState::TYPE_INSTANTIATED; //no type? assume this was instantiated } HashSet<StringName> path_properties; @@ -256,7 +256,7 @@ Ref<PackedScene> ResourceLoaderText::_parse_node_tag(VariantParser::ResourcePars if (next_tag.fields.has("owner")) { owner = packed_scene->get_state()->add_node_path(next_tag.fields["owner"]); } else { - if (parent != -1 && !(type == SceneState::TYPE_INSTANCED && instance == -1)) { + if (parent != -1 && !(type == SceneState::TYPE_INSTANTIATED && instance == -1)) { owner = 0; //if no owner, owner is root } } @@ -511,6 +511,7 @@ Error ResourceLoaderText::load() { if (error) { _printerr(); + return error; } resource_current++; @@ -884,6 +885,7 @@ void ResourceLoaderText::get_dependencies(Ref<FileAccess> p_f, List<String> *p_d error_text = "Unexpected end of file"; _printerr(); error = ERR_FILE_CORRUPT; + return; } } } diff --git a/scene/resources/skeleton_modification_2d_fabrik.h b/scene/resources/skeleton_modification_2d_fabrik.h index 4a875d039f..0ca6582965 100644 --- a/scene/resources/skeleton_modification_2d_fabrik.h +++ b/scene/resources/skeleton_modification_2d_fabrik.h @@ -68,8 +68,8 @@ private: float chain_tolarance = 0.01; int chain_max_iterations = 10; int chain_iterations = 0; - Transform2D target_global_pose = Transform2D(); - Transform2D origin_global_pose = Transform2D(); + Transform2D target_global_pose; + Transform2D origin_global_pose; void fabrik_joint_update_bone2d_cache(int p_joint_idx); void chain_backwards(); diff --git a/scene/resources/skeleton_modification_3d_fabrik.h b/scene/resources/skeleton_modification_3d_fabrik.h index e2e490d636..3e3aa5e587 100644 --- a/scene/resources/skeleton_modification_3d_fabrik.h +++ b/scene/resources/skeleton_modification_3d_fabrik.h @@ -47,7 +47,7 @@ private: bool auto_calculate_length = true; bool use_tip_node = false; - NodePath tip_node = NodePath(); + NodePath tip_node; ObjectID tip_node_cache; bool use_target_basis = false; @@ -68,8 +68,8 @@ private: void update_joint_tip_cache(int p_joint_idx); int final_joint_idx = 0; - Transform3D target_global_pose = Transform3D(); - Transform3D origin_global_pose = Transform3D(); + Transform3D target_global_pose; + Transform3D origin_global_pose; void chain_backwards(); void chain_forwards(); diff --git a/scene/resources/syntax_highlighter.cpp b/scene/resources/syntax_highlighter.cpp index f1eddd8ffc..cb5cb4ef96 100644 --- a/scene/resources/syntax_highlighter.cpp +++ b/scene/resources/syntax_highlighter.cpp @@ -336,7 +336,7 @@ Dictionary CodeHighlighter::_get_line_syntax_highlighting_impl(int p_line) { } String word = str.substr(j, to - j); - Color col = Color(); + Color col; if (keywords.has(word)) { col = keywords[word]; } else if (member_keywords.has(word)) { diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index a6fb359051..b5754caa6a 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -64,6 +64,7 @@ bool Texture2D::is_pixel_opaque(int p_x, int p_y) const { GDVIRTUAL_CALL(_is_pixel_opaque, p_x, p_y, ret); return ret; } + bool Texture2D::has_alpha() const { bool ret = true; GDVIRTUAL_CALL(_has_alpha, ret); diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index 3caf6484d9..d4ad81614d 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -3789,7 +3789,7 @@ Vector2i TileSetAtlasSource::get_atlas_grid_size() const { Size2i valid_area = txt->get_size() - margins; // Compute the number of valid tiles in the tiles atlas - Size2i grid_size = Size2i(); + Size2i grid_size; if (valid_area.x >= texture_region_size.x && valid_area.y >= texture_region_size.y) { valid_area -= texture_region_size; grid_size = Size2i(1, 1) + valid_area / (texture_region_size + separation); diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h index 8f175e99a6..9f465a17e9 100644 --- a/scene/resources/tile_set.h +++ b/scene/resources/tile_set.h @@ -785,7 +785,7 @@ private: bool flip_h = false; bool flip_v = false; bool transpose = false; - Vector2i tex_offset = Vector2i(); + Vector2i tex_offset; Ref<Material> material = Ref<Material>(); Color modulate = Color(1.0, 1.0, 1.0, 1.0); int z_index = 0; |