diff options
Diffstat (limited to 'scene/resources')
67 files changed, 5606 insertions, 2336 deletions
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index e6a74e7685..b4eec2530b 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -29,9 +29,9 @@ /*************************************************************************/ #include "animation.h" -#include "scene/scene_string_names.h" #include "core/math/geometry_3d.h" +#include "scene/scene_string_names.h" bool Animation::_set(const StringName &p_name, const Variant &p_value) { String name = p_name; @@ -79,15 +79,16 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) { TransformTrack *tt = static_cast<TransformTrack *>(tracks[track]); Vector<real_t> values = p_value; int vcount = values.size(); - ERR_FAIL_COND_V(vcount % 12, false); // should be multiple of 12 + ERR_FAIL_COND_V(vcount % TRANSFORM_TRACK_SIZE, false); const real_t *r = values.ptr(); - tt->transforms.resize(vcount / 12); + int64_t count = vcount / TRANSFORM_TRACK_SIZE; + tt->transforms.resize(count); - for (int i = 0; i < (vcount / 12); i++) { + for (int i = 0; i < count; i++) { TKey<TransformKey> &tk = tt->transforms.write[i]; - const real_t *ofs = &r[i * 12]; + const real_t *ofs = &r[i * TRANSFORM_TRACK_SIZE]; tk.time = ofs[0]; tk.transition = ofs[1]; @@ -354,7 +355,7 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const { if (track_get_type(track) == TYPE_TRANSFORM3D) { Vector<real_t> keys; int kk = track_get_key_count(track); - keys.resize(kk * sizeof(Transform3D)); + keys.resize(kk * TRANSFORM_TRACK_SIZE); real_t *w = keys.ptrw(); diff --git a/scene/resources/animation.h b/scene/resources/animation.h index 6227f6967f..9a410bd566 100644 --- a/scene/resources/animation.h +++ b/scene/resources/animation.h @@ -92,6 +92,9 @@ private: Vector3 scale; }; + // Not necessarily the same size as Transform3D. The amount of numbers in Animation::Key and TransformKey. + const int32_t TRANSFORM_TRACK_SIZE = 12; + /* TRANSFORM TRACK */ struct TransformTrack : public Track { diff --git a/scene/resources/audio_stream_sample.cpp b/scene/resources/audio_stream_sample.cpp index ef070589e4..d018103e64 100644 --- a/scene/resources/audio_stream_sample.cpp +++ b/scene/resources/audio_stream_sample.cpp @@ -221,12 +221,12 @@ void AudioStreamPlaybackSample::do_resample(const Depth *p_src, AudioFrame *p_ds } } -void AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) { +int AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) { if (!base->data || !active) { for (int i = 0; i < p_frames; i++) { p_buffer[i] = AudioFrame(0, 0); } - return; + return 0; } int len = base->data_bytes; @@ -395,12 +395,15 @@ void AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, in } if (todo) { + int mixed_frames = p_frames - todo; //bit was missing from mix int todo_ofs = p_frames - todo; for (int i = todo_ofs; i < p_frames; i++) { p_buffer[i] = AudioFrame(0, 0); } + return mixed_frames; } + return p_frames; } AudioStreamPlaybackSample::AudioStreamPlaybackSample() {} @@ -477,6 +480,10 @@ float AudioStreamSample::get_length() const { return float(len) / mix_rate; } +bool AudioStreamSample::is_monophonic() const { + return false; +} + void AudioStreamSample::set_data(const Vector<uint8_t> &p_data) { AudioServer::get_singleton()->lock(); if (data) { diff --git a/scene/resources/audio_stream_sample.h b/scene/resources/audio_stream_sample.h index 70b8ba79ad..24198e3c98 100644 --- a/scene/resources/audio_stream_sample.h +++ b/scene/resources/audio_stream_sample.h @@ -73,7 +73,7 @@ public: virtual float get_playback_position() const override; virtual void seek(float p_time) override; - virtual void mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) override; + virtual int mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) override; AudioStreamPlaybackSample(); }; @@ -136,6 +136,8 @@ public: virtual float get_length() const override; //if supported, otherwise return 0 + virtual bool is_monophonic() const override; + void set_data(const Vector<uint8_t> &p_data); Vector<uint8_t> get_data() const; diff --git a/scene/resources/bit_map.cpp b/scene/resources/bit_map.cpp index de557494c3..49ed9dcb82 100644 --- a/scene/resources/bit_map.cpp +++ b/scene/resources/bit_map.cpp @@ -48,7 +48,7 @@ void BitMap::create_from_image_alpha(const Ref<Image> &p_image, float p_threshol img->convert(Image::FORMAT_LA8); ERR_FAIL_COND(img->get_format() != Image::FORMAT_LA8); - create(Size2(img->get_width(), img->get_height())); + create(img->get_size()); const uint8_t *r = img->get_data().ptr(); uint8_t *w = bitmask.ptrw(); diff --git a/scene/resources/canvas_item_material.cpp b/scene/resources/canvas_item_material.cpp index 7501efea9e..b291724c4c 100644 --- a/scene/resources/canvas_item_material.cpp +++ b/scene/resources/canvas_item_material.cpp @@ -135,7 +135,7 @@ void CanvasItemMaterial::_update_shader() { code += " particle_frame = mod(particle_frame, particle_total_frames);\n"; code += " }"; code += " UV /= vec2(h_frames, v_frames);\n"; - code += " UV += vec2(mod(particle_frame, h_frames) / h_frames, floor(particle_frame / h_frames) / v_frames);\n"; + code += " UV += vec2(mod(particle_frame, h_frames) / h_frames, floor((particle_frame + 0.5) / h_frames) / v_frames);\n"; code += "}\n"; } @@ -161,7 +161,7 @@ void CanvasItemMaterial::flush_changes() { void CanvasItemMaterial::_queue_shader_change() { MutexLock lock(material_mutex); - if (!element.in_list()) { + if (is_initialized && !element.in_list()) { dirty_materials->add(&element); } } @@ -287,6 +287,7 @@ CanvasItemMaterial::CanvasItemMaterial() : set_particles_anim_loop(false); current_key.invalid_key = 1; + is_initialized = true; _queue_shader_change(); } diff --git a/scene/resources/canvas_item_material.h b/scene/resources/canvas_item_material.h index 0a813e0ae5..37cd4de136 100644 --- a/scene/resources/canvas_item_material.h +++ b/scene/resources/canvas_item_material.h @@ -102,6 +102,7 @@ private: _FORCE_INLINE_ void _queue_shader_change(); _FORCE_INLINE_ bool _is_shader_dirty() const; + bool is_initialized = false; BlendMode blend_mode = BLEND_MODE_MIX; LightMode light_mode = LIGHT_MODE_NORMAL; bool particles_animation = false; diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp index 3b666640f8..9dc76dcf44 100644 --- a/scene/resources/curve.cpp +++ b/scene/resources/curve.cpp @@ -662,19 +662,27 @@ void Curve2D::_bake() const { if (points.size() == 0) { baked_point_cache.resize(0); + baked_dist_cache.resize(0); return; } if (points.size() == 1) { baked_point_cache.resize(1); baked_point_cache.set(0, points[0].pos); + + baked_dist_cache.resize(1); + baked_dist_cache.set(0, 0.0); return; } Vector2 pos = points[0].pos; + float dist = 0.0; + List<Vector2> pointlist; + List<float> distlist; pointlist.push_back(pos); //start always from origin + distlist.push_back(0.0); for (int i = 0; i < points.size() - 1; i++) { float step = 0.1; // at least 10 substeps ought to be enough? @@ -712,7 +720,10 @@ void Curve2D::_bake() const { pos = npp; p = mid; + dist += d; + pointlist.push_back(pos); + distlist.push_back(dist); } else { p = np; } @@ -722,16 +733,20 @@ void Curve2D::_bake() const { Vector2 lastpos = points[points.size() - 1].pos; float rem = pos.distance_to(lastpos); - baked_max_ofs = (pointlist.size() - 1) * bake_interval + rem; + dist += rem; + baked_max_ofs = dist; pointlist.push_back(lastpos); + distlist.push_back(dist); baked_point_cache.resize(pointlist.size()); + baked_dist_cache.resize(distlist.size()); + Vector2 *w = baked_point_cache.ptrw(); - int idx = 0; + float *wd = baked_dist_cache.ptrw(); - for (const Vector2 &E : pointlist) { - w[idx] = E; - idx++; + for (int i = 0; i < pointlist.size(); i++) { + w[i] = pointlist[i]; + wd[i] = distlist[i]; } } @@ -766,19 +781,26 @@ Vector2 Curve2D::interpolate_baked(float p_offset, bool p_cubic) const { return r[bpc - 1]; } - int idx = Math::floor((double)p_offset / (double)bake_interval); - float frac = Math::fmod(p_offset, (float)bake_interval); - - if (idx >= bpc - 1) { - return r[bpc - 1]; - } else if (idx == bpc - 2) { - if (frac > 0) { - frac /= Math::fmod(baked_max_ofs, bake_interval); + int start = 0, end = bpc, idx = (end + start) / 2; + // binary search to find baked points + while (start < idx) { + float offset = baked_dist_cache[idx]; + if (p_offset <= offset) { + end = idx; + } else { + start = idx; } - } else { - frac /= bake_interval; + idx = (end + start) / 2; } + float offset_begin = baked_dist_cache[idx]; + float offset_end = baked_dist_cache[idx + 1]; + + float idx_interval = offset_end - offset_begin; + ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, Vector2(), "failed to find baked segment"); + + float frac = (p_offset - offset_begin) / idx_interval; + if (p_cubic) { Vector2 pre = idx > 0 ? r[idx - 1] : r[idx]; Vector2 post = (idx < (bpc - 2)) ? r[idx + 2] : r[idx + 1]; @@ -944,9 +966,9 @@ PackedVector2Array Curve2D::tessellate(int p_max_stages, float p_tolerance) cons int pidx = 0; for (int i = 0; i < points.size() - 1; i++) { - for (Map<float, Vector2>::Element *E = midpoints[i].front(); E; E = E->next()) { + for (const KeyValue<float, Vector2> &E : midpoints[i]) { pidx++; - bpw[pidx] = E->get(); + bpw[pidx] = E.value; } pidx++; @@ -1145,6 +1167,7 @@ void Curve3D::_bake() const { baked_point_cache.resize(0); baked_tilt_cache.resize(0); baked_up_vector_cache.resize(0); + baked_dist_cache.resize(0); return; } @@ -1153,6 +1176,8 @@ void Curve3D::_bake() const { baked_point_cache.set(0, points[0].pos); baked_tilt_cache.resize(1); baked_tilt_cache.set(0, points[0].tilt); + baked_dist_cache.resize(1); + baked_dist_cache.set(0, 0.0); if (up_vector_enabled) { baked_up_vector_cache.resize(1); @@ -1165,8 +1190,12 @@ void Curve3D::_bake() const { } Vector3 pos = points[0].pos; + float dist = 0.0; List<Plane> pointlist; + List<float> distlist; + pointlist.push_back(Plane(pos, points[0].tilt)); + distlist.push_back(0.0); for (int i = 0; i < points.size() - 1; i++) { float step = 0.1; // at least 10 substeps ought to be enough? @@ -1207,7 +1236,10 @@ void Curve3D::_bake() const { Plane post; post.normal = pos; post.d = Math::lerp(points[i].tilt, points[i + 1].tilt, mid); + dist += d; + pointlist.push_back(post); + distlist.push_back(dist); } else { p = np; } @@ -1218,8 +1250,10 @@ void Curve3D::_bake() const { float lastilt = points[points.size() - 1].tilt; float rem = pos.distance_to(lastpos); - baked_max_ofs = (pointlist.size() - 1) * bake_interval + rem; + dist += rem; + baked_max_ofs = dist; pointlist.push_back(Plane(lastpos, lastilt)); + distlist.push_back(dist); baked_point_cache.resize(pointlist.size()); Vector3 *w = baked_point_cache.ptrw(); @@ -1231,6 +1265,9 @@ void Curve3D::_bake() const { baked_up_vector_cache.resize(up_vector_enabled ? pointlist.size() : 0); Vector3 *up_write = baked_up_vector_cache.ptrw(); + baked_dist_cache.resize(pointlist.size()); + float *wd = baked_dist_cache.ptrw(); + Vector3 sideways; Vector3 up; Vector3 forward; @@ -1242,6 +1279,7 @@ void Curve3D::_bake() const { for (const Plane &E : pointlist) { w[idx] = E.normal; wt[idx] = E.d; + wd[idx] = distlist[idx]; if (!up_vector_enabled) { idx++; @@ -1308,19 +1346,26 @@ Vector3 Curve3D::interpolate_baked(float p_offset, bool p_cubic) const { return r[bpc - 1]; } - int idx = Math::floor((double)p_offset / (double)bake_interval); - float frac = Math::fmod(p_offset, bake_interval); - - if (idx >= bpc - 1) { - return r[bpc - 1]; - } else if (idx == bpc - 2) { - if (frac > 0) { - frac /= Math::fmod(baked_max_ofs, bake_interval); + int start = 0, end = bpc, idx = (end + start) / 2; + // binary search to find baked points + while (start < idx) { + float offset = baked_dist_cache[idx]; + if (p_offset <= offset) { + end = idx; + } else { + start = idx; } - } else { - frac /= bake_interval; + idx = (end + start) / 2; } + float offset_begin = baked_dist_cache[idx]; + float offset_end = baked_dist_cache[idx + 1]; + + float idx_interval = offset_end - offset_begin; + ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, Vector3(), "failed to find baked segment"); + + float frac = (p_offset - offset_begin) / idx_interval; + if (p_cubic) { Vector3 pre = idx > 0 ? r[idx - 1] : r[idx]; Vector3 post = (idx < (bpc - 2)) ? r[idx + 2] : r[idx + 1]; @@ -1607,9 +1652,9 @@ PackedVector3Array Curve3D::tessellate(int p_max_stages, float p_tolerance) cons int pidx = 0; for (int i = 0; i < points.size() - 1; i++) { - for (Map<float, Vector3>::Element *E = midpoints[i].front(); E; E = E->next()) { + for (const KeyValue<float, Vector3> &E : midpoints[i]) { pidx++; - bpw[pidx] = E->get(); + bpw[pidx] = E.value; } pidx++; diff --git a/scene/resources/curve.h b/scene/resources/curve.h index c25d307608..5808fd6508 100644 --- a/scene/resources/curve.h +++ b/scene/resources/curve.h @@ -161,6 +161,7 @@ class Curve2D : public Resource { mutable bool baked_cache_dirty = false; mutable PackedVector2Array baked_point_cache; + mutable PackedFloat32Array baked_dist_cache; mutable float baked_max_ofs = 0.0; void _bake() const; @@ -224,6 +225,7 @@ class Curve3D : public Resource { mutable PackedVector3Array baked_point_cache; mutable Vector<real_t> baked_tilt_cache; mutable PackedVector3Array baked_up_vector_cache; + mutable PackedFloat32Array baked_dist_cache; mutable float baked_max_ofs = 0.0; void _bake() const; diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index 5f70a31844..54bb7a82cf 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -51,7 +51,7 @@ static Ref<StyleBoxTexture> make_stylebox(T p_src, float p_left, float p_top, fl } else { texture = Ref<ImageTexture>(memnew(ImageTexture)); Ref<Image> img = memnew(Image(p_src)); - const Size2 orig_size = Size2(img->get_width(), img->get_height()); + const Size2 orig_size = img->get_size(); img->convert(Image::FORMAT_RGBA8); img->resize(orig_size.x * scale, orig_size.y * scale); @@ -97,7 +97,7 @@ template <class T> static Ref<Texture2D> make_icon(T p_src) { Ref<ImageTexture> texture(memnew(ImageTexture)); Ref<Image> img = memnew(Image(p_src)); - const Size2 orig_size = Size2(img->get_width(), img->get_height()); + const Size2 orig_size = img->get_size(); img->convert(Image::FORMAT_RGBA8); img->resize(orig_size.x * scale, orig_size.y * scale); texture->create_from_image(img); @@ -212,26 +212,6 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_constant("outline_size", "LinkButton", 0); theme->set_constant("underline_spacing", "LinkButton", 2 * scale); - // ColorPickerButton - - theme->set_stylebox("normal", "ColorPickerButton", sb_button_normal); - theme->set_stylebox("pressed", "ColorPickerButton", sb_button_pressed); - theme->set_stylebox("hover", "ColorPickerButton", sb_button_hover); - theme->set_stylebox("disabled", "ColorPickerButton", sb_button_disabled); - theme->set_stylebox("focus", "ColorPickerButton", sb_button_focus); - - theme->set_font("font", "ColorPickerButton", Ref<Font>()); - theme->set_font_size("font_size", "ColorPickerButton", -1); - - theme->set_color("font_color", "ColorPickerButton", Color(1, 1, 1, 1)); - theme->set_color("font_pressed_color", "ColorPickerButton", Color(0.8, 0.8, 0.8, 1)); - theme->set_color("font_hover_color", "ColorPickerButton", Color(1, 1, 1, 1)); - theme->set_color("font_disabled_color", "ColorPickerButton", Color(0.9, 0.9, 0.9, 0.3)); - theme->set_color("font_outline_color", "ColorPickerButton", Color(1, 1, 1)); - - theme->set_constant("hseparation", "ColorPickerButton", 2 * scale); - theme->set_constant("outline_size", "ColorPickerButton", 0); - // OptionButton Ref<StyleBox> sb_optbutton_focus = sb_expand(make_stylebox(button_focus_png, 4, 4, 4, 4, 6, 2, 6, 2), 2, 2, 2, 2); @@ -458,6 +438,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("caret_color", "TextEdit", control_font_color); theme->set_color("caret_background_color", "TextEdit", Color(0, 0, 0)); theme->set_color("word_highlighted_color", "TextEdit", Color(0.8, 0.9, 0.9, 0.15)); + theme->set_color("search_result_color", "TextEdit", Color(0.3, 0.3, 0.3)); + theme->set_color("search_result_border_color", "TextEdit", Color(0.3, 0.3, 0.3, 0.4)); theme->set_constant("line_spacing", "TextEdit", 4 * scale); theme->set_constant("outline_size", "TextEdit", 0); @@ -503,6 +485,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("line_number_color", "CodeEdit", Color(0.67, 0.67, 0.67, 0.4)); theme->set_color("word_highlighted_color", "CodeEdit", Color(0.8, 0.9, 0.9, 0.15)); theme->set_color("line_length_guideline_color", "CodeEdit", Color(0.3, 0.5, 0.8, 0.1)); + theme->set_color("search_result_color", "CodeEdit", Color(0.3, 0.3, 0.3)); + theme->set_color("search_result_border_color", "CodeEdit", Color(0.3, 0.3, 0.3, 0.4)); theme->set_constant("completion_lines", "CodeEdit", 7); theme->set_constant("completion_max_width", "CodeEdit", 50); @@ -522,8 +506,10 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_icon("increment", "HScrollBar", empty_icon); theme->set_icon("increment_highlight", "HScrollBar", empty_icon); + theme->set_icon("increment_pressed", "HScrollBar", empty_icon); theme->set_icon("decrement", "HScrollBar", empty_icon); theme->set_icon("decrement_highlight", "HScrollBar", empty_icon); + theme->set_icon("decrement_pressed", "HScrollBar", empty_icon); // VScrollBar @@ -535,8 +521,10 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_icon("increment", "VScrollBar", empty_icon); theme->set_icon("increment_highlight", "VScrollBar", empty_icon); + theme->set_icon("increment_pressed", "VScrollBar", empty_icon); theme->set_icon("decrement", "VScrollBar", empty_icon); theme->set_icon("decrement_highlight", "VScrollBar", empty_icon); + theme->set_icon("decrement_pressed", "VScrollBar", empty_icon); // HSlider @@ -802,8 +790,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_stylebox("tab_selected", "Tabs", sb_expand(make_stylebox(tab_current_png, 4, 3, 4, 1, 16, 3, 16, 2), 2, 2, 2, 2)); theme->set_stylebox("tab_unselected", "Tabs", sb_expand(make_stylebox(tab_behind_png, 5, 4, 5, 1, 16, 5, 16, 2), 3, 3, 3, 3)); theme->set_stylebox("tab_disabled", "Tabs", sb_expand(make_stylebox(tab_disabled_png, 5, 5, 5, 1, 16, 6, 16, 4), 3, 0, 3, 3)); - theme->set_stylebox("button_pressed", "Tabs", make_stylebox(button_pressed_png, 4, 4, 4, 4)); - theme->set_stylebox("button", "Tabs", make_stylebox(button_normal_png, 4, 4, 4, 4)); + theme->set_stylebox("close_bg_pressed", "Tabs", make_stylebox(button_pressed_png, 4, 4, 4, 4)); + theme->set_stylebox("close_bg_highlight", "Tabs", make_stylebox(button_normal_png, 4, 4, 4, 4)); theme->set_icon("increment", "Tabs", make_icon(scroll_button_right_png)); theme->set_icon("increment_highlight", "Tabs", make_icon(scroll_button_right_hl_png)); @@ -859,7 +847,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_icon("add_preset", "ColorPicker", make_icon(icon_add_png)); theme->set_icon("color_hue", "ColorPicker", make_icon(color_picker_hue_png)); theme->set_icon("color_sample", "ColorPicker", make_icon(color_picker_sample_png)); - theme->set_icon("preset_bg", "ColorPicker", make_icon(mini_checkerboard_png)); + theme->set_icon("sample_bg", "ColorPicker", make_icon(mini_checkerboard_png)); theme->set_icon("overbright_indicator", "ColorPicker", make_icon(overbright_indicator_png)); theme->set_icon("bar_arrow", "ColorPicker", make_icon(bar_arrow_png)); theme->set_icon("picker_cursor", "ColorPicker", make_icon(picker_cursor_png)); @@ -867,6 +855,34 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const // ColorPickerButton theme->set_icon("bg", "ColorPickerButton", make_icon(mini_checkerboard_png)); + theme->set_stylebox("normal", "ColorPickerButton", sb_button_normal); + theme->set_stylebox("pressed", "ColorPickerButton", sb_button_pressed); + theme->set_stylebox("hover", "ColorPickerButton", sb_button_hover); + theme->set_stylebox("disabled", "ColorPickerButton", sb_button_disabled); + theme->set_stylebox("focus", "ColorPickerButton", sb_button_focus); + + theme->set_font("font", "ColorPickerButton", Ref<Font>()); + theme->set_font_size("font_size", "ColorPickerButton", -1); + + theme->set_color("font_color", "ColorPickerButton", Color(1, 1, 1, 1)); + theme->set_color("font_pressed_color", "ColorPickerButton", Color(0.8, 0.8, 0.8, 1)); + theme->set_color("font_hover_color", "ColorPickerButton", Color(1, 1, 1, 1)); + theme->set_color("font_disabled_color", "ColorPickerButton", Color(0.9, 0.9, 0.9, 0.3)); + theme->set_color("font_outline_color", "ColorPickerButton", Color(1, 1, 1)); + + theme->set_constant("hseparation", "ColorPickerButton", 2 * scale); + theme->set_constant("outline_size", "ColorPickerButton", 0); + + // ColorPresetButton + + Ref<StyleBoxFlat> preset_sb = make_flat_stylebox(Color(1, 1, 1), 2, 2, 2, 2); + preset_sb->set_corner_radius_all(2); + preset_sb->set_corner_detail(2); + preset_sb->set_anti_aliased(false); + + theme->set_stylebox("preset_fg", "ColorPresetButton", preset_sb); + theme->set_icon("preset_bg", "ColorPresetButton", make_icon(mini_checkerboard_png)); + theme->set_icon("overbright_indicator", "ColorPresetButton", make_icon(overbright_indicator_png)); // TooltipPanel @@ -917,7 +933,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_constant("shadow_offset_y", "RichTextLabel", 1 * scale); theme->set_constant("shadow_as_outline", "RichTextLabel", 0 * scale); - theme->set_constant("line_separation", "RichTextLabel", 1 * scale); + theme->set_constant("line_separation", "RichTextLabel", 0 * scale); theme->set_constant("table_hseparation", "RichTextLabel", 3 * scale); theme->set_constant("table_vseparation", "RichTextLabel", 3 * scale); @@ -1012,16 +1028,24 @@ void make_default_theme(bool p_hidpi, Ref<Font> p_font) { Ref<FontData> dynamic_font_data; dynamic_font_data.instantiate(); - dynamic_font_data->load_memory(_font_OpenSans_SemiBold, _font_OpenSans_SemiBold_size, "ttf", default_font_size); + dynamic_font_data->set_data_ptr(_font_OpenSans_SemiBold, _font_OpenSans_SemiBold_size); dynamic_font->add_data(dynamic_font_data); + dynamic_font->set_base_size(default_font_size); default_font = dynamic_font; } Ref<Font> large_font = default_font; - fill_default_theme(t, default_font, large_font, default_icon, default_style, p_hidpi ? 2.0 : 1.0); + + float default_scale = 1.0; + if (p_hidpi) { + default_scale = 2.0; + } + + fill_default_theme(t, default_font, large_font, default_icon, default_style, default_scale); Theme::set_default(t); + Theme::set_default_base_scale(default_scale); Theme::set_default_icon(default_icon); Theme::set_default_style(default_style); Theme::set_default_font(default_font); diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp index cab6c0378a..9a3f081a8b 100644 --- a/scene/resources/environment.cpp +++ b/scene/resources/environment.cpp @@ -173,23 +173,13 @@ Environment::ReflectionSource Environment::get_reflection_source() const { return reflection_source; } -void Environment::set_ao_color(const Color &p_color) { - ao_color = p_color; - _update_ambient_light(); -} - -Color Environment::get_ao_color() const { - return ao_color; -} - void Environment::_update_ambient_light() { RS::get_singleton()->environment_set_ambient_light( environment, ambient_color, RS::EnvironmentAmbientSource(ambient_source), ambient_energy, - ambient_sky_contribution, RS::EnvironmentReflectionSource(reflection_source), - ao_color); + ambient_sky_contribution, RS::EnvironmentReflectionSource(reflection_source)); } // Tonemap @@ -302,7 +292,7 @@ int Environment::get_ssr_max_steps() const { } void Environment::set_ssr_fade_in(float p_fade_in) { - ssr_fade_in = p_fade_in; + ssr_fade_in = MAX(p_fade_in, 0.0f); _update_ssr(); } @@ -311,7 +301,7 @@ float Environment::get_ssr_fade_in() const { } void Environment::set_ssr_fade_out(float p_fade_out) { - ssr_fade_out = p_fade_out; + ssr_fade_out = MAX(p_fade_out, 0.0f); _update_ssr(); } @@ -1092,15 +1082,12 @@ void Environment::_bind_methods() { ClassDB::bind_method(D_METHOD("get_ambient_light_sky_contribution"), &Environment::get_ambient_light_sky_contribution); ClassDB::bind_method(D_METHOD("set_reflection_source", "source"), &Environment::set_reflection_source); ClassDB::bind_method(D_METHOD("get_reflection_source"), &Environment::get_reflection_source); - ClassDB::bind_method(D_METHOD("set_ao_color", "color"), &Environment::set_ao_color); - ClassDB::bind_method(D_METHOD("get_ao_color"), &Environment::get_ao_color); ADD_GROUP("Ambient Light", "ambient_light_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "ambient_light_source", PROPERTY_HINT_ENUM, "Background,Disabled,Color,Sky"), "set_ambient_source", "get_ambient_source"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ambient_light_color"), "set_ambient_light_color", "get_ambient_light_color"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ambient_light_sky_contribution", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_ambient_light_sky_contribution", "get_ambient_light_sky_contribution"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ambient_light_energy", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_ambient_light_energy", "get_ambient_light_energy"); - ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ambient_light_occlusion_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_ao_color", "get_ao_color"); ADD_GROUP("Reflected Light", "reflected_light_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "reflected_light_source", PROPERTY_HINT_ENUM, "Background,Disabled,Sky"), "set_reflection_source", "get_reflection_source"); diff --git a/scene/resources/environment.h b/scene/resources/environment.h index 0df2c3cc27..46842754f4 100644 --- a/scene/resources/environment.h +++ b/scene/resources/environment.h @@ -116,7 +116,6 @@ private: float ambient_energy = 1.0; float ambient_sky_contribution = 1.0; ReflectionSource reflection_source = REFLECTION_SOURCE_BG; - Color ao_color; void _update_ambient_light(); // Tonemap @@ -250,8 +249,6 @@ public: float get_ambient_light_sky_contribution() const; void set_reflection_source(ReflectionSource p_source); ReflectionSource get_reflection_source() const; - void set_ao_color(const Color &p_color); - Color get_ao_color() const; // Tonemap void set_tonemapper(ToneMapper p_tone_mapper); diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp index 032171847d..04e2b0dc70 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -36,54 +36,129 @@ #include "scene/resources/text_line.h" #include "scene/resources/text_paragraph.h" -void FontData::_bind_methods() { - ClassDB::bind_method(D_METHOD("load_resource", "filename", "base_size"), &FontData::load_resource, DEFVAL(16)); - ClassDB::bind_method(D_METHOD("load_memory", "data", "type", "base_size"), &FontData::_load_memory, DEFVAL(16)); - ClassDB::bind_method(D_METHOD("new_bitmap", "height", "ascent", "base_size"), &FontData::new_bitmap); - - ClassDB::bind_method(D_METHOD("bitmap_add_texture", "texture"), &FontData::bitmap_add_texture); - ClassDB::bind_method(D_METHOD("bitmap_add_char", "char", "texture_idx", "rect", "align", "advance"), &FontData::bitmap_add_char); - ClassDB::bind_method(D_METHOD("bitmap_add_kerning_pair", "A", "B", "kerning"), &FontData::bitmap_add_kerning_pair); +_FORCE_INLINE_ void FontData::_clear_cache() { + for (int i = 0; i < cache.size(); i++) { + if (cache[i].is_valid()) { + TS->free(cache[i]); + cache.write[i] = RID(); + } + } +} - ClassDB::bind_method(D_METHOD("set_data_path", "path"), &FontData::set_data_path); - ClassDB::bind_method(D_METHOD("get_data_path"), &FontData::get_data_path); +_FORCE_INLINE_ void FontData::_ensure_rid(int p_cache_index) const { + if (unlikely(p_cache_index >= cache.size())) { + cache.resize(p_cache_index + 1); + } + if (unlikely(!cache[p_cache_index].is_valid())) { + cache.write[p_cache_index] = TS->create_font(); + TS->font_set_data_ptr(cache[p_cache_index], data_ptr, data_size); + TS->font_set_antialiased(cache[p_cache_index], antialiased); + TS->font_set_multichannel_signed_distance_field(cache[p_cache_index], msdf); + TS->font_set_msdf_pixel_range(cache[p_cache_index], msdf_pixel_range); + TS->font_set_msdf_size(cache[p_cache_index], msdf_size); + TS->font_set_fixed_size(cache[p_cache_index], fixed_size); + TS->font_set_force_autohinter(cache[p_cache_index], force_autohinter); + TS->font_set_hinting(cache[p_cache_index], hinting); + TS->font_set_oversampling(cache[p_cache_index], oversampling); + } +} - ClassDB::bind_method(D_METHOD("get_height", "size"), &FontData::get_height); - ClassDB::bind_method(D_METHOD("get_ascent", "size"), &FontData::get_ascent); - ClassDB::bind_method(D_METHOD("get_descent", "size"), &FontData::get_descent); +void FontData::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_data", "data"), &FontData::set_data); + ClassDB::bind_method(D_METHOD("get_data"), &FontData::get_data); - ClassDB::bind_method(D_METHOD("get_underline_position", "size"), &FontData::get_underline_position); - ClassDB::bind_method(D_METHOD("get_underline_thickness", "size"), &FontData::get_underline_thickness); + ClassDB::bind_method(D_METHOD("set_antialiased", "antialiased"), &FontData::set_antialiased); + ClassDB::bind_method(D_METHOD("is_antialiased"), &FontData::is_antialiased); - ClassDB::bind_method(D_METHOD("get_spacing", "type"), &FontData::get_spacing); - ClassDB::bind_method(D_METHOD("set_spacing", "type", "value"), &FontData::set_spacing); + ClassDB::bind_method(D_METHOD("set_multichannel_signed_distance_field", "msdf"), &FontData::set_multichannel_signed_distance_field); + ClassDB::bind_method(D_METHOD("is_multichannel_signed_distance_field"), &FontData::is_multichannel_signed_distance_field); - ClassDB::bind_method(D_METHOD("set_antialiased", "antialiased"), &FontData::set_antialiased); - ClassDB::bind_method(D_METHOD("get_antialiased"), &FontData::get_antialiased); + ClassDB::bind_method(D_METHOD("set_msdf_pixel_range", "msdf_pixel_range"), &FontData::set_msdf_pixel_range); + ClassDB::bind_method(D_METHOD("get_msdf_pixel_range"), &FontData::get_msdf_pixel_range); - ClassDB::bind_method(D_METHOD("get_variation_list"), &FontData::get_variation_list); + ClassDB::bind_method(D_METHOD("set_msdf_size", "msdf_size"), &FontData::set_msdf_size); + ClassDB::bind_method(D_METHOD("get_msdf_size"), &FontData::get_msdf_size); - ClassDB::bind_method(D_METHOD("set_variation", "tag", "value"), &FontData::set_variation); - ClassDB::bind_method(D_METHOD("get_variation", "tag"), &FontData::get_variation); + ClassDB::bind_method(D_METHOD("set_force_autohinter", "force_autohinter"), &FontData::set_force_autohinter); + ClassDB::bind_method(D_METHOD("is_force_autohinter"), &FontData::is_force_autohinter); ClassDB::bind_method(D_METHOD("set_hinting", "hinting"), &FontData::set_hinting); ClassDB::bind_method(D_METHOD("get_hinting"), &FontData::get_hinting); - ClassDB::bind_method(D_METHOD("set_force_autohinter", "enabled"), &FontData::set_force_autohinter); - ClassDB::bind_method(D_METHOD("get_force_autohinter"), &FontData::get_force_autohinter); + ClassDB::bind_method(D_METHOD("set_oversampling", "oversampling"), &FontData::set_oversampling); + ClassDB::bind_method(D_METHOD("get_oversampling"), &FontData::get_oversampling); - ClassDB::bind_method(D_METHOD("set_distance_field_hint", "distance_field"), &FontData::set_distance_field_hint); - ClassDB::bind_method(D_METHOD("get_distance_field_hint"), &FontData::get_distance_field_hint); + ClassDB::bind_method(D_METHOD("find_cache", "variation_coordinates"), &FontData::find_cache); - ClassDB::bind_method(D_METHOD("has_char", "char"), &FontData::has_char); - ClassDB::bind_method(D_METHOD("get_supported_chars"), &FontData::get_supported_chars); + ClassDB::bind_method(D_METHOD("get_cache_count"), &FontData::get_cache_count); + ClassDB::bind_method(D_METHOD("clear_cache"), &FontData::clear_cache); + ClassDB::bind_method(D_METHOD("remove_cache", "cache_index"), &FontData::remove_cache); + + ClassDB::bind_method(D_METHOD("get_size_cache_list", "cache_index"), &FontData::get_size_cache_list); + ClassDB::bind_method(D_METHOD("clear_size_cache", "cache_index"), &FontData::clear_size_cache); + ClassDB::bind_method(D_METHOD("remove_size_cache", "cache_index", "size"), &FontData::remove_size_cache); + + ClassDB::bind_method(D_METHOD("set_variation_coordinates", "cache_index", "variation_coordinates"), &FontData::set_variation_coordinates); + ClassDB::bind_method(D_METHOD("get_variation_coordinates", "cache_index"), &FontData::get_variation_coordinates); + + ClassDB::bind_method(D_METHOD("set_ascent", "cache_index", "size", "ascent"), &FontData::set_ascent); + ClassDB::bind_method(D_METHOD("get_ascent", "cache_index", "size"), &FontData::get_ascent); + + ClassDB::bind_method(D_METHOD("set_descent", "cache_index", "size", "descent"), &FontData::set_descent); + ClassDB::bind_method(D_METHOD("get_descent", "cache_index", "size"), &FontData::get_descent); + + ClassDB::bind_method(D_METHOD("set_underline_position", "cache_index", "size", "underline_position"), &FontData::set_underline_position); + ClassDB::bind_method(D_METHOD("get_underline_position", "cache_index", "size"), &FontData::get_underline_position); + + ClassDB::bind_method(D_METHOD("set_underline_thickness", "cache_index", "size", "underline_thickness"), &FontData::set_underline_thickness); + ClassDB::bind_method(D_METHOD("get_underline_thickness", "cache_index", "size"), &FontData::get_underline_thickness); + + ClassDB::bind_method(D_METHOD("set_scale", "cache_index", "size", "scale"), &FontData::set_scale); + ClassDB::bind_method(D_METHOD("get_scale", "cache_index", "size"), &FontData::get_scale); + + ClassDB::bind_method(D_METHOD("set_spacing", "cache_index", "size", "spacing_type", "value"), &FontData::set_spacing); + ClassDB::bind_method(D_METHOD("get_spacing", "cache_index", "size", "spacing_type"), &FontData::get_spacing); + + ClassDB::bind_method(D_METHOD("get_texture_count", "cache_index", "size"), &FontData::get_texture_count); + ClassDB::bind_method(D_METHOD("clear_textures", "cache_index", "size"), &FontData::clear_textures); + ClassDB::bind_method(D_METHOD("remove_texture", "cache_index", "size", "texture_index"), &FontData::remove_texture); + + ClassDB::bind_method(D_METHOD("set_texture_image", "cache_index", "size", "texture_index", "image"), &FontData::set_texture_image); + ClassDB::bind_method(D_METHOD("get_texture_image", "cache_index", "size", "texture_index"), &FontData::get_texture_image); + + ClassDB::bind_method(D_METHOD("set_texture_offsets", "cache_index", "size", "texture_index", "offset"), &FontData::set_texture_offsets); + ClassDB::bind_method(D_METHOD("get_texture_offsets", "cache_index", "size", "texture_index"), &FontData::get_texture_offsets); + + ClassDB::bind_method(D_METHOD("get_glyph_list", "cache_index", "size"), &FontData::get_glyph_list); + ClassDB::bind_method(D_METHOD("clear_glyphs", "cache_index", "size"), &FontData::clear_glyphs); + ClassDB::bind_method(D_METHOD("remove_glyph", "cache_index", "size", "glyph"), &FontData::remove_glyph); + + ClassDB::bind_method(D_METHOD("set_glyph_advance", "cache_index", "size", "glyph", "advance"), &FontData::set_glyph_advance); + ClassDB::bind_method(D_METHOD("get_glyph_advance", "cache_index", "size", "glyph"), &FontData::get_glyph_advance); + + ClassDB::bind_method(D_METHOD("set_glyph_offset", "cache_index", "size", "glyph", "offset"), &FontData::set_glyph_offset); + ClassDB::bind_method(D_METHOD("get_glyph_offset", "cache_index", "size", "glyph"), &FontData::get_glyph_offset); + + ClassDB::bind_method(D_METHOD("set_glyph_size", "cache_index", "size", "glyph", "gl_size"), &FontData::set_glyph_size); + ClassDB::bind_method(D_METHOD("get_glyph_size", "cache_index", "size", "glyph"), &FontData::get_glyph_size); + + ClassDB::bind_method(D_METHOD("set_glyph_uv_rect", "cache_index", "size", "glyph", "uv_rect"), &FontData::set_glyph_uv_rect); + ClassDB::bind_method(D_METHOD("get_glyph_uv_rect", "cache_index", "size", "glyph"), &FontData::get_glyph_uv_rect); - ClassDB::bind_method(D_METHOD("get_glyph_advance", "index", "size"), &FontData::get_glyph_advance); - ClassDB::bind_method(D_METHOD("get_glyph_kerning", "index_a", "index_b", "size"), &FontData::get_glyph_kerning); + ClassDB::bind_method(D_METHOD("set_glyph_texture_idx", "cache_index", "size", "glyph", "texture_idx"), &FontData::set_glyph_texture_idx); + ClassDB::bind_method(D_METHOD("get_glyph_texture_idx", "cache_index", "size", "glyph"), &FontData::get_glyph_texture_idx); - ClassDB::bind_method(D_METHOD("get_base_size"), &FontData::get_base_size); + ClassDB::bind_method(D_METHOD("get_kerning_list", "cache_index", "size"), &FontData::get_kerning_list); + ClassDB::bind_method(D_METHOD("clear_kerning_map", "cache_index", "size"), &FontData::clear_kerning_map); + ClassDB::bind_method(D_METHOD("remove_kerning", "cache_index", "size", "glyph_pair"), &FontData::remove_kerning); - ClassDB::bind_method(D_METHOD("has_outline"), &FontData::has_outline); + ClassDB::bind_method(D_METHOD("set_kerning", "cache_index", "size", "glyph_pair", "kerning"), &FontData::set_kerning); + ClassDB::bind_method(D_METHOD("get_kerning", "cache_index", "size", "glyph_pair"), &FontData::get_kerning); + + ClassDB::bind_method(D_METHOD("render_range", "cache_index", "size", "start", "end"), &FontData::render_range); + ClassDB::bind_method(D_METHOD("render_glyph", "cache_index", "size", "index"), &FontData::render_glyph); + + ClassDB::bind_method(D_METHOD("get_cache_rid", "cache_index"), &FontData::get_cache_rid); ClassDB::bind_method(D_METHOD("is_language_supported", "language"), &FontData::is_language_supported); ClassDB::bind_method(D_METHOD("set_language_support_override", "language", "supported"), &FontData::set_language_support_override); @@ -97,511 +172,963 @@ void FontData::_bind_methods() { ClassDB::bind_method(D_METHOD("remove_script_support_override", "script"), &FontData::remove_script_support_override); ClassDB::bind_method(D_METHOD("get_script_support_overrides"), &FontData::get_script_support_overrides); - ClassDB::bind_method(D_METHOD("get_glyph_index", "char", "variation_selector"), &FontData::get_glyph_index, DEFVAL(0x0000)); - ClassDB::bind_method(D_METHOD("draw_glyph", "canvas", "size", "pos", "index", "color"), &FontData::draw_glyph, DEFVAL(Color(1, 1, 1))); - ClassDB::bind_method(D_METHOD("draw_glyph_outline", "canvas", "size", "outline_size", "pos", "index", "color"), &FontData::draw_glyph_outline, DEFVAL(Color(1, 1, 1))); - - ADD_PROPERTY(PropertyInfo(Variant::STRING, "data_path", PROPERTY_HINT_FILE, "*.ttf,*.otf,*.woff,*.fnt,*.font"), "set_data_path", "get_data_path"); - - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "antialiased"), "set_antialiased", "get_antialiased"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "force_autohinter"), "set_force_autohinter", "get_force_autohinter"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "distance_field_hint"), "set_distance_field_hint", "get_distance_field_hint"); - - ADD_PROPERTY(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal"), "set_hinting", "get_hinting"); + ClassDB::bind_method(D_METHOD("has_char", "char"), &FontData::has_char); + ClassDB::bind_method(D_METHOD("get_supported_chars"), &FontData::get_supported_chars); - ADD_GROUP("Extra Spacing", "extra_spacing"); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "extra_spacing_glyph"), "set_spacing", "get_spacing", SPACING_GLYPH); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "extra_spacing_space"), "set_spacing", "get_spacing", SPACING_SPACE); + ClassDB::bind_method(D_METHOD("get_glyph_index", "size", "char", "variation_selector"), &FontData::get_glyph_index); - BIND_ENUM_CONSTANT(SPACING_GLYPH); - BIND_ENUM_CONSTANT(SPACING_SPACE); + ClassDB::bind_method(D_METHOD("get_supported_feature_list"), &FontData::get_supported_feature_list); + ClassDB::bind_method(D_METHOD("get_supported_variation_list"), &FontData::get_supported_variation_list); } bool FontData::_set(const StringName &p_name, const Variant &p_value) { - String str = p_name; - if (str.begins_with("language_support_override/")) { - String lang = str.get_slicec('/', 1); - if (lang == "_new") { - return false; + Vector<String> tokens = p_name.operator String().split("/"); + if (tokens.size() == 1) { + if (tokens[0] == "data") { + set_data(p_value); + return true; + } else if (tokens[0] == "antialiased") { + set_antialiased(p_value); + return true; + } else if (tokens[0] == "multichannel_signed_distance_field") { + set_multichannel_signed_distance_field(p_value); + return true; + } else if (tokens[0] == "msdf_pixel_range") { + set_msdf_pixel_range(p_value); + return true; + } else if (tokens[0] == "msdf_size") { + set_msdf_size(p_value); + return true; + } else if (tokens[0] == "fixed_size") { + set_fixed_size(p_value); + return true; + } else if (tokens[0] == "hinting") { + set_hinting((TextServer::Hinting)p_value.operator int()); + return true; + } else if (tokens[0] == "force_autohinter") { + set_force_autohinter(p_value); + return true; + } else if (tokens[0] == "oversampling") { + set_oversampling(p_value); + return true; } + } else if (tokens.size() == 2 && tokens[0] == "language_support_override") { + String lang = tokens[1]; set_language_support_override(lang, p_value); return true; - } - if (str.begins_with("script_support_override/")) { - String scr = str.get_slicec('/', 1); - if (scr == "_new") { - return false; - } - set_script_support_override(scr, p_value); - return true; - } - if (str.begins_with("variation/")) { - String name = str.get_slicec('/', 1); - set_variation(name, p_value); + } else if (tokens.size() == 2 && tokens[0] == "script_support_override") { + String script = tokens[1]; + set_script_support_override(script, p_value); return true; + } else if (tokens.size() >= 3 && tokens[0] == "cache") { + int cache_index = tokens[1].to_int(); + if (tokens.size() == 3 && tokens[2] == "variation_coordinates") { + set_variation_coordinates(cache_index, p_value); + return true; + } + if (tokens.size() >= 5) { + Vector2i sz = Vector2i(tokens[2].to_int(), tokens[3].to_int()); + if (tokens[4] == "ascent") { + set_ascent(cache_index, sz.x, p_value); + return true; + } else if (tokens[4] == "descent") { + set_descent(cache_index, sz.x, p_value); + return true; + } else if (tokens[4] == "underline_position") { + set_underline_position(cache_index, sz.x, p_value); + return true; + } else if (tokens[4] == "underline_thickness") { + set_underline_thickness(cache_index, sz.x, p_value); + return true; + } else if (tokens[4] == "scale") { + set_scale(cache_index, sz.x, p_value); + return true; + } else if (tokens[4] == "spacing_glyph") { + set_spacing(cache_index, sz.x, TextServer::SPACING_GLYPH, p_value); + return true; + } else if (tokens[4] == "spacing_space") { + set_spacing(cache_index, sz.x, TextServer::SPACING_SPACE, p_value); + return true; + } else if (tokens.size() == 7 && tokens[4] == "textures") { + int texture_index = tokens[5].to_int(); + if (tokens[6] == "image") { + set_texture_image(cache_index, sz, texture_index, p_value); + return true; + } else if (tokens[6] == "offsets") { + set_texture_offsets(cache_index, sz, texture_index, p_value); + return true; + } + } else if (tokens.size() == 7 && tokens[4] == "glyphs") { + int32_t glyph_index = tokens[5].to_int(); + if (tokens[6] == "advance") { + set_glyph_advance(cache_index, sz.x, glyph_index, p_value); + return true; + } else if (tokens[6] == "offset") { + set_glyph_offset(cache_index, sz, glyph_index, p_value); + return true; + } else if (tokens[6] == "size") { + set_glyph_size(cache_index, sz, glyph_index, p_value); + return true; + } else if (tokens[6] == "uv_rect") { + set_glyph_uv_rect(cache_index, sz, glyph_index, p_value); + return true; + } else if (tokens[6] == "texture_idx") { + set_glyph_texture_idx(cache_index, sz, glyph_index, p_value); + return true; + } + } else if (tokens.size() == 7 && tokens[4] == "kerning_overrides") { + Vector2i gp = Vector2i(tokens[5].to_int(), tokens[6].to_int()); + set_kerning(cache_index, sz.x, gp, p_value); + return true; + } + } } - return false; } bool FontData::_get(const StringName &p_name, Variant &r_ret) const { - String str = p_name; - if (str.begins_with("language_support_override/")) { - String lang = str.get_slicec('/', 1); - if (lang == "_new") { + Vector<String> tokens = p_name.operator String().split("/"); + if (tokens.size() == 1) { + if (tokens[0] == "data") { + r_ret = get_data(); + return true; + } else if (tokens[0] == "antialiased") { + r_ret = is_antialiased(); + return true; + } else if (tokens[0] == "multichannel_signed_distance_field") { + r_ret = is_multichannel_signed_distance_field(); + return true; + } else if (tokens[0] == "msdf_pixel_range") { + r_ret = get_msdf_pixel_range(); + return true; + } else if (tokens[0] == "msdf_size") { + r_ret = get_msdf_size(); + return true; + } else if (tokens[0] == "fixed_size") { + r_ret = get_fixed_size(); + return true; + } else if (tokens[0] == "hinting") { + r_ret = get_hinting(); + return true; + } else if (tokens[0] == "force_autohinter") { + r_ret = is_force_autohinter(); + return true; + } else if (tokens[0] == "oversampling") { + r_ret = get_oversampling(); return true; } + } else if (tokens.size() == 2 && tokens[0] == "language_support_override") { + String lang = tokens[1]; r_ret = get_language_support_override(lang); return true; - } - if (str.begins_with("script_support_override/")) { - String scr = str.get_slicec('/', 1); - if (scr == "_new") { + } else if (tokens.size() == 2 && tokens[0] == "script_support_override") { + String script = tokens[1]; + r_ret = get_script_support_override(script); + return true; + } else if (tokens.size() >= 3 && tokens[0] == "cache") { + int cache_index = tokens[1].to_int(); + if (tokens.size() == 3 && tokens[2] == "variation_coordinates") { + r_ret = get_variation_coordinates(cache_index); return true; } - r_ret = get_script_support_override(scr); - return true; - } - if (str.begins_with("variation/")) { - String name = str.get_slicec('/', 1); - - r_ret = get_variation(name); - return true; + if (tokens.size() >= 5) { + Vector2i sz = Vector2i(tokens[2].to_int(), tokens[3].to_int()); + if (tokens[4] == "ascent") { + r_ret = get_ascent(cache_index, sz.x); + return true; + } else if (tokens[4] == "descent") { + r_ret = get_descent(cache_index, sz.x); + return true; + } else if (tokens[4] == "underline_position") { + r_ret = get_underline_position(cache_index, sz.x); + return true; + } else if (tokens[4] == "underline_thickness") { + r_ret = get_underline_thickness(cache_index, sz.x); + return true; + } else if (tokens[4] == "scale") { + r_ret = get_scale(cache_index, sz.x); + return true; + } else if (tokens[4] == "spacing_glyph") { + r_ret = get_spacing(cache_index, sz.x, TextServer::SPACING_GLYPH); + return true; + } else if (tokens[4] == "spacing_space") { + r_ret = get_spacing(cache_index, sz.x, TextServer::SPACING_SPACE); + return true; + } else if (tokens.size() == 7 && tokens[4] == "textures") { + int texture_index = tokens[5].to_int(); + if (tokens[6] == "image") { + r_ret = get_texture_image(cache_index, sz, texture_index); + return true; + } else if (tokens[6] == "offsets") { + r_ret = get_texture_offsets(cache_index, sz, texture_index); + return true; + } + } else if (tokens.size() == 7 && tokens[4] == "glyphs") { + int32_t glyph_index = tokens[5].to_int(); + if (tokens[6] == "advance") { + r_ret = get_glyph_advance(cache_index, sz.x, glyph_index); + return true; + } else if (tokens[6] == "offset") { + r_ret = get_glyph_offset(cache_index, sz, glyph_index); + return true; + } else if (tokens[6] == "size") { + r_ret = get_glyph_size(cache_index, sz, glyph_index); + return true; + } else if (tokens[6] == "uv_rect") { + r_ret = get_glyph_uv_rect(cache_index, sz, glyph_index); + return true; + } else if (tokens[6] == "texture_idx") { + r_ret = get_glyph_texture_idx(cache_index, sz, glyph_index); + return true; + } + } else if (tokens.size() == 7 && tokens[4] == "kerning_overrides") { + Vector2i gp = Vector2i(tokens[5].to_int(), tokens[6].to_int()); + r_ret = get_kerning(cache_index, sz.x, gp); + return true; + } + } } - return false; } void FontData::_get_property_list(List<PropertyInfo> *p_list) const { + p_list->push_back(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + + p_list->push_back(PropertyInfo(Variant::BOOL, "antialiased", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::BOOL, "multichannel_signed_distance_field", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::INT, "msdf_pixel_range", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::INT, "msdf_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::INT, "fixed_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::BOOL, "force_autohinter", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + Vector<String> lang_over = get_language_support_overrides(); for (int i = 0; i < lang_over.size(); i++) { - p_list->push_back(PropertyInfo(Variant::BOOL, "language_support_override/" + lang_over[i], PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::BOOL, "language_support_override/" + lang_over[i], PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); } - p_list->push_back(PropertyInfo(Variant::NIL, "language_support_override/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); - Vector<String> scr_over = get_script_support_overrides(); for (int i = 0; i < scr_over.size(); i++) { - p_list->push_back(PropertyInfo(Variant::BOOL, "script_support_override/" + scr_over[i], PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE)); - } - p_list->push_back(PropertyInfo(Variant::NIL, "script_support_override/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); + p_list->push_back(PropertyInfo(Variant::BOOL, "script_support_override/" + scr_over[i], PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + } + for (int i = 0; i < cache.size(); i++) { + String prefix = "cache/" + itos(i) + "/"; + Array sizes = get_size_cache_list(i); + p_list->push_back(PropertyInfo(Variant::DICTIONARY, prefix + "variation_coordinates", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + for (int j = 0; j < sizes.size(); j++) { + Vector2i sz = sizes[j]; + String prefix_sz = prefix + itos(sz.x) + "/" + itos(sz.y) + "/"; + if (sz.y == 0) { + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "ascent", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "descent", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "underline_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "underline_thickness", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::BOOL, prefix_sz + "spacing_glyph", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::BOOL, prefix_sz + "spacing_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + } - Dictionary variations = get_variation_list(); - for (const Variant *ftr = variations.next(nullptr); ftr != nullptr; ftr = variations.next(ftr)) { - Vector3i v = variations[*ftr]; - p_list->push_back(PropertyInfo(Variant::FLOAT, "variation/" + TS->tag_to_name(*ftr), PROPERTY_HINT_RANGE, itos(v.x) + "," + itos(v.y), PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE)); + int tx_cnt = get_texture_count(i, sz); + for (int k = 0; k < tx_cnt; k++) { + p_list->push_back(PropertyInfo(Variant::PACKED_INT32_ARRAY, prefix_sz + "textures/" + itos(k) + "/offsets", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::OBJECT, prefix_sz + "textures/" + itos(k) + "/image", PROPERTY_HINT_RESOURCE_TYPE, "Image", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT)); + } + Array glyphs = get_glyph_list(i, sz); + for (int k = 0; k < glyphs.size(); k++) { + const int32_t &gl = glyphs[k]; + if (sz.y == 0) { + p_list->push_back(PropertyInfo(Variant::VECTOR2, prefix_sz + "glyphs/" + itos(gl) + "/advance", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + } + p_list->push_back(PropertyInfo(Variant::VECTOR2, prefix_sz + "glyphs/" + itos(gl) + "/offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::VECTOR2, prefix_sz + "glyphs/" + itos(gl) + "/size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::RECT2, prefix_sz + "glyphs/" + itos(gl) + "/uv_rect", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::INT, prefix_sz + "glyphs/" + itos(gl) + "/texture_idx", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + } + if (sz.y == 0) { + Array kerning_map = get_kerning_list(i, sz.x); + for (int k = 0; k < kerning_map.size(); k++) { + const Vector2i &gl_pair = kerning_map[k]; + p_list->push_back(PropertyInfo(Variant::VECTOR2, prefix_sz + "kerning_overrides/" + itos(gl_pair.x) + "/" + itos(gl_pair.y), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + } + } + } } } void FontData::reset_state() { - if (rid != RID()) { - TS->free(rid); - } - base_size = 16; - path = String(); -} + _clear_cache(); + data.clear(); + data_ptr = nullptr; + data_size = 0; + cache.clear(); -RID FontData::get_rid() const { - return rid; + antialiased = true; + msdf = false; + force_autohinter = false; + hinting = TextServer::HINTING_LIGHT; + msdf_pixel_range = 14; + msdf_size = 128; + oversampling = 0.f; } -void FontData::load_resource(const String &p_filename, int p_base_size) { - if (rid != RID()) { - TS->free(rid); +/*************************************************************************/ + +void FontData::set_data_ptr(const uint8_t *p_data, size_t p_size) { + data.clear(); + data_ptr = p_data; + data_size = p_size; + + if (data_ptr != nullptr) { + for (int i = 0; i < cache.size(); i++) { + if (cache[i].is_valid()) { + TS->font_set_data_ptr(cache[i], data_ptr, data_size); + } + } } - rid = TS->create_font_resource(p_filename, p_base_size); - path = p_filename; - base_size = TS->font_get_base_size(rid); - emit_changed(); } -void FontData::_load_memory(const PackedByteArray &p_data, const String &p_type, int p_base_size) { - if (rid != RID()) { - TS->free(rid); +void FontData::set_data(const PackedByteArray &p_data) { + data = p_data; + data_ptr = data.ptr(); + data_size = data.size(); + + if (data_ptr != nullptr) { + for (int i = 0; i < cache.size(); i++) { + if (cache[i].is_valid()) { + TS->font_set_data_ptr(cache[i], data_ptr, data_size); + } + } } - rid = TS->create_font_memory(p_data.ptr(), p_data.size(), p_type, p_base_size); - path = TTR("(Memory: " + p_type.to_upper() + " @ 0x" + String::num_int64((uint64_t)p_data.ptr(), 16, true) + ")"); - base_size = TS->font_get_base_size(rid); - emit_changed(); } -void FontData::load_memory(const uint8_t *p_data, size_t p_size, const String &p_type, int p_base_size) { - if (rid != RID()) { - TS->free(rid); +PackedByteArray FontData::get_data() const { + if (unlikely((size_t)data.size() != data_size)) { + PackedByteArray *data_w = const_cast<PackedByteArray *>(&data); + data_w->resize(data_size); + memcpy(data_w->ptrw(), data_ptr, data_size); } - rid = TS->create_font_memory(p_data, p_size, p_type, p_base_size); - path = TTR("(Memory: " + p_type.to_upper() + " @ 0x" + String::num_int64((uint64_t)p_data, 16, true) + ")"); - base_size = TS->font_get_base_size(rid); - emit_changed(); + return data; } -void FontData::new_bitmap(float p_height, float p_ascent, int p_base_size) { - if (rid != RID()) { - TS->free(rid); +void FontData::set_antialiased(bool p_antialiased) { + if (antialiased != p_antialiased) { + antialiased = p_antialiased; + for (int i = 0; i < cache.size(); i++) { + _ensure_rid(i); + TS->font_set_antialiased(cache[i], antialiased); + } + emit_changed(); } - rid = TS->create_font_bitmap(p_height, p_ascent, p_base_size); - path = TTR("(Bitmap: " + String::num_int64(rid.get_id(), 16, true) + ")"); - base_size = TS->font_get_base_size(rid); - emit_changed(); } -void FontData::bitmap_add_texture(const Ref<Texture> &p_texture) { - if (rid != RID()) { - TS->font_bitmap_add_texture(rid, p_texture); - } +bool FontData::is_antialiased() const { + return antialiased; } -void FontData::bitmap_add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) { - if (rid != RID()) { - TS->font_bitmap_add_char(rid, p_char, p_texture_idx, p_rect, p_align, p_advance); +void FontData::set_multichannel_signed_distance_field(bool p_msdf) { + if (msdf != p_msdf) { + msdf = p_msdf; + for (int i = 0; i < cache.size(); i++) { + _ensure_rid(i); + TS->font_set_multichannel_signed_distance_field(cache[i], msdf); + } + emit_changed(); } } -void FontData::bitmap_add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning) { - if (rid != RID()) { - TS->font_bitmap_add_kerning_pair(rid, p_A, p_B, p_kerning); - } +bool FontData::is_multichannel_signed_distance_field() const { + return msdf; } -void FontData::set_data_path(const String &p_path) { - load_resource(p_path, base_size); +void FontData::set_msdf_pixel_range(int p_msdf_pixel_range) { + if (msdf_pixel_range != p_msdf_pixel_range) { + msdf_pixel_range = p_msdf_pixel_range; + for (int i = 0; i < cache.size(); i++) { + _ensure_rid(i); + TS->font_set_msdf_pixel_range(cache[i], msdf_pixel_range); + } + emit_changed(); + } } -String FontData::get_data_path() const { - return path; +int FontData::get_msdf_pixel_range() const { + return msdf_pixel_range; } -float FontData::get_height(int p_size) const { - if (rid == RID()) { - return 0.f; // Do not raise errors in getters, to prevent editor from spamming errors on incomplete (without data_path set) fonts. +void FontData::set_msdf_size(int p_msdf_size) { + if (msdf_size != p_msdf_size) { + msdf_size = p_msdf_size; + for (int i = 0; i < cache.size(); i++) { + _ensure_rid(i); + TS->font_set_msdf_size(cache[i], msdf_size); + } + emit_changed(); } - return TS->font_get_height(rid, (p_size < 0) ? base_size : p_size); } -float FontData::get_ascent(int p_size) const { - if (rid == RID()) { - return 0.f; - } - return TS->font_get_ascent(rid, (p_size < 0) ? base_size : p_size); +int FontData::get_msdf_size() const { + return msdf_size; } -float FontData::get_descent(int p_size) const { - if (rid == RID()) { - return 0.f; +void FontData::set_fixed_size(int p_fixed_size) { + if (fixed_size != p_fixed_size) { + fixed_size = p_fixed_size; + for (int i = 0; i < cache.size(); i++) { + _ensure_rid(i); + TS->font_set_fixed_size(cache[i], fixed_size); + } + emit_changed(); } - return TS->font_get_descent(rid, (p_size < 0) ? base_size : p_size); } -float FontData::get_underline_position(int p_size) const { - if (rid == RID()) { - return 0.f; - } - return TS->font_get_underline_position(rid, (p_size < 0) ? base_size : p_size); +int FontData::get_fixed_size() const { + return fixed_size; } -Dictionary FontData::get_feature_list() const { - if (rid == RID()) { - return Dictionary(); +void FontData::set_force_autohinter(bool p_force_autohinter) { + if (force_autohinter != p_force_autohinter) { + force_autohinter = p_force_autohinter; + for (int i = 0; i < cache.size(); i++) { + _ensure_rid(i); + TS->font_set_force_autohinter(cache[i], force_autohinter); + } + emit_changed(); } - return TS->font_get_feature_list(rid); } -float FontData::get_underline_thickness(int p_size) const { - if (rid == RID()) { - return 0.f; - } - return TS->font_get_underline_thickness(rid, (p_size < 0) ? base_size : p_size); +bool FontData::is_force_autohinter() const { + return force_autohinter; } -Dictionary FontData::get_variation_list() const { - if (rid == RID()) { - return Dictionary(); +void FontData::set_hinting(TextServer::Hinting p_hinting) { + if (hinting != p_hinting) { + hinting = p_hinting; + for (int i = 0; i < cache.size(); i++) { + _ensure_rid(i); + TS->font_set_hinting(cache[i], hinting); + } + emit_changed(); } - return TS->font_get_variation_list(rid); } -void FontData::set_variation(const String &p_name, double p_value) { - ERR_FAIL_COND(rid == RID()); - TS->font_set_variation(rid, p_name, p_value); - emit_changed(); +TextServer::Hinting FontData::get_hinting() const { + return hinting; } -double FontData::get_variation(const String &p_name) const { - if (rid == RID()) { - return 0; +void FontData::set_oversampling(real_t p_oversampling) { + if (oversampling != p_oversampling) { + oversampling = p_oversampling; + for (int i = 0; i < cache.size(); i++) { + _ensure_rid(i); + TS->font_set_oversampling(cache[i], oversampling); + } + emit_changed(); + } +} + +real_t FontData::get_oversampling() const { + return oversampling; +} + +RID FontData::find_cache(const Dictionary &p_variation_coordinates) const { + // Find existing variation cache. + const Dictionary &supported_coords = get_supported_variation_list(); + for (int i = 0; i < cache.size(); i++) { + if (cache[i].is_valid()) { + const Dictionary &cache_var = TS->font_get_variation_coordinates(cache[i]); + bool match = true; + for (const Variant *V = supported_coords.next(nullptr); V && match; V = supported_coords.next(V)) { + const Vector3 &def = supported_coords[*V]; + + real_t c_v = def.z; + if (cache_var.has(*V)) { + real_t val = cache_var[*V]; + c_v = CLAMP(val, def.x, def.y); + } + if (cache_var.has(TS->tag_to_name(*V))) { + real_t val = cache_var[TS->tag_to_name(*V)]; + c_v = CLAMP(val, def.x, def.y); + } + + real_t s_v = def.z; + if (p_variation_coordinates.has(*V)) { + real_t val = p_variation_coordinates[*V]; + s_v = CLAMP(val, def.x, def.y); + } + if (p_variation_coordinates.has(TS->tag_to_name(*V))) { + real_t val = p_variation_coordinates[TS->tag_to_name(*V)]; + s_v = CLAMP(val, def.x, def.y); + } + + match = match && (c_v == s_v); + } + if (match) { + return cache[i]; + } + } } - return TS->font_get_variation(rid, p_name); + + // Create new variation cache. + int idx = cache.size(); + _ensure_rid(idx); + TS->font_set_variation_coordinates(cache[idx], p_variation_coordinates); + return cache[idx]; } -int FontData::get_spacing(int p_type) const { - if (rid == RID()) { - return 0; - } - if (p_type == SPACING_GLYPH) { - return TS->font_get_spacing_glyph(rid); - } else { - return TS->font_get_spacing_space(rid); - } +int FontData::get_cache_count() const { + return cache.size(); } -void FontData::set_spacing(int p_type, int p_value) { - ERR_FAIL_COND(rid == RID()); - if (p_type == SPACING_GLYPH) { - TS->font_set_spacing_glyph(rid, p_value); - } else { - TS->font_set_spacing_space(rid, p_value); +void FontData::clear_cache() { + _clear_cache(); + cache.clear(); +} + +void FontData::remove_cache(int p_cache_index) { + ERR_FAIL_INDEX(p_cache_index, cache.size()); + if (cache[p_cache_index].is_valid()) { + TS->free(cache.write[p_cache_index]); } + cache.remove(p_cache_index); emit_changed(); } -void FontData::set_antialiased(bool p_antialiased) { - ERR_FAIL_COND(rid == RID()); - TS->font_set_antialiased(rid, p_antialiased); - emit_changed(); +Array FontData::get_size_cache_list(int p_cache_index) const { + ERR_FAIL_COND_V(p_cache_index < 0, Array()); + _ensure_rid(p_cache_index); + return TS->font_get_size_cache_list(cache[p_cache_index]); } -bool FontData::get_antialiased() const { - if (rid == RID()) { - return false; - } - return TS->font_get_antialiased(rid); +void FontData::clear_size_cache(int p_cache_index) { + ERR_FAIL_COND(p_cache_index < 0); + _ensure_rid(p_cache_index); + TS->font_clear_size_cache(cache[p_cache_index]); +} + +void FontData::remove_size_cache(int p_cache_index, const Vector2i &p_size) { + ERR_FAIL_COND(p_cache_index < 0); + _ensure_rid(p_cache_index); + TS->font_remove_size_cache(cache[p_cache_index], p_size); } -void FontData::set_distance_field_hint(bool p_distance_field) { - ERR_FAIL_COND(rid == RID()); - TS->font_set_distance_field_hint(rid, p_distance_field); +void FontData::set_variation_coordinates(int p_cache_index, const Dictionary &p_variation_coordinates) { + ERR_FAIL_COND(p_cache_index < 0); + _ensure_rid(p_cache_index); + TS->font_set_variation_coordinates(cache[p_cache_index], p_variation_coordinates); emit_changed(); } -bool FontData::get_distance_field_hint() const { - if (rid == RID()) { - return false; - } - return TS->font_get_distance_field_hint(rid); +Dictionary FontData::get_variation_coordinates(int p_cache_index) const { + ERR_FAIL_COND_V(p_cache_index < 0, Dictionary()); + _ensure_rid(p_cache_index); + return TS->font_get_variation_coordinates(cache[p_cache_index]); } -void FontData::set_hinting(TextServer::Hinting p_hinting) { - ERR_FAIL_COND(rid == RID()); - TS->font_set_hinting(rid, p_hinting); - emit_changed(); +void FontData::set_ascent(int p_cache_index, int p_size, real_t p_ascent) { + ERR_FAIL_COND(p_cache_index < 0); + _ensure_rid(p_cache_index); + TS->font_set_ascent(cache[p_cache_index], p_size, p_ascent); } -TextServer::Hinting FontData::get_hinting() const { - if (rid == RID()) { - return TextServer::HINTING_NONE; - } - return TS->font_get_hinting(rid); +real_t FontData::get_ascent(int p_cache_index, int p_size) const { + ERR_FAIL_COND_V(p_cache_index < 0, 0.f); + _ensure_rid(p_cache_index); + return TS->font_get_ascent(cache[p_cache_index], p_size); } -void FontData::set_force_autohinter(bool p_enabeld) { - ERR_FAIL_COND(rid == RID()); - TS->font_set_force_autohinter(rid, p_enabeld); - emit_changed(); +void FontData::set_descent(int p_cache_index, int p_size, real_t p_descent) { + ERR_FAIL_COND(p_cache_index < 0); + _ensure_rid(p_cache_index); + TS->font_set_descent(cache[p_cache_index], p_size, p_descent); } -bool FontData::get_force_autohinter() const { - if (rid == RID()) { - return false; - } - return TS->font_get_force_autohinter(rid); +real_t FontData::get_descent(int p_cache_index, int p_size) const { + ERR_FAIL_COND_V(p_cache_index < 0, 0.f); + _ensure_rid(p_cache_index); + return TS->font_get_descent(cache[p_cache_index], p_size); } -bool FontData::has_char(char32_t p_char) const { - if (rid == RID()) { - return false; - } - return TS->font_has_char(rid, p_char); +void FontData::set_underline_position(int p_cache_index, int p_size, real_t p_underline_position) { + ERR_FAIL_COND(p_cache_index < 0); + _ensure_rid(p_cache_index); + TS->font_set_underline_position(cache[p_cache_index], p_size, p_underline_position); } -String FontData::get_supported_chars() const { - ERR_FAIL_COND_V(rid == RID(), String()); - return TS->font_get_supported_chars(rid); +real_t FontData::get_underline_position(int p_cache_index, int p_size) const { + ERR_FAIL_COND_V(p_cache_index < 0, 0.f); + _ensure_rid(p_cache_index); + return TS->font_get_underline_position(cache[p_cache_index], p_size); } -Vector2 FontData::get_glyph_advance(uint32_t p_index, int p_size) const { - ERR_FAIL_COND_V(rid == RID(), Vector2()); - return TS->font_get_glyph_advance(rid, p_index, (p_size < 0) ? base_size : p_size); +void FontData::set_underline_thickness(int p_cache_index, int p_size, real_t p_underline_thickness) { + ERR_FAIL_COND(p_cache_index < 0); + _ensure_rid(p_cache_index); + TS->font_set_underline_thickness(cache[p_cache_index], p_size, p_underline_thickness); } -Vector2 FontData::get_glyph_kerning(uint32_t p_index_a, uint32_t p_index_b, int p_size) const { - ERR_FAIL_COND_V(rid == RID(), Vector2()); - return TS->font_get_glyph_kerning(rid, p_index_a, p_index_b, (p_size < 0) ? base_size : p_size); +real_t FontData::get_underline_thickness(int p_cache_index, int p_size) const { + ERR_FAIL_COND_V(p_cache_index < 0, 0.f); + _ensure_rid(p_cache_index); + return TS->font_get_underline_thickness(cache[p_cache_index], p_size); } -bool FontData::has_outline() const { - if (rid == RID()) { - return false; - } - return TS->font_has_outline(rid); +void FontData::set_scale(int p_cache_index, int p_size, real_t p_scale) { + ERR_FAIL_COND(p_cache_index < 0); + _ensure_rid(p_cache_index); + TS->font_set_scale(cache[p_cache_index], p_size, p_scale); } -float FontData::get_base_size() const { - return base_size; +real_t FontData::get_scale(int p_cache_index, int p_size) const { + ERR_FAIL_COND_V(p_cache_index < 0, 0.f); + _ensure_rid(p_cache_index); + return TS->font_get_scale(cache[p_cache_index], p_size); +} + +void FontData::set_spacing(int p_cache_index, int p_size, TextServer::SpacingType p_spacing, int p_value) { + ERR_FAIL_COND(p_cache_index < 0); + _ensure_rid(p_cache_index); + TS->font_set_spacing(cache[p_cache_index], p_size, p_spacing, p_value); +} + +int FontData::get_spacing(int p_cache_index, int p_size, TextServer::SpacingType p_spacing) const { + ERR_FAIL_COND_V(p_cache_index < 0, 0); + _ensure_rid(p_cache_index); + return TS->font_get_spacing(cache[p_cache_index], p_size, p_spacing); +} + +int FontData::get_texture_count(int p_cache_index, const Vector2i &p_size) const { + ERR_FAIL_COND_V(p_cache_index < 0, 0); + _ensure_rid(p_cache_index); + return TS->font_get_texture_count(cache[p_cache_index], p_size); +} + +void FontData::clear_textures(int p_cache_index, const Vector2i &p_size) { + ERR_FAIL_COND(p_cache_index < 0); + _ensure_rid(p_cache_index); + TS->font_clear_textures(cache[p_cache_index], p_size); +} + +void FontData::remove_texture(int p_cache_index, const Vector2i &p_size, int p_texture_index) { + ERR_FAIL_COND(p_cache_index < 0); + _ensure_rid(p_cache_index); + TS->font_remove_texture(cache[p_cache_index], p_size, p_texture_index); +} + +void FontData::set_texture_image(int p_cache_index, const Vector2i &p_size, int p_texture_index, const Ref<Image> &p_image) { + ERR_FAIL_COND(p_cache_index < 0); + _ensure_rid(p_cache_index); + TS->font_set_texture_image(cache[p_cache_index], p_size, p_texture_index, p_image); +} + +Ref<Image> FontData::get_texture_image(int p_cache_index, const Vector2i &p_size, int p_texture_index) const { + ERR_FAIL_COND_V(p_cache_index < 0, Ref<Image>()); + _ensure_rid(p_cache_index); + return TS->font_get_texture_image(cache[p_cache_index], p_size, p_texture_index); +} + +void FontData::set_texture_offsets(int p_cache_index, const Vector2i &p_size, int p_texture_index, const PackedInt32Array &p_offset) { + ERR_FAIL_COND(p_cache_index < 0); + _ensure_rid(p_cache_index); + TS->font_set_texture_offsets(cache[p_cache_index], p_size, p_texture_index, p_offset); +} + +PackedInt32Array FontData::get_texture_offsets(int p_cache_index, const Vector2i &p_size, int p_texture_index) const { + ERR_FAIL_COND_V(p_cache_index < 0, PackedInt32Array()); + _ensure_rid(p_cache_index); + return TS->font_get_texture_offsets(cache[p_cache_index], p_size, p_texture_index); +} + +Array FontData::get_glyph_list(int p_cache_index, const Vector2i &p_size) const { + ERR_FAIL_COND_V(p_cache_index < 0, Array()); + _ensure_rid(p_cache_index); + return TS->font_get_glyph_list(cache[p_cache_index], p_size); +} + +void FontData::clear_glyphs(int p_cache_index, const Vector2i &p_size) { + ERR_FAIL_COND(p_cache_index < 0); + _ensure_rid(p_cache_index); + TS->font_clear_glyphs(cache[p_cache_index], p_size); +} + +void FontData::remove_glyph(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) { + ERR_FAIL_COND(p_cache_index < 0); + _ensure_rid(p_cache_index); + TS->font_remove_glyph(cache[p_cache_index], p_size, p_glyph); +} + +void FontData::set_glyph_advance(int p_cache_index, int p_size, int32_t p_glyph, const Vector2 &p_advance) { + ERR_FAIL_COND(p_cache_index < 0); + _ensure_rid(p_cache_index); + TS->font_set_glyph_advance(cache[p_cache_index], p_size, p_glyph, p_advance); +} + +Vector2 FontData::get_glyph_advance(int p_cache_index, int p_size, int32_t p_glyph) const { + ERR_FAIL_COND_V(p_cache_index < 0, Vector2()); + _ensure_rid(p_cache_index); + return TS->font_get_glyph_advance(cache[p_cache_index], p_size, p_glyph); +} + +void FontData::set_glyph_offset(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_offset) { + ERR_FAIL_COND(p_cache_index < 0); + _ensure_rid(p_cache_index); + TS->font_set_glyph_offset(cache[p_cache_index], p_size, p_glyph, p_offset); +} + +Vector2 FontData::get_glyph_offset(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const { + ERR_FAIL_COND_V(p_cache_index < 0, Vector2()); + _ensure_rid(p_cache_index); + return TS->font_get_glyph_offset(cache[p_cache_index], p_size, p_glyph); +} + +void FontData::set_glyph_size(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_gl_size) { + ERR_FAIL_COND(p_cache_index < 0); + _ensure_rid(p_cache_index); + TS->font_set_glyph_size(cache[p_cache_index], p_size, p_glyph, p_gl_size); +} + +Vector2 FontData::get_glyph_size(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const { + ERR_FAIL_COND_V(p_cache_index < 0, Vector2()); + _ensure_rid(p_cache_index); + return TS->font_get_glyph_size(cache[p_cache_index], p_size, p_glyph); +} + +void FontData::set_glyph_uv_rect(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Rect2 &p_uv_rect) { + ERR_FAIL_COND(p_cache_index < 0); + _ensure_rid(p_cache_index); + TS->font_set_glyph_uv_rect(cache[p_cache_index], p_size, p_glyph, p_uv_rect); +} + +Rect2 FontData::get_glyph_uv_rect(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const { + ERR_FAIL_COND_V(p_cache_index < 0, Rect2()); + _ensure_rid(p_cache_index); + return TS->font_get_glyph_uv_rect(cache[p_cache_index], p_size, p_glyph); +} + +void FontData::set_glyph_texture_idx(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, int p_texture_idx) { + ERR_FAIL_COND(p_cache_index < 0); + _ensure_rid(p_cache_index); + TS->font_set_glyph_texture_idx(cache[p_cache_index], p_size, p_glyph, p_texture_idx); +} + +int FontData::get_glyph_texture_idx(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const { + ERR_FAIL_COND_V(p_cache_index < 0, 0); + _ensure_rid(p_cache_index); + return TS->font_get_glyph_texture_idx(cache[p_cache_index], p_size, p_glyph); +} + +Array FontData::get_kerning_list(int p_cache_index, int p_size) const { + ERR_FAIL_COND_V(p_cache_index < 0, Array()); + _ensure_rid(p_cache_index); + return TS->font_get_kerning_list(cache[p_cache_index], p_size); +} + +void FontData::clear_kerning_map(int p_cache_index, int p_size) { + ERR_FAIL_COND(p_cache_index < 0); + _ensure_rid(p_cache_index); + TS->font_clear_kerning_map(cache[p_cache_index], p_size); +} + +void FontData::remove_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair) { + ERR_FAIL_COND(p_cache_index < 0); + _ensure_rid(p_cache_index); + TS->font_remove_kerning(cache[p_cache_index], p_size, p_glyph_pair); +} + +void FontData::set_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) { + ERR_FAIL_COND(p_cache_index < 0); + _ensure_rid(p_cache_index); + TS->font_set_kerning(cache[p_cache_index], p_size, p_glyph_pair, p_kerning); +} + +Vector2 FontData::get_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair) const { + ERR_FAIL_COND_V(p_cache_index < 0, Vector2()); + _ensure_rid(p_cache_index); + return TS->font_get_kerning(cache[p_cache_index], p_size, p_glyph_pair); +} + +void FontData::render_range(int p_cache_index, const Vector2i &p_size, char32_t p_start, char32_t p_end) { + ERR_FAIL_COND(p_cache_index < 0); + _ensure_rid(p_cache_index); + TS->font_render_range(cache[p_cache_index], p_size, p_start, p_end); +} + +void FontData::render_glyph(int p_cache_index, const Vector2i &p_size, int32_t p_index) { + ERR_FAIL_COND(p_cache_index < 0); + _ensure_rid(p_cache_index); + TS->font_render_glyph(cache[p_cache_index], p_size, p_index); +} + +RID FontData::get_cache_rid(int p_cache_index) const { + ERR_FAIL_COND_V(p_cache_index < 0, RID()); + _ensure_rid(p_cache_index); + return cache[p_cache_index]; } bool FontData::is_language_supported(const String &p_language) const { - if (rid == RID()) { - return false; - } - return TS->font_is_language_supported(rid, p_language); + _ensure_rid(0); + return TS->font_is_language_supported(cache[0], p_language); } void FontData::set_language_support_override(const String &p_language, bool p_supported) { - ERR_FAIL_COND(rid == RID()); - TS->font_set_language_support_override(rid, p_language, p_supported); - emit_changed(); + _ensure_rid(0); + TS->font_set_language_support_override(cache[0], p_language, p_supported); } bool FontData::get_language_support_override(const String &p_language) const { - if (rid == RID()) { - return false; - } - return TS->font_get_language_support_override(rid, p_language); + _ensure_rid(0); + return TS->font_get_language_support_override(cache[0], p_language); } void FontData::remove_language_support_override(const String &p_language) { - ERR_FAIL_COND(rid == RID()); - TS->font_remove_language_support_override(rid, p_language); - emit_changed(); + _ensure_rid(0); + TS->font_remove_language_support_override(cache[0], p_language); } Vector<String> FontData::get_language_support_overrides() const { - if (rid == RID()) { - return Vector<String>(); - } - return TS->font_get_language_support_overrides(rid); + _ensure_rid(0); + return TS->font_get_language_support_overrides(cache[0]); } bool FontData::is_script_supported(const String &p_script) const { - if (rid == RID()) { - return false; - } - return TS->font_is_script_supported(rid, p_script); + _ensure_rid(0); + return TS->font_is_script_supported(cache[0], p_script); } void FontData::set_script_support_override(const String &p_script, bool p_supported) { - ERR_FAIL_COND(rid == RID()); - TS->font_set_script_support_override(rid, p_script, p_supported); - emit_changed(); + _ensure_rid(0); + TS->font_set_script_support_override(cache[0], p_script, p_supported); } bool FontData::get_script_support_override(const String &p_script) const { - if (rid == RID()) { - return false; - } - return TS->font_get_script_support_override(rid, p_script); + _ensure_rid(0); + return TS->font_get_script_support_override(cache[0], p_script); } void FontData::remove_script_support_override(const String &p_script) { - ERR_FAIL_COND(rid == RID()); - TS->font_remove_script_support_override(rid, p_script); - emit_changed(); + _ensure_rid(0); + TS->font_remove_script_support_override(cache[0], p_script); } Vector<String> FontData::get_script_support_overrides() const { - if (rid == RID()) { - return Vector<String>(); - } - return TS->font_get_script_support_overrides(rid); + _ensure_rid(0); + return TS->font_get_script_support_overrides(cache[0]); } -uint32_t FontData::get_glyph_index(char32_t p_char, char32_t p_variation_selector) const { - ERR_FAIL_COND_V(rid == RID(), 0); - return TS->font_get_glyph_index(rid, p_char, p_variation_selector); +bool FontData::has_char(char32_t p_char) const { + _ensure_rid(0); + return TS->font_has_char(cache[0], p_char); } -Vector2 FontData::draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const { - ERR_FAIL_COND_V(rid == RID(), Vector2()); - return TS->font_draw_glyph(rid, p_canvas, (p_size <= 0) ? base_size : p_size, p_pos, p_index, p_color); +String FontData::get_supported_chars() const { + _ensure_rid(0); + return TS->font_get_supported_chars(cache[0]); } -Vector2 FontData::draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color) const { - ERR_FAIL_COND_V(rid == RID(), Vector2()); - return TS->font_draw_glyph_outline(rid, p_canvas, (p_size <= 0) ? base_size : p_size, p_outline_size, p_pos, p_index, p_color); +int32_t FontData::get_glyph_index(int p_size, char32_t p_char, char32_t p_variation_selector) const { + _ensure_rid(0); + return TS->font_get_glyph_index(cache[0], p_size, p_char, p_variation_selector); } -FontData::FontData() {} +Dictionary FontData::get_supported_feature_list() const { + _ensure_rid(0); + return TS->font_supported_feature_list(cache[0]); +} -FontData::FontData(const String &p_filename, int p_base_size) { - load_resource(p_filename, p_base_size); +Dictionary FontData::get_supported_variation_list() const { + _ensure_rid(0); + return TS->font_supported_variation_list(cache[0]); } -FontData::FontData(const PackedByteArray &p_data, const String &p_type, int p_base_size) { - _load_memory(p_data, p_type, p_base_size); +FontData::FontData() { + /* NOP */ } FontData::~FontData() { - if (rid != RID()) { - TS->free(rid); - } + _clear_cache(); } /*************************************************************************/ +void Font::_data_changed() { + for (int i = 0; i < rids.size(); i++) { + rids.write[i] = RID(); + } + emit_changed(); +} + +void Font::_ensure_rid(int p_index) const { + // Find or create cache record. + if (!rids[p_index].is_valid() && data[p_index].is_valid()) { + rids.write[p_index] = data[p_index]->find_cache(variation_coordinates); + } +} + void Font::_bind_methods() { ClassDB::bind_method(D_METHOD("add_data", "data"), &Font::add_data); ClassDB::bind_method(D_METHOD("set_data", "idx", "data"), &Font::set_data); ClassDB::bind_method(D_METHOD("get_data_count"), &Font::get_data_count); ClassDB::bind_method(D_METHOD("get_data", "idx"), &Font::get_data); + ClassDB::bind_method(D_METHOD("get_data_rid", "idx"), &Font::get_data_rid); + ClassDB::bind_method(D_METHOD("clear_data"), &Font::clear_data); ClassDB::bind_method(D_METHOD("remove_data", "idx"), &Font::remove_data); + ClassDB::bind_method(D_METHOD("set_base_size", "size"), &Font::set_base_size); + ClassDB::bind_method(D_METHOD("get_base_size"), &Font::get_base_size); + ADD_PROPERTY(PropertyInfo(Variant::INT, "base_size"), "set_base_size", "get_base_size"); + + ClassDB::bind_method(D_METHOD("set_variation_coordinates", "variation_coordinates"), &Font::set_variation_coordinates); + ClassDB::bind_method(D_METHOD("get_variation_coordinates"), &Font::get_variation_coordinates); + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "variation_coordinates"), "set_variation_coordinates", "get_variation_coordinates"); + + ClassDB::bind_method(D_METHOD("set_spacing", "spacing", "value"), &Font::set_spacing); + ClassDB::bind_method(D_METHOD("get_spacing", "spacing"), &Font::get_spacing); + + ADD_GROUP("Extra Spacing", "spacing"); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "spacing_top"), "set_spacing", "get_spacing", TextServer::SPACING_TOP); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "spacing_bottom"), "set_spacing", "get_spacing", TextServer::SPACING_BOTTOM); + ClassDB::bind_method(D_METHOD("get_height", "size"), &Font::get_height, DEFVAL(-1)); ClassDB::bind_method(D_METHOD("get_ascent", "size"), &Font::get_ascent, DEFVAL(-1)); ClassDB::bind_method(D_METHOD("get_descent", "size"), &Font::get_descent, DEFVAL(-1)); - ClassDB::bind_method(D_METHOD("get_underline_position", "size"), &Font::get_underline_position, DEFVAL(-1)); ClassDB::bind_method(D_METHOD("get_underline_thickness", "size"), &Font::get_underline_thickness, DEFVAL(-1)); - ClassDB::bind_method(D_METHOD("get_spacing", "type"), &Font::get_spacing); - ClassDB::bind_method(D_METHOD("set_spacing", "type", "value"), &Font::set_spacing); - - ClassDB::bind_method(D_METHOD("get_string_size", "text", "size"), &Font::get_string_size, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("get_string_size", "text", "size", "align", "width", "flags"), &Font::get_string_size, DEFVAL(-1), DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND)); ClassDB::bind_method(D_METHOD("get_multiline_string_size", "text", "width", "size", "flags"), &Font::get_multiline_string_size, DEFVAL(-1), DEFVAL(-1), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND)); ClassDB::bind_method(D_METHOD("draw_string", "canvas_item", "pos", "text", "align", "width", "size", "modulate", "outline_size", "outline_modulate", "flags"), &Font::draw_string, DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND)); ClassDB::bind_method(D_METHOD("draw_multiline_string", "canvas_item", "pos", "text", "align", "width", "max_lines", "size", "modulate", "outline_size", "outline_modulate", "flags"), &Font::draw_multiline_string, DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(-1), DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND)); - ClassDB::bind_method(D_METHOD("has_char", "char"), &Font::has_char); - ClassDB::bind_method(D_METHOD("get_supported_chars"), &Font::get_supported_chars); - ClassDB::bind_method(D_METHOD("get_char_size", "char", "next", "size"), &Font::get_char_size, DEFVAL(0), DEFVAL(-1)); ClassDB::bind_method(D_METHOD("draw_char", "canvas_item", "pos", "char", "next", "size", "modulate", "outline_size", "outline_modulate"), &Font::draw_char, DEFVAL(0), DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0))); - ClassDB::bind_method(D_METHOD("update_changes"), &Font::update_changes); - - ADD_GROUP("Extra Spacing", "extra_spacing"); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "extra_spacing_top"), "set_spacing", "get_spacing", SPACING_TOP); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "extra_spacing_bottom"), "set_spacing", "get_spacing", SPACING_BOTTOM); - - BIND_ENUM_CONSTANT(SPACING_TOP); - BIND_ENUM_CONSTANT(SPACING_BOTTOM); -} - -void Font::_data_changed() { - cache.clear(); - cache_wrap.clear(); + ClassDB::bind_method(D_METHOD("has_char", "char"), &Font::has_char); + ClassDB::bind_method(D_METHOD("get_supported_chars"), &Font::get_supported_chars); - emit_changed(); - notify_property_list_changed(); + ClassDB::bind_method(D_METHOD("update_changes"), &Font::update_changes); } bool Font::_set(const StringName &p_name, const Variant &p_value) { - String str = p_name; + Vector<String> tokens = p_name.operator String().split("/"); #ifndef DISABLE_DEPRECATED - if (str == "font_data") { // Compatibility, DynamicFont main data + if (tokens.size() == 1 && tokens[0] == "font_data") { + // Compatibility, DynamicFont main data. Ref<FontData> fd = p_value; if (fd.is_valid()) { add_data(fd); return true; } return false; - } else if (str.begins_with("fallback/")) { // Compatibility, DynamicFont fallback data + } else if (tokens.size() == 2 && tokens[0] == "fallback") { + // Compatibility, DynamicFont fallback data. Ref<FontData> fd = p_value; if (fd.is_valid()) { add_data(fd); return true; } return false; - } else if (str == "fallback") { // Compatibility, BitmapFont fallback + } else if (tokens.size() == 1 && tokens[0] == "fallback") { + // Compatibility, BitmapFont fallback data. Ref<Font> f = p_value; if (f.is_valid()) { for (int i = 0; i < f->get_data_count(); i++) { @@ -612,10 +1139,9 @@ bool Font::_set(const StringName &p_name, const Variant &p_value) { return false; } #endif /* DISABLE_DEPRECATED */ - if (str.begins_with("data/")) { - int idx = str.get_slicec('/', 1).to_int(); + if (tokens.size() == 2 && tokens[0] == "data") { + int idx = tokens[1].to_int(); Ref<FontData> fd = p_value; - if (fd.is_valid()) { if (idx == data.size()) { add_data(fd); @@ -631,14 +1157,13 @@ bool Font::_set(const StringName &p_name, const Variant &p_value) { return true; } } - return false; } bool Font::_get(const StringName &p_name, Variant &r_ret) const { - String str = p_name; - if (str.begins_with("data/")) { - int idx = str.get_slicec('/', 1).to_int(); + Vector<String> tokens = p_name.operator String().split("/"); + if (tokens.size() == 2 && tokens[0] == "data") { + int idx = tokens[1].to_int(); if (idx == data.size()) { r_ret = Ref<FontData>(); @@ -656,24 +1181,52 @@ void Font::_get_property_list(List<PropertyInfo> *p_list) const { for (int i = 0; i < data.size(); i++) { p_list->push_back(PropertyInfo(Variant::OBJECT, "data/" + itos(i), PROPERTY_HINT_RESOURCE_TYPE, "FontData")); } - p_list->push_back(PropertyInfo(Variant::OBJECT, "data/" + itos(data.size()), PROPERTY_HINT_RESOURCE_TYPE, "FontData")); } void Font::reset_state() { - spacing_top = 0; - spacing_bottom = 0; + for (int i = 0; i < data.size(); i++) { + if (data[i].is_valid()) { + data.write[i]->connect(SNAME("changed"), callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED); + } + } cache.clear(); cache_wrap.clear(); data.clear(); + rids.clear(); + + base_size = 16; + variation_coordinates.clear(); + spacing_bottom = 0; + spacing_top = 0; +} + +Dictionary Font::get_feature_list() const { + Dictionary out; + for (int i = 0; i < data.size(); i++) { + Dictionary data_ftrs = data[i]->get_supported_feature_list(); + for (const Variant *ftr = data_ftrs.next(nullptr); ftr != nullptr; ftr = data_ftrs.next(ftr)) { + out[*ftr] = data_ftrs[*ftr]; + } + } + return out; } void Font::add_data(const Ref<FontData> &p_data) { ERR_FAIL_COND(p_data.is_null()); data.push_back(p_data); + rids.push_back(RID()); if (data[data.size() - 1].is_valid()) { - data.write[data.size() - 1]->connect("changed", callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED); + data.write[data.size() - 1]->connect(SNAME("changed"), callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED); + Dictionary data_var_list = p_data->get_supported_variation_list(); + for (int j = 0; j < data_var_list.size(); j++) { + int32_t tag = data_var_list.get_key_at_index(j); + Vector3i value = data_var_list.get_value_at_index(j); + if (!variation_coordinates.has(tag) && !variation_coordinates.has(TS->tag_to_name(tag))) { + variation_coordinates[TS->tag_to_name(tag)] = value.z; + } + } } cache.clear(); @@ -688,13 +1241,22 @@ void Font::set_data(int p_idx, const Ref<FontData> &p_data) { ERR_FAIL_INDEX(p_idx, data.size()); if (data[p_idx].is_valid()) { - data.write[p_idx]->disconnect("changed", callable_mp(this, &Font::_data_changed)); + data.write[p_idx]->disconnect(SNAME("changed"), callable_mp(this, &Font::_data_changed)); } data.write[p_idx] = p_data; + rids.write[p_idx] = RID(); + Dictionary data_var_list = p_data->get_supported_variation_list(); + for (int j = 0; j < data_var_list.size(); j++) { + int32_t tag = data_var_list.get_key_at_index(j); + Vector3i value = data_var_list.get_value_at_index(j); + if (!variation_coordinates.has(tag) && !variation_coordinates.has(TS->tag_to_name(tag))) { + variation_coordinates[TS->tag_to_name(tag)] = value.z; + } + } if (data[p_idx].is_valid()) { - data.write[p_idx]->connect("changed", callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED); + data.write[p_idx]->connect(SNAME("changed"), callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED); } cache.clear(); @@ -713,14 +1275,31 @@ Ref<FontData> Font::get_data(int p_idx) const { return data[p_idx]; } +RID Font::get_data_rid(int p_idx) const { + ERR_FAIL_INDEX_V(p_idx, data.size(), RID()); + _ensure_rid(p_idx); + return rids[p_idx]; +} + +void Font::clear_data() { + for (int i = 0; i < data.size(); i++) { + if (data[i].is_valid()) { + data.write[i]->connect(SNAME("changed"), callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED); + } + } + data.clear(); + rids.clear(); +} + void Font::remove_data(int p_idx) { ERR_FAIL_INDEX(p_idx, data.size()); if (data[p_idx].is_valid()) { - data.write[p_idx]->disconnect("changed", callable_mp(this, &Font::_data_changed)); + data.write[p_idx]->disconnect(SNAME("changed"), callable_mp(this, &Font::_data_changed)); } data.remove(p_idx); + rids.remove(p_idx); cache.clear(); cache_wrap.clear(); @@ -729,117 +1308,148 @@ void Font::remove_data(int p_idx) { notify_property_list_changed(); } -Dictionary Font::get_feature_list() const { - Dictionary out; - for (int i = 0; i < data.size(); i++) { - Dictionary data_ftrs = data[i]->get_feature_list(); - for (const Variant *ftr = data_ftrs.next(nullptr); ftr != nullptr; ftr = data_ftrs.next(ftr)) { - out[*ftr] = data_ftrs[*ftr]; - } +void Font::set_base_size(int p_size) { + base_size = p_size; +} + +int Font::get_base_size() const { + return base_size; +} + +void Font::set_variation_coordinates(const Dictionary &p_variation_coordinates) { + _data_changed(); + variation_coordinates = p_variation_coordinates; +} + +Dictionary Font::get_variation_coordinates() const { + return variation_coordinates; +} + +void Font::set_spacing(TextServer::SpacingType p_spacing, int p_value) { + _data_changed(); + switch (p_spacing) { + case TextServer::SPACING_TOP: { + spacing_top = p_value; + } break; + case TextServer::SPACING_BOTTOM: { + spacing_bottom = p_value; + } break; + default: { + ERR_FAIL_MSG("Invalid spacing type: " + itos(p_spacing)); + } break; + } +} + +int Font::get_spacing(TextServer::SpacingType p_spacing) const { + switch (p_spacing) { + case TextServer::SPACING_TOP: { + return spacing_top; + } break; + case TextServer::SPACING_BOTTOM: { + return spacing_bottom; + } break; + default: { + ERR_FAIL_V_MSG(0, "Invalid spacing type: " + itos(p_spacing)); + } break; } - return out; } -float Font::get_height(int p_size) const { - float ret = 0.f; +real_t Font::get_height(int p_size) const { + int size = (p_size <= 0) ? base_size : p_size; + real_t ret = 0.f; for (int i = 0; i < data.size(); i++) { - ret = MAX(ret, data[i]->get_height(p_size)); + _ensure_rid(i); + ret = MAX(ret, TS->font_get_ascent(rids[i], size) + TS->font_get_descent(rids[i], size)); } - return ret + spacing_top + spacing_bottom; + return ret + spacing_bottom + spacing_top; } -float Font::get_ascent(int p_size) const { - float ret = 0.f; +real_t Font::get_ascent(int p_size) const { + int size = (p_size <= 0) ? base_size : p_size; + real_t ret = 0.f; for (int i = 0; i < data.size(); i++) { - ret = MAX(ret, data[i]->get_ascent(p_size)); + _ensure_rid(i); + ret = MAX(ret, TS->font_get_ascent(rids[i], size)); } return ret + spacing_top; } -float Font::get_descent(int p_size) const { - float ret = 0.f; +real_t Font::get_descent(int p_size) const { + int size = (p_size <= 0) ? base_size : p_size; + real_t ret = 0.f; for (int i = 0; i < data.size(); i++) { - ret = MAX(ret, data[i]->get_descent(p_size)); + _ensure_rid(i); + ret = MAX(ret, TS->font_get_descent(rids[i], size)); } return ret + spacing_bottom; } -float Font::get_underline_position(int p_size) const { - float ret = 0.f; +real_t Font::get_underline_position(int p_size) const { + int size = (p_size <= 0) ? base_size : p_size; + real_t ret = 0.f; for (int i = 0; i < data.size(); i++) { - ret = MAX(ret, data[i]->get_underline_position(p_size)); + _ensure_rid(i); + ret = MAX(ret, TS->font_get_underline_position(rids[i], size)); } - return ret; + return ret + spacing_top; } -float Font::get_underline_thickness(int p_size) const { - float ret = 0.f; +real_t Font::get_underline_thickness(int p_size) const { + int size = (p_size <= 0) ? base_size : p_size; + real_t ret = 0.f; for (int i = 0; i < data.size(); i++) { - ret = MAX(ret, data[i]->get_underline_thickness(p_size)); + _ensure_rid(i); + ret = MAX(ret, TS->font_get_underline_thickness(rids[i], size)); } return ret; } -int Font::get_spacing(int p_type) const { - if (p_type == SPACING_TOP) { - return spacing_top; - } else if (p_type == SPACING_BOTTOM) { - return spacing_bottom; - } +Size2 Font::get_string_size(const String &p_text, int p_size, HAlign p_align, real_t p_width, uint16_t p_flags) const { + ERR_FAIL_COND_V(data.is_empty(), Size2()); - return 0; -} + int size = (p_size <= 0) ? base_size : p_size; -void Font::set_spacing(int p_type, int p_value) { - if (p_type == SPACING_TOP) { - spacing_top = p_value; - } else if (p_type == SPACING_BOTTOM) { - spacing_bottom = p_value; + for (int i = 0; i < data.size(); i++) { + _ensure_rid(i); } - emit_changed(); - notify_property_list_changed(); -} - -// Drawing string and string sizes, cached. - -Size2 Font::get_string_size(const String &p_text, int p_size) const { - ERR_FAIL_COND_V(data.is_empty(), Size2()); - uint64_t hash = p_text.hash64(); - hash = hash_djb2_one_64(p_size, hash); + if (p_align == HALIGN_FILL) { + hash = hash_djb2_one_64(hash_djb2_one_float(p_width), hash); + hash = hash_djb2_one_64(p_flags, hash); + } + hash = hash_djb2_one_64(size, hash); Ref<TextLine> buffer; if (cache.has(hash)) { buffer = cache.get(hash); } else { buffer.instantiate(); - int size = p_size <= 0 ? data[0]->get_base_size() : p_size; buffer->add_string(p_text, Ref<Font>(this), size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); cache.insert(hash, buffer); } - if (buffer->get_orientation() == TextServer::ORIENTATION_HORIZONTAL) { - return buffer->get_size() + Vector2(0, spacing_top + spacing_bottom); - } else { - return buffer->get_size() + Vector2(spacing_top + spacing_bottom, 0); - } + return buffer->get_size(); } -Size2 Font::get_multiline_string_size(const String &p_text, float p_width, int p_size, uint8_t p_flags) const { +Size2 Font::get_multiline_string_size(const String &p_text, real_t p_width, int p_size, uint16_t p_flags) const { ERR_FAIL_COND_V(data.is_empty(), Size2()); - uint64_t hash = p_text.hash64(); - hash = hash_djb2_one_64(p_size, hash); + int size = (p_size <= 0) ? base_size : p_size; + for (int i = 0; i < data.size(); i++) { + _ensure_rid(i); + } + + uint64_t hash = p_text.hash64(); uint64_t wrp_hash = hash_djb2_one_64(hash_djb2_one_float(p_width), hash); wrp_hash = hash_djb2_one_64(p_flags, wrp_hash); + wrp_hash = hash_djb2_one_64(size, wrp_hash); Ref<TextParagraph> lines_buffer; if (cache_wrap.has(wrp_hash)) { lines_buffer = cache_wrap.get(wrp_hash); } else { lines_buffer.instantiate(); - int size = p_size <= 0 ? data[0]->get_base_size() : p_size; lines_buffer->add_string(p_text, Ref<Font>(this), size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); lines_buffer->set_width(p_width); lines_buffer->set_flags(p_flags); @@ -851,40 +1461,50 @@ Size2 Font::get_multiline_string_size(const String &p_text, float p_width, int p Size2 line_size = lines_buffer->get_line_size(i); if (lines_buffer->get_orientation() == TextServer::ORIENTATION_HORIZONTAL) { ret.x = MAX(ret.x, line_size.x); - ret.y += line_size.y + spacing_top + spacing_bottom; + ret.y += line_size.y; } else { ret.y = MAX(ret.y, line_size.y); - ret.x += line_size.x + spacing_top + spacing_bottom; + ret.x += line_size.x; } } return ret; } -void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align, float p_width, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint8_t p_flags) const { +void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align, real_t p_width, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint16_t p_flags) const { ERR_FAIL_COND(data.is_empty()); + int size = (p_size <= 0) ? base_size : p_size; + + for (int i = 0; i < data.size(); i++) { + _ensure_rid(i); + } + uint64_t hash = p_text.hash64(); - hash = hash_djb2_one_64(p_size, hash); + if (p_align == HALIGN_FILL) { + hash = hash_djb2_one_64(hash_djb2_one_float(p_width), hash); + hash = hash_djb2_one_64(p_flags, hash); + } + hash = hash_djb2_one_64(size, hash); Ref<TextLine> buffer; if (cache.has(hash)) { buffer = cache.get(hash); } else { buffer.instantiate(); - int size = p_size <= 0 ? data[0]->get_base_size() : p_size; buffer->add_string(p_text, Ref<Font>(this), size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); cache.insert(hash, buffer); } Vector2 ofs = p_pos; if (buffer->get_orientation() == TextServer::ORIENTATION_HORIZONTAL) { - ofs.y += spacing_top - buffer->get_line_ascent(); + ofs.y -= buffer->get_line_ascent(); } else { - ofs.x += spacing_top - buffer->get_line_ascent(); + ofs.x -= buffer->get_line_ascent(); } buffer->set_width(p_width); buffer->set_align(p_align); + buffer->set_flags(p_flags); if (p_outline_size > 0 && p_outline_modulate.a != 0.0f) { buffer->draw_outline(p_canvas_item, ofs, p_outline_size, p_outline_modulate); @@ -892,21 +1512,25 @@ void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_t buffer->draw(p_canvas_item, ofs, p_modulate); } -void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align, float p_width, int p_max_lines, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint8_t p_flags) const { +void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align, float p_width, int p_max_lines, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint16_t p_flags) const { ERR_FAIL_COND(data.is_empty()); - uint64_t hash = p_text.hash64(); - hash = hash_djb2_one_64(p_size, hash); + int size = (p_size <= 0) ? base_size : p_size; + for (int i = 0; i < data.size(); i++) { + _ensure_rid(i); + } + + uint64_t hash = p_text.hash64(); uint64_t wrp_hash = hash_djb2_one_64(hash_djb2_one_float(p_width), hash); wrp_hash = hash_djb2_one_64(p_flags, wrp_hash); + wrp_hash = hash_djb2_one_64(size, wrp_hash); Ref<TextParagraph> lines_buffer; if (cache_wrap.has(wrp_hash)) { lines_buffer = cache_wrap.get(wrp_hash); } else { lines_buffer.instantiate(); - int size = p_size <= 0 ? data[0]->get_base_size() : p_size; lines_buffer->add_string(p_text, Ref<Font>(this), size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); lines_buffer->set_width(p_width); lines_buffer->set_flags(p_flags); @@ -918,12 +1542,10 @@ void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const S Vector2 lofs = p_pos; for (int i = 0; i < lines_buffer->get_line_count(); i++) { if (lines_buffer->get_orientation() == TextServer::ORIENTATION_HORIZONTAL) { - lofs.y += spacing_top; if (i == 0) { lofs.y -= lines_buffer->get_line_ascent(0); } } else { - lofs.x += spacing_top; if (i == 0) { lofs.x -= lines_buffer->get_line_ascent(0); } @@ -939,9 +1561,9 @@ void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const S Size2 line_size = lines_buffer->get_line_size(i); if (lines_buffer->get_orientation() == TextServer::ORIENTATION_HORIZONTAL) { - lofs.y += line_size.y + spacing_bottom; + lofs.y += line_size.y; } else { - lofs.x += line_size.x + spacing_bottom; + lofs.x += line_size.x; } if ((p_max_lines > 0) && (i >= p_max_lines)) { @@ -950,37 +1572,17 @@ void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const S } } -bool Font::has_char(char32_t p_char) const { - for (int i = 0; i < data.size(); i++) { - if (data[i]->has_char(p_char)) { - return true; - } - } - return false; -} - -String Font::get_supported_chars() const { - String chars; - for (int i = 0; i < data.size(); i++) { - String data_chars = data[i]->get_supported_chars(); - for (int j = 0; j < data_chars.length(); j++) { - if (chars.find_char(data_chars[j]) == -1) { - chars += data_chars[j]; - } - } - } - return chars; -} - Size2 Font::get_char_size(char32_t p_char, char32_t p_next, int p_size) const { + int size = (p_size <= 0) ? base_size : p_size; + for (int i = 0; i < data.size(); i++) { + _ensure_rid(i); if (data[i]->has_char(p_char)) { - int size = p_size <= 0 ? data[i]->get_base_size() : p_size; - uint32_t glyph_a = data[i]->get_glyph_index(p_char); - Size2 ret = Size2(data[i]->get_glyph_advance(glyph_a, size).x, data[i]->get_height(size)); + int32_t glyph_a = TS->font_get_glyph_index(rids[i], size, p_char, 0); + Size2 ret = Size2(TS->font_get_glyph_advance(rids[i], size, glyph_a).x, TS->font_get_ascent(rids[i], size) + TS->font_get_descent(rids[i], size)); if ((p_next != 0) && data[i]->has_char(p_next)) { - uint32_t glyph_b = data[i]->get_glyph_index(p_next); - ret.x -= data[i]->get_glyph_kerning(glyph_a, glyph_b, size).x; + int32_t glyph_b = TS->font_get_glyph_index(rids[i], size, p_next, 0); + ret.x -= TS->font_get_kerning(rids[i], size, Vector2i(glyph_a, glyph_b)).x; } return ret; } @@ -988,35 +1590,55 @@ Size2 Font::get_char_size(char32_t p_char, char32_t p_next, int p_size) const { return Size2(); } -float Font::draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate) const { +real_t Font::draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate) const { + int size = (p_size <= 0) ? base_size : p_size; + for (int i = 0; i < data.size(); i++) { + _ensure_rid(i); if (data[i]->has_char(p_char)) { - int size = p_size <= 0 ? data[i]->get_base_size() : p_size; - uint32_t glyph_a = data[i]->get_glyph_index(p_char); - float ret = data[i]->get_glyph_advance(glyph_a, size).x; + int32_t glyph_a = TS->font_get_glyph_index(rids[i], size, p_char, 0); + real_t ret = TS->font_get_glyph_advance(rids[i], size, glyph_a).x; if ((p_next != 0) && data[i]->has_char(p_next)) { - uint32_t glyph_b = data[i]->get_glyph_index(p_next); - ret -= data[i]->get_glyph_kerning(glyph_a, glyph_b, size).x; + int32_t glyph_b = TS->font_get_glyph_index(rids[i], size, p_next, 0); + ret -= TS->font_get_kerning(rids[i], size, Vector2i(glyph_a, glyph_b)).x; } + if (p_outline_size > 0 && p_outline_modulate.a != 0.0f) { - data[i]->draw_glyph_outline(p_canvas_item, size, p_outline_size, p_pos, glyph_a, p_outline_modulate); + TS->font_draw_glyph_outline(rids[i], p_canvas_item, size, p_outline_size, p_pos, glyph_a, p_outline_modulate); } - data[i]->draw_glyph(p_canvas_item, size, p_pos, glyph_a, p_modulate); + TS->font_draw_glyph(rids[i], p_canvas_item, size, p_pos, glyph_a, p_modulate); return ret; } } return 0; } -Vector<RID> Font::get_rids() const { - Vector<RID> ret; +bool Font::has_char(char32_t p_char) const { + for (int i = 0; i < data.size(); i++) { + if (data[i]->has_char(p_char)) + return true; + } + return false; +} + +String Font::get_supported_chars() const { + String chars; for (int i = 0; i < data.size(); i++) { - RID rid = data[i]->get_rid(); - if (rid != RID()) { - ret.push_back(rid); + String data_chars = data[i]->get_supported_chars(); + for (int j = 0; j < data_chars.length(); j++) { + if (chars.find_char(data_chars[j]) == -1) { + chars += data_chars[j]; + } } } - return ret; + return chars; +} + +Vector<RID> Font::get_rids() const { + for (int i = 0; i < data.size(); i++) { + _ensure_rid(i); + } + return rids; } void Font::update_changes() { @@ -1029,103 +1651,7 @@ Font::Font() { } Font::~Font() { + clear_data(); cache.clear(); cache_wrap.clear(); } - -/*************************************************************************/ - -RES ResourceFormatLoaderFont::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) { - if (r_error) { - *r_error = ERR_FILE_CANT_OPEN; - } - - Ref<FontData> dfont; - dfont.instantiate(); - dfont->load_resource(p_path); - - if (r_error) { - *r_error = OK; - } - - return dfont; -} - -void ResourceFormatLoaderFont::get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const { -#ifndef DISABLE_DEPRECATED - if (p_type == "DynamicFontData") { - p_extensions->push_back("ttf"); - p_extensions->push_back("otf"); - p_extensions->push_back("woff"); - return; - } - if (p_type == "BitmapFont") { // BitmapFont (*.font, *fnt) is handled by ResourceFormatLoaderCompatFont - return; - } -#endif /* DISABLE_DEPRECATED */ - if (p_type == "" || handles_type(p_type)) { - get_recognized_extensions(p_extensions); - } -} - -void ResourceFormatLoaderFont::get_recognized_extensions(List<String> *p_extensions) const { - p_extensions->push_back("ttf"); - p_extensions->push_back("otf"); - p_extensions->push_back("woff"); - p_extensions->push_back("font"); - p_extensions->push_back("fnt"); -} - -bool ResourceFormatLoaderFont::handles_type(const String &p_type) const { - return (p_type == "FontData"); -} - -String ResourceFormatLoaderFont::get_resource_type(const String &p_path) const { - String el = p_path.get_extension().to_lower(); - if (el == "ttf" || el == "otf" || el == "woff" || el == "font" || el == "fnt") { - return "FontData"; - } - return ""; -} - -#ifndef DISABLE_DEPRECATED - -RES ResourceFormatLoaderCompatFont::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) { - if (r_error) { - *r_error = ERR_FILE_CANT_OPEN; - } - - Ref<FontData> dfont; - dfont.instantiate(); - dfont->load_resource(p_path); - - Ref<Font> font; - font.instantiate(); - font->add_data(dfont); - - if (r_error) { - *r_error = OK; - } - - return font; -} - -void ResourceFormatLoaderCompatFont::get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const { - if (p_type == "BitmapFont") { - p_extensions->push_back("font"); - p_extensions->push_back("fnt"); - } -} - -void ResourceFormatLoaderCompatFont::get_recognized_extensions(List<String> *p_extensions) const { -} - -bool ResourceFormatLoaderCompatFont::handles_type(const String &p_type) const { - return (p_type == "Font"); -} - -String ResourceFormatLoaderCompatFont::get_resource_type(const String &p_path) const { - return ""; -} - -#endif /* DISABLE_DEPRECATED */ diff --git a/scene/resources/font.h b/scene/resources/font.h index 200373aa8c..e65fdb0751 100644 --- a/scene/resources/font.h +++ b/scene/resources/font.h @@ -41,17 +41,27 @@ class FontData : public Resource { GDCLASS(FontData, Resource); + RES_BASE_EXTENSION("fontdata"); -public: - enum SpacingType { - SPACING_GLYPH, - SPACING_SPACE, - }; + // Font source data. + const uint8_t *data_ptr = nullptr; + size_t data_size = 0; + PackedByteArray data; -private: - RID rid; - int base_size = 16; - String path; + bool antialiased = true; + bool msdf = false; + int msdf_pixel_range = 16; + int msdf_size = 48; + int fixed_size = 0; + bool force_autohinter = false; + TextServer::Hinting hinting = TextServer::HINTING_LIGHT; + real_t oversampling = 0.f; + + // Cache. + mutable Vector<RID> cache; + + _FORCE_INLINE_ void _clear_cache(); + _FORCE_INLINE_ void _ensure_rid(int p_cache_index) const; protected: static void _bind_methods(); @@ -63,79 +73,132 @@ protected: virtual void reset_state() override; public: - virtual RID get_rid() const override; + // Font source data. + virtual void set_data_ptr(const uint8_t *p_data, size_t p_size); + virtual void set_data(const PackedByteArray &p_data); + virtual PackedByteArray get_data() const; - void load_resource(const String &p_filename, int p_base_size = 16); - void load_memory(const uint8_t *p_data, size_t p_size, const String &p_type, int p_base_size = 16); - void _load_memory(const PackedByteArray &p_data, const String &p_type, int p_base_size = 16); + // Common properties. + virtual void set_antialiased(bool p_antialiased); + virtual bool is_antialiased() const; - void new_bitmap(float p_height, float p_ascent, int p_base_size = 16); + virtual void set_multichannel_signed_distance_field(bool p_msdf); + virtual bool is_multichannel_signed_distance_field() const; - void bitmap_add_texture(const Ref<Texture> &p_texture); - void bitmap_add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance); - void bitmap_add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning); + virtual void set_msdf_pixel_range(int p_msdf_pixel_range); + virtual int get_msdf_pixel_range() const; - void set_data_path(const String &p_path); - String get_data_path() const; + virtual void set_msdf_size(int p_msdf_size); + virtual int get_msdf_size() const; - float get_height(int p_size) const; - float get_ascent(int p_size) const; - float get_descent(int p_size) const; + virtual void set_fixed_size(int p_fixed_size); + virtual int get_fixed_size() const; - Dictionary get_feature_list() const; - Dictionary get_variation_list() const; + virtual void set_force_autohinter(bool p_force_autohinter); + virtual bool is_force_autohinter() const; - void set_variation(const String &p_name, double p_value); - double get_variation(const String &p_name) const; + virtual void set_hinting(TextServer::Hinting p_hinting); + virtual TextServer::Hinting get_hinting() const; - float get_underline_position(int p_size) const; - float get_underline_thickness(int p_size) const; + virtual void set_oversampling(real_t p_oversampling); + virtual real_t get_oversampling() const; - int get_spacing(int p_type) const; - void set_spacing(int p_type, int p_value); + // Cache. + virtual RID find_cache(const Dictionary &p_variation_coordinates) const; - void set_antialiased(bool p_antialiased); - bool get_antialiased() const; + virtual int get_cache_count() const; + virtual void clear_cache(); + virtual void remove_cache(int p_cache_index); - void set_distance_field_hint(bool p_distance_field); - bool get_distance_field_hint() const; + virtual Array get_size_cache_list(int p_cache_index) const; + virtual void clear_size_cache(int p_cache_index); + virtual void remove_size_cache(int p_cache_index, const Vector2i &p_size); - void set_force_autohinter(bool p_enabeld); - bool get_force_autohinter() const; + virtual void set_variation_coordinates(int p_cache_index, const Dictionary &p_variation_coordinates); + virtual Dictionary get_variation_coordinates(int p_cache_index) const; - void set_hinting(TextServer::Hinting p_hinting); - TextServer::Hinting get_hinting() const; + virtual void set_ascent(int p_cache_index, int p_size, real_t p_ascent); + virtual real_t get_ascent(int p_cache_index, int p_size) const; - bool has_char(char32_t p_char) const; - String get_supported_chars() const; + virtual void set_descent(int p_cache_index, int p_size, real_t p_descent); + virtual real_t get_descent(int p_cache_index, int p_size) const; - Vector2 get_glyph_advance(uint32_t p_index, int p_size) const; - Vector2 get_glyph_kerning(uint32_t p_index_a, uint32_t p_index_b, int p_size) const; + virtual void set_underline_position(int p_cache_index, int p_size, real_t p_underline_position); + virtual real_t get_underline_position(int p_cache_index, int p_size) const; - bool has_outline() const; - float get_base_size() const; + virtual void set_underline_thickness(int p_cache_index, int p_size, real_t p_underline_thickness); + virtual real_t get_underline_thickness(int p_cache_index, int p_size) const; - bool is_language_supported(const String &p_language) const; - void set_language_support_override(const String &p_language, bool p_supported); - bool get_language_support_override(const String &p_language) const; - void remove_language_support_override(const String &p_language); - Vector<String> get_language_support_overrides() const; + virtual void set_scale(int p_cache_index, int p_size, real_t p_scale); // Rendering scale for bitmap fonts (e.g. emoji fonts). + virtual real_t get_scale(int p_cache_index, int p_size) const; - bool is_script_supported(const String &p_script) const; - void set_script_support_override(const String &p_script, bool p_supported); - bool get_script_support_override(const String &p_script) const; - void remove_script_support_override(const String &p_script); - Vector<String> get_script_support_overrides() const; + virtual void set_spacing(int p_cache_index, int p_size, TextServer::SpacingType p_spacing, int p_value); + virtual int get_spacing(int p_cache_index, int p_size, TextServer::SpacingType p_spacing) const; - uint32_t get_glyph_index(char32_t p_char, char32_t p_variation_selector = 0x0000) const; + virtual int get_texture_count(int p_cache_index, const Vector2i &p_size) const; + virtual void clear_textures(int p_cache_index, const Vector2i &p_size); + virtual void remove_texture(int p_cache_index, const Vector2i &p_size, int p_texture_index); - Vector2 draw_glyph(RID p_canvas, int p_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color = Color(1, 1, 1)) const; - Vector2 draw_glyph_outline(RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, uint32_t p_index, const Color &p_color = Color(1, 1, 1)) const; + virtual void set_texture_image(int p_cache_index, const Vector2i &p_size, int p_texture_index, const Ref<Image> &p_image); + virtual Ref<Image> get_texture_image(int p_cache_index, const Vector2i &p_size, int p_texture_index) const; - FontData(); - FontData(const String &p_filename, int p_base_size); - FontData(const PackedByteArray &p_data, const String &p_type, int p_base_size); + virtual void set_texture_offsets(int p_cache_index, const Vector2i &p_size, int p_texture_index, const PackedInt32Array &p_offset); + virtual PackedInt32Array get_texture_offsets(int p_cache_index, const Vector2i &p_size, int p_texture_index) const; + + virtual Array get_glyph_list(int p_cache_index, const Vector2i &p_size) const; + virtual void clear_glyphs(int p_cache_index, const Vector2i &p_size); + virtual void remove_glyph(int p_cache_index, const Vector2i &p_size, int32_t p_glyph); + + virtual void set_glyph_advance(int p_cache_index, int p_size, int32_t p_glyph, const Vector2 &p_advance); + virtual Vector2 get_glyph_advance(int p_cache_index, int p_size, int32_t p_glyph) const; + + virtual void set_glyph_offset(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_offset); + virtual Vector2 get_glyph_offset(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const; + + virtual void set_glyph_size(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_gl_size); + virtual Vector2 get_glyph_size(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const; + + virtual void set_glyph_uv_rect(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Rect2 &p_uv_rect); + virtual Rect2 get_glyph_uv_rect(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const; + + virtual void set_glyph_texture_idx(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, int p_texture_idx); + virtual int get_glyph_texture_idx(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const; + virtual Array get_kerning_list(int p_cache_index, int p_size) const; + virtual void clear_kerning_map(int p_cache_index, int p_size); + virtual void remove_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair); + + virtual void set_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning); + virtual Vector2 get_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair) const; + + virtual void render_range(int p_cache_index, const Vector2i &p_size, char32_t p_start, char32_t p_end); + virtual void render_glyph(int p_cache_index, const Vector2i &p_size, int32_t p_index); + + virtual RID get_cache_rid(int p_cache_index) const; + + // Language/script support override. + virtual bool is_language_supported(const String &p_language) const; + virtual void set_language_support_override(const String &p_language, bool p_supported); + virtual bool get_language_support_override(const String &p_language) const; + virtual void remove_language_support_override(const String &p_language); + virtual Vector<String> get_language_support_overrides() const; + + virtual bool is_script_supported(const String &p_script) const; + virtual void set_script_support_override(const String &p_script, bool p_supported); + virtual bool get_script_support_override(const String &p_script) const; + virtual void remove_script_support_override(const String &p_script); + virtual Vector<String> get_script_support_overrides() const; + + // Base font properties. + virtual bool has_char(char32_t p_char) const; + virtual String get_supported_chars() const; + + virtual int32_t get_glyph_index(int p_size, char32_t p_char, char32_t p_variation_selector = 0x0000) const; + + virtual Dictionary get_supported_feature_list() const; + virtual Dictionary get_supported_variation_list() const; + + FontData(); ~FontData(); }; @@ -147,20 +210,22 @@ class TextParagraph; class Font : public Resource { GDCLASS(Font, Resource); -public: - enum SpacingType { - SPACING_TOP, - SPACING_BOTTOM, - }; - -private: - int spacing_top = 0; - int spacing_bottom = 0; - + // Shaped string cache. mutable LRUCache<uint64_t, Ref<TextLine>> cache; mutable LRUCache<uint64_t, Ref<TextParagraph>> cache_wrap; + // Font data cache. Vector<Ref<FontData>> data; + mutable Vector<RID> rids; + + // Font config. + int base_size = 16; + Dictionary variation_coordinates; + int spacing_bottom = 0; + int spacing_top = 0; + + _FORCE_INLINE_ void _data_changed(); + _FORCE_INLINE_ void _ensure_rid(int p_index) const; // Find or create cache record. protected: static void _bind_methods(); @@ -171,41 +236,49 @@ protected: virtual void reset_state() override; - void _data_changed(); - public: Dictionary get_feature_list() const; - // Font data control. - void add_data(const Ref<FontData> &p_data); - void set_data(int p_idx, const Ref<FontData> &p_data); - int get_data_count() const; - Ref<FontData> get_data(int p_idx) const; - void remove_data(int p_idx); + // Font data. + virtual void add_data(const Ref<FontData> &p_data); + virtual void set_data(int p_idx, const Ref<FontData> &p_data); + virtual int get_data_count() const; + virtual Ref<FontData> get_data(int p_idx) const; + virtual RID get_data_rid(int p_idx) const; + virtual void clear_data(); + virtual void remove_data(int p_idx); + + // Font configuration. + virtual void set_base_size(int p_size); + virtual int get_base_size() const; - float get_height(int p_size = -1) const; - float get_ascent(int p_size = -1) const; - float get_descent(int p_size = -1) const; + virtual void set_variation_coordinates(const Dictionary &p_variation_coordinates); + virtual Dictionary get_variation_coordinates() const; - float get_underline_position(int p_size = -1) const; - float get_underline_thickness(int p_size = -1) const; + virtual void set_spacing(TextServer::SpacingType p_spacing, int p_value); + virtual int get_spacing(TextServer::SpacingType p_spacing) const; - int get_spacing(int p_type) const; - void set_spacing(int p_type, int p_value); + // Font metrics. + virtual real_t get_height(int p_size = -1) const; + virtual real_t get_ascent(int p_size = -1) const; + virtual real_t get_descent(int p_size = -1) const; + virtual real_t get_underline_position(int p_size = -1) const; + virtual real_t get_underline_thickness(int p_size = -1) const; // Drawing string. - Size2 get_string_size(const String &p_text, int p_size = -1) const; - Size2 get_multiline_string_size(const String &p_text, float p_width = -1, int p_size = -1, uint8_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND) const; + virtual Size2 get_string_size(const String &p_text, int p_size = -1, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, uint16_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const; + virtual Size2 get_multiline_string_size(const String &p_text, real_t p_width = -1, int p_size = -1, uint16_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND) const; - void draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, float p_width = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint8_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const; - void draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, float p_width = -1, int p_max_lines = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint8_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const; + virtual void draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint16_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const; + virtual void draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_max_lines = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint16_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const; // Helper functions. - bool has_char(char32_t p_char) const; - String get_supported_chars() const; + virtual bool has_char(char32_t p_char) const; + virtual String get_supported_chars() const; - Size2 get_char_size(char32_t p_char, char32_t p_next = 0, int p_size = -1) const; - float draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next = 0, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0)) const; + // Drawing char. + virtual Size2 get_char_size(char32_t p_char, char32_t p_next = 0, int p_size = -1) const; + virtual real_t draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next = 0, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0)) const; Vector<RID> get_rids() const; @@ -215,31 +288,4 @@ public: ~Font(); }; -VARIANT_ENUM_CAST(FontData::SpacingType); -VARIANT_ENUM_CAST(Font::SpacingType); - -/*************************************************************************/ - -class ResourceFormatLoaderFont : public ResourceFormatLoader { -public: - virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE); - virtual void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const; - virtual void get_recognized_extensions(List<String> *p_extensions) const; - virtual bool handles_type(const String &p_type) const; - virtual String get_resource_type(const String &p_path) const; -}; - -#ifndef DISABLE_DEPRECATED - -class ResourceFormatLoaderCompatFont : public ResourceFormatLoader { -public: - virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE); - virtual void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const; - virtual void get_recognized_extensions(List<String> *p_extensions) const; - virtual bool handles_type(const String &p_type) const; - virtual String get_resource_type(const String &p_path) const; -}; - -#endif /* DISABLE_DEPRECATED */ - #endif /* FONT_H */ diff --git a/scene/resources/immediate_mesh.cpp b/scene/resources/immediate_mesh.cpp index 05d1a7bf94..fe7124de9e 100644 --- a/scene/resources/immediate_mesh.cpp +++ b/scene/resources/immediate_mesh.cpp @@ -335,9 +335,6 @@ int ImmediateMesh::surface_get_array_len(int p_idx) const { int ImmediateMesh::surface_get_array_index_len(int p_idx) const { return 0; } -bool ImmediateMesh::surface_is_softbody_friendly(int p_idx) const { - return false; -} Array ImmediateMesh::surface_get_arrays(int p_surface) const { ERR_FAIL_INDEX_V(p_surface, int(surfaces.size()), Array()); return RS::get_singleton()->mesh_surface_get_arrays(mesh, p_surface); diff --git a/scene/resources/immediate_mesh.h b/scene/resources/immediate_mesh.h index 7010d40719..6673ee6f3d 100644 --- a/scene/resources/immediate_mesh.h +++ b/scene/resources/immediate_mesh.h @@ -94,7 +94,6 @@ public: virtual int get_surface_count() const override; virtual int surface_get_array_len(int p_idx) const override; virtual int surface_get_array_index_len(int p_idx) const override; - virtual bool surface_is_softbody_friendly(int p_idx) const override; virtual Array surface_get_arrays(int p_surface) const override; virtual Array surface_get_blend_shape_arrays(int p_surface) const override; virtual Dictionary surface_get_lods(int p_surface) const override; diff --git a/scene/resources/importer_mesh.cpp b/scene/resources/importer_mesh.cpp new file mode 100644 index 0000000000..2572c5de33 --- /dev/null +++ b/scene/resources/importer_mesh.cpp @@ -0,0 +1,1247 @@ +/*************************************************************************/ +/* importer_mesh.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "importer_mesh.h" + +#include "core/math/random_pcg.h" +#include "core/math/static_raycaster.h" +#include "scene/resources/surface_tool.h" + +#include <cstdint> + +void ImporterMesh::Surface::split_normals(const LocalVector<int> &p_indices, const LocalVector<Vector3> &p_normals) { + _split_normals(arrays, p_indices, p_normals); + + for (BlendShape &blend_shape : blend_shape_data) { + _split_normals(blend_shape.arrays, p_indices, p_normals); + } +} + +void ImporterMesh::Surface::_split_normals(Array &r_arrays, const LocalVector<int> &p_indices, const LocalVector<Vector3> &p_normals) { + ERR_FAIL_COND(r_arrays.size() != RS::ARRAY_MAX); + + const PackedVector3Array &vertices = r_arrays[RS::ARRAY_VERTEX]; + int current_vertex_count = vertices.size(); + int new_vertex_count = p_indices.size(); + int final_vertex_count = current_vertex_count + new_vertex_count; + const int *indices_ptr = p_indices.ptr(); + + for (int i = 0; i < r_arrays.size(); i++) { + if (i == RS::ARRAY_INDEX) { + continue; + } + + if (r_arrays[i].get_type() == Variant::NIL) { + continue; + } + + switch (r_arrays[i].get_type()) { + case Variant::PACKED_VECTOR3_ARRAY: { + PackedVector3Array data = r_arrays[i]; + data.resize(final_vertex_count); + Vector3 *data_ptr = data.ptrw(); + if (i == RS::ARRAY_NORMAL) { + const Vector3 *normals_ptr = p_normals.ptr(); + memcpy(&data_ptr[current_vertex_count], normals_ptr, sizeof(Vector3) * new_vertex_count); + } else { + for (int j = 0; j < new_vertex_count; j++) { + data_ptr[current_vertex_count + j] = data_ptr[indices_ptr[j]]; + } + } + r_arrays[i] = data; + } break; + case Variant::PACKED_VECTOR2_ARRAY: { + PackedVector2Array data = r_arrays[i]; + data.resize(final_vertex_count); + Vector2 *data_ptr = data.ptrw(); + for (int j = 0; j < new_vertex_count; j++) { + data_ptr[current_vertex_count + j] = data_ptr[indices_ptr[j]]; + } + r_arrays[i] = data; + } break; + case Variant::PACKED_FLOAT32_ARRAY: { + PackedFloat32Array data = r_arrays[i]; + int elements = data.size() / current_vertex_count; + data.resize(final_vertex_count * elements); + float *data_ptr = data.ptrw(); + for (int j = 0; j < new_vertex_count; j++) { + memcpy(&data_ptr[(current_vertex_count + j) * elements], &data_ptr[indices_ptr[j] * elements], sizeof(float) * elements); + } + r_arrays[i] = data; + } break; + case Variant::PACKED_INT32_ARRAY: { + PackedInt32Array data = r_arrays[i]; + int elements = data.size() / current_vertex_count; + data.resize(final_vertex_count * elements); + int32_t *data_ptr = data.ptrw(); + for (int j = 0; j < new_vertex_count; j++) { + memcpy(&data_ptr[(current_vertex_count + j) * elements], &data_ptr[indices_ptr[j] * elements], sizeof(int32_t) * elements); + } + r_arrays[i] = data; + } break; + case Variant::PACKED_BYTE_ARRAY: { + PackedByteArray data = r_arrays[i]; + int elements = data.size() / current_vertex_count; + data.resize(final_vertex_count * elements); + uint8_t *data_ptr = data.ptrw(); + for (int j = 0; j < new_vertex_count; j++) { + memcpy(&data_ptr[(current_vertex_count + j) * elements], &data_ptr[indices_ptr[j] * elements], sizeof(uint8_t) * elements); + } + r_arrays[i] = data; + } break; + case Variant::PACKED_COLOR_ARRAY: { + PackedColorArray data = r_arrays[i]; + data.resize(final_vertex_count); + Color *data_ptr = data.ptrw(); + for (int j = 0; j < new_vertex_count; j++) { + data_ptr[current_vertex_count + j] = data_ptr[indices_ptr[j]]; + } + r_arrays[i] = data; + } break; + default: { + ERR_FAIL_MSG("Unhandled array type."); + } break; + } + } +} + +void ImporterMesh::add_blend_shape(const String &p_name) { + ERR_FAIL_COND(surfaces.size() > 0); + blend_shapes.push_back(p_name); +} + +int ImporterMesh::get_blend_shape_count() const { + return blend_shapes.size(); +} + +String ImporterMesh::get_blend_shape_name(int p_blend_shape) const { + ERR_FAIL_INDEX_V(p_blend_shape, blend_shapes.size(), String()); + return blend_shapes[p_blend_shape]; +} + +void ImporterMesh::set_blend_shape_mode(Mesh::BlendShapeMode p_blend_shape_mode) { + blend_shape_mode = p_blend_shape_mode; +} + +Mesh::BlendShapeMode ImporterMesh::get_blend_shape_mode() const { + return blend_shape_mode; +} + +void ImporterMesh::add_surface(Mesh::PrimitiveType p_primitive, const Array &p_arrays, const Array &p_blend_shapes, const Dictionary &p_lods, const Ref<Material> &p_material, const String &p_name, const uint32_t p_flags) { + ERR_FAIL_COND(p_blend_shapes.size() != blend_shapes.size()); + ERR_FAIL_COND(p_arrays.size() != Mesh::ARRAY_MAX); + Surface s; + s.primitive = p_primitive; + s.arrays = p_arrays; + s.name = p_name; + s.flags = p_flags; + + Vector<Vector3> vertex_array = p_arrays[Mesh::ARRAY_VERTEX]; + int vertex_count = vertex_array.size(); + ERR_FAIL_COND(vertex_count == 0); + + for (int i = 0; i < blend_shapes.size(); i++) { + Array bsdata = p_blend_shapes[i]; + ERR_FAIL_COND(bsdata.size() != Mesh::ARRAY_MAX); + Vector<Vector3> vertex_data = bsdata[Mesh::ARRAY_VERTEX]; + ERR_FAIL_COND(vertex_data.size() != vertex_count); + Surface::BlendShape bs; + bs.arrays = bsdata; + s.blend_shape_data.push_back(bs); + } + + List<Variant> lods; + p_lods.get_key_list(&lods); + for (const Variant &E : lods) { + ERR_CONTINUE(!E.is_num()); + Surface::LOD lod; + lod.distance = E; + lod.indices = p_lods[E]; + ERR_CONTINUE(lod.indices.size() == 0); + s.lods.push_back(lod); + } + + s.material = p_material; + + surfaces.push_back(s); + mesh.unref(); +} + +int ImporterMesh::get_surface_count() const { + return surfaces.size(); +} + +Mesh::PrimitiveType ImporterMesh::get_surface_primitive_type(int p_surface) { + ERR_FAIL_INDEX_V(p_surface, surfaces.size(), Mesh::PRIMITIVE_MAX); + return surfaces[p_surface].primitive; +} +Array ImporterMesh::get_surface_arrays(int p_surface) const { + ERR_FAIL_INDEX_V(p_surface, surfaces.size(), Array()); + return surfaces[p_surface].arrays; +} +String ImporterMesh::get_surface_name(int p_surface) const { + ERR_FAIL_INDEX_V(p_surface, surfaces.size(), String()); + return surfaces[p_surface].name; +} +void ImporterMesh::set_surface_name(int p_surface, const String &p_name) { + ERR_FAIL_INDEX(p_surface, surfaces.size()); + surfaces.write[p_surface].name = p_name; + mesh.unref(); +} + +Array ImporterMesh::get_surface_blend_shape_arrays(int p_surface, int p_blend_shape) const { + ERR_FAIL_INDEX_V(p_surface, surfaces.size(), Array()); + ERR_FAIL_INDEX_V(p_blend_shape, surfaces[p_surface].blend_shape_data.size(), Array()); + return surfaces[p_surface].blend_shape_data[p_blend_shape].arrays; +} +int ImporterMesh::get_surface_lod_count(int p_surface) const { + ERR_FAIL_INDEX_V(p_surface, surfaces.size(), 0); + return surfaces[p_surface].lods.size(); +} +Vector<int> ImporterMesh::get_surface_lod_indices(int p_surface, int p_lod) const { + ERR_FAIL_INDEX_V(p_surface, surfaces.size(), Vector<int>()); + ERR_FAIL_INDEX_V(p_lod, surfaces[p_surface].lods.size(), Vector<int>()); + + return surfaces[p_surface].lods[p_lod].indices; +} + +float ImporterMesh::get_surface_lod_size(int p_surface, int p_lod) const { + ERR_FAIL_INDEX_V(p_surface, surfaces.size(), 0); + ERR_FAIL_INDEX_V(p_lod, surfaces[p_surface].lods.size(), 0); + return surfaces[p_surface].lods[p_lod].distance; +} + +uint32_t ImporterMesh::get_surface_format(int p_surface) const { + ERR_FAIL_INDEX_V(p_surface, surfaces.size(), 0); + return surfaces[p_surface].flags; +} + +Ref<Material> ImporterMesh::get_surface_material(int p_surface) const { + ERR_FAIL_INDEX_V(p_surface, surfaces.size(), Ref<Material>()); + return surfaces[p_surface].material; +} + +void ImporterMesh::set_surface_material(int p_surface, const Ref<Material> &p_material) { + ERR_FAIL_INDEX(p_surface, surfaces.size()); + surfaces.write[p_surface].material = p_material; + mesh.unref(); +} + +void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_split_angle) { + if (!SurfaceTool::simplify_scale_func) { + return; + } + if (!SurfaceTool::simplify_with_attrib_func) { + return; + } + if (!SurfaceTool::optimize_vertex_cache_func) { + return; + } + + for (int i = 0; i < surfaces.size(); i++) { + if (surfaces[i].primitive != Mesh::PRIMITIVE_TRIANGLES) { + continue; + } + + surfaces.write[i].lods.clear(); + Vector<Vector3> vertices = surfaces[i].arrays[RS::ARRAY_VERTEX]; + PackedInt32Array indices = surfaces[i].arrays[RS::ARRAY_INDEX]; + Vector<Vector3> normals = surfaces[i].arrays[RS::ARRAY_NORMAL]; + Vector<Vector2> uvs = surfaces[i].arrays[RS::ARRAY_TEX_UV]; + + unsigned int index_count = indices.size(); + unsigned int vertex_count = vertices.size(); + + if (index_count == 0) { + continue; //no lods if no indices + } + + const Vector3 *vertices_ptr = vertices.ptr(); + const int *indices_ptr = indices.ptr(); + + if (normals.is_empty()) { + normals.resize(vertices.size()); + Vector3 *n_ptr = normals.ptrw(); + for (unsigned int j = 0; j < index_count; j += 3) { + const Vector3 &v0 = vertices_ptr[indices_ptr[j + 0]]; + const Vector3 &v1 = vertices_ptr[indices_ptr[j + 1]]; + const Vector3 &v2 = vertices_ptr[indices_ptr[j + 2]]; + Vector3 n = vec3_cross(v0 - v2, v0 - v1).normalized(); + n_ptr[j + 0] = n; + n_ptr[j + 1] = n; + n_ptr[j + 2] = n; + } + } + + float normal_merge_threshold = Math::cos(Math::deg2rad(p_normal_merge_angle)); + float normal_pre_split_threshold = Math::cos(Math::deg2rad(MIN(180.0f, p_normal_split_angle * 2.0f))); + float normal_split_threshold = Math::cos(Math::deg2rad(p_normal_split_angle)); + const Vector3 *normals_ptr = normals.ptr(); + + Map<Vector3, LocalVector<Pair<int, int>>> unique_vertices; + + LocalVector<int> vertex_remap; + LocalVector<int> vertex_inverse_remap; + LocalVector<Vector3> merged_vertices; + LocalVector<Vector3> merged_normals; + LocalVector<int> merged_normals_counts; + const Vector2 *uvs_ptr = uvs.ptr(); + + for (unsigned int j = 0; j < vertex_count; j++) { + const Vector3 &v = vertices_ptr[j]; + const Vector3 &n = normals_ptr[j]; + + Map<Vector3, LocalVector<Pair<int, int>>>::Element *E = unique_vertices.find(v); + + if (E) { + const LocalVector<Pair<int, int>> &close_verts = E->get(); + + bool found = false; + for (unsigned int k = 0; k < close_verts.size(); k++) { + const Pair<int, int> &idx = close_verts[k]; + + // TODO check more attributes? + if ((!uvs_ptr || uvs_ptr[j].distance_squared_to(uvs_ptr[idx.second]) < CMP_EPSILON2) && normals[idx.second].dot(n) > normal_merge_threshold) { + vertex_remap.push_back(idx.first); + merged_normals[idx.first] += normals[idx.second]; + merged_normals_counts[idx.first]++; + found = true; + break; + } + } + + if (!found) { + int vcount = merged_vertices.size(); + unique_vertices[v].push_back(Pair<int, int>(vcount, j)); + vertex_inverse_remap.push_back(j); + merged_vertices.push_back(v); + vertex_remap.push_back(vcount); + merged_normals.push_back(normals_ptr[j]); + merged_normals_counts.push_back(1); + } + } else { + int vcount = merged_vertices.size(); + unique_vertices[v] = LocalVector<Pair<int, int>>(); + unique_vertices[v].push_back(Pair<int, int>(vcount, j)); + vertex_inverse_remap.push_back(j); + merged_vertices.push_back(v); + vertex_remap.push_back(vcount); + merged_normals.push_back(normals_ptr[j]); + merged_normals_counts.push_back(1); + } + } + + LocalVector<int> merged_indices; + merged_indices.resize(index_count); + for (unsigned int j = 0; j < index_count; j++) { + merged_indices[j] = vertex_remap[indices[j]]; + } + + unsigned int merged_vertex_count = merged_vertices.size(); + const Vector3 *merged_vertices_ptr = merged_vertices.ptr(); + const int32_t *merged_indices_ptr = merged_indices.ptr(); + + { + const int *counts_ptr = merged_normals_counts.ptr(); + Vector3 *merged_normals_ptrw = merged_normals.ptr(); + for (unsigned int j = 0; j < merged_vertex_count; j++) { + merged_normals_ptrw[j] /= counts_ptr[j]; + } + } + + LocalVector<float> normal_weights; + normal_weights.resize(merged_vertex_count); + for (unsigned int j = 0; j < merged_vertex_count; j++) { + normal_weights[j] = 2.0; // Give some weight to normal preservation, may be worth exposing as an import setting + } + + const float max_mesh_error = FLT_MAX; // We don't want to limit by error, just by index target + float scale = SurfaceTool::simplify_scale_func((const float *)merged_vertices_ptr, merged_vertex_count, sizeof(Vector3)); + float mesh_error = 0.0f; + + unsigned int index_target = 12; // Start with the smallest target, 4 triangles + unsigned int last_index_count = 0; + + int split_vertex_count = vertex_count; + LocalVector<Vector3> split_vertex_normals; + LocalVector<int> split_vertex_indices; + split_vertex_normals.reserve(index_count / 3); + split_vertex_indices.reserve(index_count / 3); + + RandomPCG pcg; + pcg.seed(123456789); // Keep seed constant across imports + + Ref<StaticRaycaster> raycaster = StaticRaycaster::create(); + if (raycaster.is_valid()) { + raycaster->add_mesh(vertices, indices, 0); + raycaster->commit(); + } + + while (index_target < index_count) { + PackedInt32Array new_indices; + new_indices.resize(index_count); + + size_t new_index_count = SurfaceTool::simplify_with_attrib_func((unsigned int *)new_indices.ptrw(), (const uint32_t *)merged_indices_ptr, index_count, (const float *)merged_vertices_ptr, merged_vertex_count, sizeof(Vector3), index_target, max_mesh_error, &mesh_error, (float *)merged_normals.ptr(), normal_weights.ptr(), 3); + + if (new_index_count < last_index_count * 1.5f) { + index_target = index_target * 1.5f; + continue; + } + + if (new_index_count <= 0 || (new_index_count >= (index_count * 0.75f))) { + break; + } + + new_indices.resize(new_index_count); + + LocalVector<LocalVector<int>> vertex_corners; + vertex_corners.resize(vertex_count); + { + int *ptrw = new_indices.ptrw(); + for (unsigned int j = 0; j < new_index_count; j++) { + const int &remapped = vertex_inverse_remap[ptrw[j]]; + vertex_corners[remapped].push_back(j); + ptrw[j] = remapped; + } + } + + if (raycaster.is_valid()) { + float error_factor = 1.0f / (scale * MAX(mesh_error, 0.15)); + const float ray_bias = 0.05; + float ray_length = ray_bias + mesh_error * scale * 3.0f; + + Vector<StaticRaycaster::Ray> rays; + LocalVector<Vector2> ray_uvs; + + int32_t *new_indices_ptr = new_indices.ptrw(); + + int current_ray_count = 0; + for (unsigned int j = 0; j < new_index_count; j += 3) { + const Vector3 &v0 = vertices_ptr[new_indices_ptr[j + 0]]; + const Vector3 &v1 = vertices_ptr[new_indices_ptr[j + 1]]; + const Vector3 &v2 = vertices_ptr[new_indices_ptr[j + 2]]; + Vector3 face_normal = vec3_cross(v0 - v2, v0 - v1); + float face_area = face_normal.length(); // Actually twice the face area, since it's the same error_factor on all faces, we don't care + + Vector3 dir = face_normal / face_area; + int ray_count = CLAMP(5.0 * face_area * error_factor, 16, 64); + + rays.resize(current_ray_count + ray_count); + StaticRaycaster::Ray *rays_ptr = rays.ptrw(); + + ray_uvs.resize(current_ray_count + ray_count); + Vector2 *ray_uvs_ptr = ray_uvs.ptr(); + + for (int k = 0; k < ray_count; k++) { + float u = pcg.randf(); + float v = pcg.randf(); + + if (u + v >= 1.0f) { + u = 1.0f - u; + v = 1.0f - v; + } + + u = 0.9f * u + 0.05f / 3.0f; // Give barycentric coordinates some padding, we don't want to sample right on the edge + v = 0.9f * v + 0.05f / 3.0f; // v = (v - one_third) * 0.95f + one_third; + float w = 1.0f - u - v; + + Vector3 org = v0 * w + v1 * u + v2 * v; + org -= dir * ray_bias; + rays_ptr[current_ray_count + k] = StaticRaycaster::Ray(org, dir, 0.0f, ray_length); + rays_ptr[current_ray_count + k].id = j / 3; + ray_uvs_ptr[current_ray_count + k] = Vector2(u, v); + } + + current_ray_count += ray_count; + } + + raycaster->intersect(rays); + + LocalVector<Vector3> ray_normals; + LocalVector<float> ray_normal_weights; + + ray_normals.resize(new_index_count); + ray_normal_weights.resize(new_index_count); + + for (unsigned int j = 0; j < new_index_count; j++) { + ray_normal_weights[j] = 0.0f; + } + + const StaticRaycaster::Ray *rp = rays.ptr(); + for (int j = 0; j < rays.size(); j++) { + if (rp[j].geomID != 0) { // Ray missed + continue; + } + + if (rp[j].normal.normalized().dot(rp[j].dir) > 0.0f) { // Hit a back face. + continue; + } + + const float &u = rp[j].u; + const float &v = rp[j].v; + const float w = 1.0f - u - v; + + const unsigned int &hit_tri_id = rp[j].primID; + const unsigned int &orig_tri_id = rp[j].id; + + const Vector3 &n0 = normals_ptr[indices_ptr[hit_tri_id * 3 + 0]]; + const Vector3 &n1 = normals_ptr[indices_ptr[hit_tri_id * 3 + 1]]; + const Vector3 &n2 = normals_ptr[indices_ptr[hit_tri_id * 3 + 2]]; + Vector3 normal = n0 * w + n1 * u + n2 * v; + + Vector2 orig_uv = ray_uvs[j]; + float orig_bary[3] = { 1.0f - orig_uv.x - orig_uv.y, orig_uv.x, orig_uv.y }; + for (int k = 0; k < 3; k++) { + int idx = orig_tri_id * 3 + k; + float weight = orig_bary[k]; + ray_normals[idx] += normal * weight; + ray_normal_weights[idx] += weight; + } + } + + for (unsigned int j = 0; j < new_index_count; j++) { + if (ray_normal_weights[j] < 1.0f) { // Not enough data, the new normal would be just a bad guess + ray_normals[j] = Vector3(); + } else { + ray_normals[j] /= ray_normal_weights[j]; + } + } + + LocalVector<LocalVector<int>> normal_group_indices; + LocalVector<Vector3> normal_group_averages; + normal_group_indices.reserve(24); + normal_group_averages.reserve(24); + + for (unsigned int j = 0; j < vertex_count; j++) { + const LocalVector<int> &corners = vertex_corners[j]; + const Vector3 &vertex_normal = normals_ptr[j]; + + for (unsigned int k = 0; k < corners.size(); k++) { + const int &corner_idx = corners[k]; + const Vector3 &ray_normal = ray_normals[corner_idx]; + + if (ray_normal.length_squared() < CMP_EPSILON2) { + continue; + } + + bool found = false; + for (unsigned int l = 0; l < normal_group_indices.size(); l++) { + LocalVector<int> &group_indices = normal_group_indices[l]; + Vector3 n = normal_group_averages[l] / group_indices.size(); + if (n.dot(ray_normal) > normal_pre_split_threshold) { + found = true; + group_indices.push_back(corner_idx); + normal_group_averages[l] += ray_normal; + break; + } + } + + if (!found) { + LocalVector<int> new_group; + new_group.push_back(corner_idx); + normal_group_indices.push_back(new_group); + normal_group_averages.push_back(ray_normal); + } + } + + for (unsigned int k = 0; k < normal_group_indices.size(); k++) { + LocalVector<int> &group_indices = normal_group_indices[k]; + Vector3 n = normal_group_averages[k] / group_indices.size(); + + if (vertex_normal.dot(n) < normal_split_threshold) { + split_vertex_indices.push_back(j); + split_vertex_normals.push_back(n); + int new_idx = split_vertex_count++; + for (unsigned int l = 0; l < group_indices.size(); l++) { + new_indices_ptr[group_indices[l]] = new_idx; + } + } + } + + normal_group_indices.clear(); + normal_group_averages.clear(); + } + } + + Surface::LOD lod; + lod.distance = MAX(mesh_error * scale, CMP_EPSILON2); + lod.indices = new_indices; + surfaces.write[i].lods.push_back(lod); + index_target = MAX(new_index_count, index_target) * 2; + last_index_count = new_index_count; + + if (mesh_error == 0.0f) { + break; + } + } + + surfaces.write[i].split_normals(split_vertex_indices, split_vertex_normals); + surfaces.write[i].lods.sort_custom<Surface::LODComparator>(); + + for (int j = 0; j < surfaces.write[i].lods.size(); j++) { + Surface::LOD &lod = surfaces.write[i].lods.write[j]; + unsigned int *lod_indices_ptr = (unsigned int *)lod.indices.ptrw(); + SurfaceTool::optimize_vertex_cache_func(lod_indices_ptr, lod_indices_ptr, lod.indices.size(), split_vertex_count); + } + } +} + +bool ImporterMesh::has_mesh() const { + return mesh.is_valid(); +} + +Ref<ArrayMesh> ImporterMesh::get_mesh(const Ref<ArrayMesh> &p_base) { + ERR_FAIL_COND_V(surfaces.size() == 0, Ref<ArrayMesh>()); + + if (mesh.is_null()) { + if (p_base.is_valid()) { + mesh = p_base; + } + if (mesh.is_null()) { + mesh.instantiate(); + } + mesh->set_name(get_name()); + if (has_meta("import_id")) { + mesh->set_meta("import_id", get_meta("import_id")); + } + for (int i = 0; i < blend_shapes.size(); i++) { + mesh->add_blend_shape(blend_shapes[i]); + } + mesh->set_blend_shape_mode(blend_shape_mode); + for (int i = 0; i < surfaces.size(); i++) { + Array bs_data; + if (surfaces[i].blend_shape_data.size()) { + for (int j = 0; j < surfaces[i].blend_shape_data.size(); j++) { + bs_data.push_back(surfaces[i].blend_shape_data[j].arrays); + } + } + Dictionary lods; + if (surfaces[i].lods.size()) { + for (int j = 0; j < surfaces[i].lods.size(); j++) { + lods[surfaces[i].lods[j].distance] = surfaces[i].lods[j].indices; + } + } + + mesh->add_surface_from_arrays(surfaces[i].primitive, surfaces[i].arrays, bs_data, lods, surfaces[i].flags); + if (surfaces[i].material.is_valid()) { + mesh->surface_set_material(mesh->get_surface_count() - 1, surfaces[i].material); + } + if (surfaces[i].name != String()) { + mesh->surface_set_name(mesh->get_surface_count() - 1, surfaces[i].name); + } + } + + mesh->set_lightmap_size_hint(lightmap_size_hint); + + if (shadow_mesh.is_valid()) { + Ref<ArrayMesh> shadow = shadow_mesh->get_mesh(); + mesh->set_shadow_mesh(shadow); + } + } + + return mesh; +} + +void ImporterMesh::clear() { + surfaces.clear(); + blend_shapes.clear(); + mesh.unref(); +} + +void ImporterMesh::create_shadow_mesh() { + if (shadow_mesh.is_valid()) { + shadow_mesh.unref(); + } + + //no shadow mesh for blendshapes + if (blend_shapes.size() > 0) { + return; + } + //no shadow mesh for skeletons + for (int i = 0; i < surfaces.size(); i++) { + if (surfaces[i].arrays[RS::ARRAY_BONES].get_type() != Variant::NIL) { + return; + } + if (surfaces[i].arrays[RS::ARRAY_WEIGHTS].get_type() != Variant::NIL) { + return; + } + } + + shadow_mesh.instantiate(); + + for (int i = 0; i < surfaces.size(); i++) { + LocalVector<int> vertex_remap; + Vector<Vector3> new_vertices; + Vector<Vector3> vertices = surfaces[i].arrays[RS::ARRAY_VERTEX]; + int vertex_count = vertices.size(); + { + Map<Vector3, int> unique_vertices; + const Vector3 *vptr = vertices.ptr(); + for (int j = 0; j < vertex_count; j++) { + const Vector3 &v = vptr[j]; + + Map<Vector3, int>::Element *E = unique_vertices.find(v); + + if (E) { + vertex_remap.push_back(E->get()); + } else { + int vcount = unique_vertices.size(); + unique_vertices[v] = vcount; + vertex_remap.push_back(vcount); + new_vertices.push_back(v); + } + } + } + + Array new_surface; + new_surface.resize(RS::ARRAY_MAX); + Dictionary lods; + + // print_line("original vertex count: " + itos(vertices.size()) + " new vertex count: " + itos(new_vertices.size())); + + new_surface[RS::ARRAY_VERTEX] = new_vertices; + + Vector<int> indices = surfaces[i].arrays[RS::ARRAY_INDEX]; + if (indices.size()) { + int index_count = indices.size(); + const int *index_rptr = indices.ptr(); + Vector<int> new_indices; + new_indices.resize(indices.size()); + int *index_wptr = new_indices.ptrw(); + + for (int j = 0; j < index_count; j++) { + int index = index_rptr[j]; + ERR_FAIL_INDEX(index, vertex_count); + index_wptr[j] = vertex_remap[index]; + } + + new_surface[RS::ARRAY_INDEX] = new_indices; + + // Make sure the same LODs as the full version are used. + // This makes it more coherent between rendered model and its shadows. + for (int j = 0; j < surfaces[i].lods.size(); j++) { + indices = surfaces[i].lods[j].indices; + + index_count = indices.size(); + index_rptr = indices.ptr(); + new_indices.resize(indices.size()); + index_wptr = new_indices.ptrw(); + + for (int k = 0; k < index_count; k++) { + int index = index_rptr[k]; + ERR_FAIL_INDEX(index, vertex_count); + index_wptr[k] = vertex_remap[index]; + } + + lods[surfaces[i].lods[j].distance] = new_indices; + } + } + + shadow_mesh->add_surface(surfaces[i].primitive, new_surface, Array(), lods, Ref<Material>(), surfaces[i].name, surfaces[i].flags); + } +} + +Ref<ImporterMesh> ImporterMesh::get_shadow_mesh() const { + return shadow_mesh; +} + +void ImporterMesh::_set_data(const Dictionary &p_data) { + clear(); + if (p_data.has("blend_shape_names")) { + blend_shapes = p_data["blend_shape_names"]; + } + if (p_data.has("surfaces")) { + Array surface_arr = p_data["surfaces"]; + for (int i = 0; i < surface_arr.size(); i++) { + Dictionary s = surface_arr[i]; + ERR_CONTINUE(!s.has("primitive")); + ERR_CONTINUE(!s.has("arrays")); + Mesh::PrimitiveType prim = Mesh::PrimitiveType(int(s["primitive"])); + ERR_CONTINUE(prim >= Mesh::PRIMITIVE_MAX); + Array arr = s["arrays"]; + Dictionary lods; + String name; + if (s.has("name")) { + name = s["name"]; + } + if (s.has("lods")) { + lods = s["lods"]; + } + Array b_shapes; + if (s.has("b_shapes")) { + b_shapes = s["b_shapes"]; + } + Ref<Material> material; + if (s.has("material")) { + material = s["material"]; + } + uint32_t flags = 0; + if (s.has("flags")) { + flags = s["flags"]; + } + add_surface(prim, arr, b_shapes, lods, material, name, flags); + } + } +} +Dictionary ImporterMesh::_get_data() const { + Dictionary data; + if (blend_shapes.size()) { + data["blend_shape_names"] = blend_shapes; + } + Array surface_arr; + for (int i = 0; i < surfaces.size(); i++) { + Dictionary d; + d["primitive"] = surfaces[i].primitive; + d["arrays"] = surfaces[i].arrays; + if (surfaces[i].blend_shape_data.size()) { + Array bs_data; + for (int j = 0; j < surfaces[i].blend_shape_data.size(); j++) { + bs_data.push_back(surfaces[i].blend_shape_data[j].arrays); + } + d["blend_shapes"] = bs_data; + } + if (surfaces[i].lods.size()) { + Dictionary lods; + for (int j = 0; j < surfaces[i].lods.size(); j++) { + lods[surfaces[i].lods[j].distance] = surfaces[i].lods[j].indices; + } + d["lods"] = lods; + } + + if (surfaces[i].material.is_valid()) { + d["material"] = surfaces[i].material; + } + + if (surfaces[i].name != String()) { + d["name"] = surfaces[i].name; + } + + if (surfaces[i].flags != 0) { + d["flags"] = surfaces[i].flags; + } + + surface_arr.push_back(d); + } + data["surfaces"] = surface_arr; + return data; +} + +Vector<Face3> ImporterMesh::get_faces() const { + Vector<Face3> faces; + for (int i = 0; i < surfaces.size(); i++) { + if (surfaces[i].primitive == Mesh::PRIMITIVE_TRIANGLES) { + Vector<Vector3> vertices = surfaces[i].arrays[Mesh::ARRAY_VERTEX]; + Vector<int> indices = surfaces[i].arrays[Mesh::ARRAY_INDEX]; + if (indices.size()) { + for (int j = 0; j < indices.size(); j += 3) { + Face3 f; + f.vertex[0] = vertices[indices[j + 0]]; + f.vertex[1] = vertices[indices[j + 1]]; + f.vertex[2] = vertices[indices[j + 2]]; + faces.push_back(f); + } + } else { + for (int j = 0; j < vertices.size(); j += 3) { + Face3 f; + f.vertex[0] = vertices[j + 0]; + f.vertex[1] = vertices[j + 1]; + f.vertex[2] = vertices[j + 2]; + faces.push_back(f); + } + } + } + } + + return faces; +} + +Vector<Ref<Shape3D>> ImporterMesh::convex_decompose(const Mesh::ConvexDecompositionSettings &p_settings) const { + ERR_FAIL_COND_V(!Mesh::convex_decomposition_function, Vector<Ref<Shape3D>>()); + + const Vector<Face3> faces = get_faces(); + int face_count = faces.size(); + + Vector<Vector3> vertices; + uint32_t vertex_count = 0; + vertices.resize(face_count * 3); + Vector<uint32_t> indices; + indices.resize(face_count * 3); + { + Map<Vector3, uint32_t> vertex_map; + Vector3 *vertex_w = vertices.ptrw(); + uint32_t *index_w = indices.ptrw(); + for (int i = 0; i < face_count; i++) { + for (int j = 0; j < 3; j++) { + const Vector3 &vertex = faces[i].vertex[j]; + Map<Vector3, uint32_t>::Element *found_vertex = vertex_map.find(vertex); + uint32_t index; + if (found_vertex) { + index = found_vertex->get(); + } else { + index = ++vertex_count; + vertex_map[vertex] = index; + vertex_w[index] = vertex; + } + index_w[i * 3 + j] = index; + } + } + } + vertices.resize(vertex_count); + + Vector<Vector<Vector3>> decomposed = Mesh::convex_decomposition_function((real_t *)vertices.ptr(), vertex_count, indices.ptr(), face_count, p_settings, nullptr); + + Vector<Ref<Shape3D>> ret; + + for (int i = 0; i < decomposed.size(); i++) { + Ref<ConvexPolygonShape3D> shape; + shape.instantiate(); + shape->set_points(decomposed[i]); + ret.push_back(shape); + } + + return ret; +} + +Ref<Shape3D> ImporterMesh::create_trimesh_shape() const { + Vector<Face3> faces = get_faces(); + if (faces.size() == 0) { + return Ref<Shape3D>(); + } + + Vector<Vector3> face_points; + face_points.resize(faces.size() * 3); + + for (int i = 0; i < face_points.size(); i += 3) { + Face3 f = faces.get(i / 3); + face_points.set(i, f.vertex[0]); + face_points.set(i + 1, f.vertex[1]); + face_points.set(i + 2, f.vertex[2]); + } + + Ref<ConcavePolygonShape3D> shape = memnew(ConcavePolygonShape3D); + shape->set_faces(face_points); + return shape; +} + +Ref<NavigationMesh> ImporterMesh::create_navigation_mesh() { + Vector<Face3> faces = get_faces(); + if (faces.size() == 0) { + return Ref<NavigationMesh>(); + } + + Map<Vector3, int> unique_vertices; + LocalVector<int> face_indices; + + for (int i = 0; i < faces.size(); i++) { + for (int j = 0; j < 3; j++) { + Vector3 v = faces[i].vertex[j]; + int idx; + if (unique_vertices.has(v)) { + idx = unique_vertices[v]; + } else { + idx = unique_vertices.size(); + unique_vertices[v] = idx; + } + face_indices.push_back(idx); + } + } + + Vector<Vector3> vertices; + vertices.resize(unique_vertices.size()); + for (const KeyValue<Vector3, int> &E : unique_vertices) { + vertices.write[E.value] = E.key; + } + + Ref<NavigationMesh> nm; + nm.instantiate(); + nm->set_vertices(vertices); + + Vector<int> v3; + v3.resize(3); + for (uint32_t i = 0; i < face_indices.size(); i += 3) { + v3.write[0] = face_indices[i + 0]; + v3.write[1] = face_indices[i + 1]; + v3.write[2] = face_indices[i + 2]; + nm->add_polygon(v3); + } + + return nm; +} + +extern bool (*array_mesh_lightmap_unwrap_callback)(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, int p_index_count, const uint8_t *p_cache_data, bool *r_use_cache, uint8_t **r_mesh_cache, int *r_mesh_cache_size, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y); + +struct EditorSceneImporterMeshLightmapSurface { + Ref<Material> material; + LocalVector<SurfaceTool::Vertex> vertices; + Mesh::PrimitiveType primitive = Mesh::PrimitiveType::PRIMITIVE_MAX; + uint32_t format = 0; + String name; +}; + +Error ImporterMesh::lightmap_unwrap_cached(const Transform3D &p_base_transform, float p_texel_size, const Vector<uint8_t> &p_src_cache, Vector<uint8_t> &r_dst_cache) { + ERR_FAIL_COND_V(!array_mesh_lightmap_unwrap_callback, ERR_UNCONFIGURED); + ERR_FAIL_COND_V_MSG(blend_shapes.size() != 0, ERR_UNAVAILABLE, "Can't unwrap mesh with blend shapes."); + + LocalVector<float> vertices; + LocalVector<float> normals; + LocalVector<int> indices; + LocalVector<float> uv; + LocalVector<Pair<int, int>> uv_indices; + + Vector<EditorSceneImporterMeshLightmapSurface> lightmap_surfaces; + + // Keep only the scale + Basis basis = p_base_transform.get_basis(); + Vector3 scale = Vector3(basis.get_axis(0).length(), basis.get_axis(1).length(), basis.get_axis(2).length()); + + Transform3D transform; + transform.scale(scale); + + Basis normal_basis = transform.basis.inverse().transposed(); + + for (int i = 0; i < get_surface_count(); i++) { + EditorSceneImporterMeshLightmapSurface s; + s.primitive = get_surface_primitive_type(i); + + ERR_FAIL_COND_V_MSG(s.primitive != Mesh::PRIMITIVE_TRIANGLES, ERR_UNAVAILABLE, "Only triangles are supported for lightmap unwrap."); + Array arrays = get_surface_arrays(i); + s.material = get_surface_material(i); + s.name = get_surface_name(i); + + SurfaceTool::create_vertex_array_from_triangle_arrays(arrays, s.vertices, &s.format); + + PackedVector3Array rvertices = arrays[Mesh::ARRAY_VERTEX]; + int vc = rvertices.size(); + + PackedVector3Array rnormals = arrays[Mesh::ARRAY_NORMAL]; + + int vertex_ofs = vertices.size() / 3; + + vertices.resize((vertex_ofs + vc) * 3); + normals.resize((vertex_ofs + vc) * 3); + uv_indices.resize(vertex_ofs + vc); + + for (int j = 0; j < vc; j++) { + Vector3 v = transform.xform(rvertices[j]); + Vector3 n = normal_basis.xform(rnormals[j]).normalized(); + + vertices[(j + vertex_ofs) * 3 + 0] = v.x; + vertices[(j + vertex_ofs) * 3 + 1] = v.y; + vertices[(j + vertex_ofs) * 3 + 2] = v.z; + normals[(j + vertex_ofs) * 3 + 0] = n.x; + normals[(j + vertex_ofs) * 3 + 1] = n.y; + normals[(j + vertex_ofs) * 3 + 2] = n.z; + uv_indices[j + vertex_ofs] = Pair<int, int>(i, j); + } + + PackedInt32Array rindices = arrays[Mesh::ARRAY_INDEX]; + int ic = rindices.size(); + + float eps = 1.19209290e-7F; // Taken from xatlas.h + if (ic == 0) { + for (int j = 0; j < vc / 3; j++) { + Vector3 p0 = transform.xform(rvertices[j * 3 + 0]); + Vector3 p1 = transform.xform(rvertices[j * 3 + 1]); + Vector3 p2 = transform.xform(rvertices[j * 3 + 2]); + + if ((p0 - p1).length_squared() < eps || (p1 - p2).length_squared() < eps || (p2 - p0).length_squared() < eps) { + continue; + } + + indices.push_back(vertex_ofs + j * 3 + 0); + indices.push_back(vertex_ofs + j * 3 + 1); + indices.push_back(vertex_ofs + j * 3 + 2); + } + + } else { + for (int j = 0; j < ic / 3; j++) { + Vector3 p0 = transform.xform(rvertices[rindices[j * 3 + 0]]); + Vector3 p1 = transform.xform(rvertices[rindices[j * 3 + 1]]); + Vector3 p2 = transform.xform(rvertices[rindices[j * 3 + 2]]); + + if ((p0 - p1).length_squared() < eps || (p1 - p2).length_squared() < eps || (p2 - p0).length_squared() < eps) { + continue; + } + + indices.push_back(vertex_ofs + rindices[j * 3 + 0]); + indices.push_back(vertex_ofs + rindices[j * 3 + 1]); + indices.push_back(vertex_ofs + rindices[j * 3 + 2]); + } + } + + lightmap_surfaces.push_back(s); + } + + //unwrap + + bool use_cache = true; // Used to request cache generation and to know if cache was used + uint8_t *gen_cache; + int gen_cache_size; + float *gen_uvs; + int *gen_vertices; + int *gen_indices; + int gen_vertex_count; + int gen_index_count; + int size_x; + int size_y; + + bool ok = array_mesh_lightmap_unwrap_callback(p_texel_size, vertices.ptr(), normals.ptr(), vertices.size() / 3, indices.ptr(), indices.size(), p_src_cache.ptr(), &use_cache, &gen_cache, &gen_cache_size, &gen_uvs, &gen_vertices, &gen_vertex_count, &gen_indices, &gen_index_count, &size_x, &size_y); + + if (!ok) { + return ERR_CANT_CREATE; + } + + //remove surfaces + clear(); + + //create surfacetools for each surface.. + LocalVector<Ref<SurfaceTool>> surfaces_tools; + + for (int i = 0; i < lightmap_surfaces.size(); i++) { + Ref<SurfaceTool> st; + st.instantiate(); + st->begin(Mesh::PRIMITIVE_TRIANGLES); + st->set_material(lightmap_surfaces[i].material); + st->set_meta("name", lightmap_surfaces[i].name); + surfaces_tools.push_back(st); //stay there + } + + print_verbose("Mesh: Gen indices: " + itos(gen_index_count)); + + //go through all indices + for (int i = 0; i < gen_index_count; i += 3) { + ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 0]], (int)uv_indices.size(), ERR_BUG); + ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 1]], (int)uv_indices.size(), ERR_BUG); + ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 2]], (int)uv_indices.size(), ERR_BUG); + + ERR_FAIL_COND_V(uv_indices[gen_vertices[gen_indices[i + 0]]].first != uv_indices[gen_vertices[gen_indices[i + 1]]].first || uv_indices[gen_vertices[gen_indices[i + 0]]].first != uv_indices[gen_vertices[gen_indices[i + 2]]].first, ERR_BUG); + + int surface = uv_indices[gen_vertices[gen_indices[i + 0]]].first; + + for (int j = 0; j < 3; j++) { + SurfaceTool::Vertex v = lightmap_surfaces[surface].vertices[uv_indices[gen_vertices[gen_indices[i + j]]].second]; + + if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_COLOR) { + surfaces_tools[surface]->set_color(v.color); + } + if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_TEX_UV) { + surfaces_tools[surface]->set_uv(v.uv); + } + if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_NORMAL) { + surfaces_tools[surface]->set_normal(v.normal); + } + if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_TANGENT) { + Plane t; + t.normal = v.tangent; + t.d = v.binormal.dot(v.normal.cross(v.tangent)) < 0 ? -1 : 1; + surfaces_tools[surface]->set_tangent(t); + } + if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_BONES) { + surfaces_tools[surface]->set_bones(v.bones); + } + if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_WEIGHTS) { + surfaces_tools[surface]->set_weights(v.weights); + } + + Vector2 uv2(gen_uvs[gen_indices[i + j] * 2 + 0], gen_uvs[gen_indices[i + j] * 2 + 1]); + surfaces_tools[surface]->set_uv2(uv2); + + surfaces_tools[surface]->add_vertex(v.vertex); + } + } + + //generate surfaces + for (unsigned int i = 0; i < surfaces_tools.size(); i++) { + surfaces_tools[i]->index(); + Array arrays = surfaces_tools[i]->commit_to_arrays(); + add_surface(surfaces_tools[i]->get_primitive(), arrays, Array(), Dictionary(), surfaces_tools[i]->get_material(), surfaces_tools[i]->get_meta("name")); + } + + set_lightmap_size_hint(Size2(size_x, size_y)); + + if (gen_cache_size > 0) { + r_dst_cache.resize(gen_cache_size); + memcpy(r_dst_cache.ptrw(), gen_cache, gen_cache_size); + memfree(gen_cache); + } + + if (!use_cache) { + // Cache was not used, free the buffers + memfree(gen_vertices); + memfree(gen_indices); + memfree(gen_uvs); + } + + return OK; +} + +void ImporterMesh::set_lightmap_size_hint(const Size2i &p_size) { + lightmap_size_hint = p_size; +} + +Size2i ImporterMesh::get_lightmap_size_hint() const { + return lightmap_size_hint; +} + +void ImporterMesh::_bind_methods() { + ClassDB::bind_method(D_METHOD("add_blend_shape", "name"), &ImporterMesh::add_blend_shape); + ClassDB::bind_method(D_METHOD("get_blend_shape_count"), &ImporterMesh::get_blend_shape_count); + ClassDB::bind_method(D_METHOD("get_blend_shape_name", "blend_shape_idx"), &ImporterMesh::get_blend_shape_name); + + ClassDB::bind_method(D_METHOD("set_blend_shape_mode", "mode"), &ImporterMesh::set_blend_shape_mode); + ClassDB::bind_method(D_METHOD("get_blend_shape_mode"), &ImporterMesh::get_blend_shape_mode); + + ClassDB::bind_method(D_METHOD("add_surface", "primitive", "arrays", "blend_shapes", "lods", "material", "name", "flags"), &ImporterMesh::add_surface, DEFVAL(Array()), DEFVAL(Dictionary()), DEFVAL(Ref<Material>()), DEFVAL(String()), DEFVAL(0)); + + ClassDB::bind_method(D_METHOD("get_surface_count"), &ImporterMesh::get_surface_count); + ClassDB::bind_method(D_METHOD("get_surface_primitive_type", "surface_idx"), &ImporterMesh::get_surface_primitive_type); + ClassDB::bind_method(D_METHOD("get_surface_name", "surface_idx"), &ImporterMesh::get_surface_name); + ClassDB::bind_method(D_METHOD("get_surface_arrays", "surface_idx"), &ImporterMesh::get_surface_arrays); + ClassDB::bind_method(D_METHOD("get_surface_blend_shape_arrays", "surface_idx", "blend_shape_idx"), &ImporterMesh::get_surface_blend_shape_arrays); + ClassDB::bind_method(D_METHOD("get_surface_lod_count", "surface_idx"), &ImporterMesh::get_surface_lod_count); + ClassDB::bind_method(D_METHOD("get_surface_lod_size", "surface_idx", "lod_idx"), &ImporterMesh::get_surface_lod_size); + ClassDB::bind_method(D_METHOD("get_surface_lod_indices", "surface_idx", "lod_idx"), &ImporterMesh::get_surface_lod_indices); + ClassDB::bind_method(D_METHOD("get_surface_material", "surface_idx"), &ImporterMesh::get_surface_material); + ClassDB::bind_method(D_METHOD("get_surface_format", "surface_idx"), &ImporterMesh::get_surface_format); + + ClassDB::bind_method(D_METHOD("set_surface_name", "surface_idx", "name"), &ImporterMesh::set_surface_name); + ClassDB::bind_method(D_METHOD("set_surface_material", "surface_idx", "material"), &ImporterMesh::set_surface_material); + + ClassDB::bind_method(D_METHOD("get_mesh", "base_mesh"), &ImporterMesh::get_mesh, DEFVAL(Ref<ArrayMesh>())); + ClassDB::bind_method(D_METHOD("clear"), &ImporterMesh::clear); + + ClassDB::bind_method(D_METHOD("_set_data", "data"), &ImporterMesh::_set_data); + ClassDB::bind_method(D_METHOD("_get_data"), &ImporterMesh::_get_data); + + ClassDB::bind_method(D_METHOD("set_lightmap_size_hint", "size"), &ImporterMesh::set_lightmap_size_hint); + ClassDB::bind_method(D_METHOD("get_lightmap_size_hint"), &ImporterMesh::get_lightmap_size_hint); + + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_data", "_get_data"); +} diff --git a/scene/resources/importer_mesh.h b/scene/resources/importer_mesh.h new file mode 100644 index 0000000000..8576312a8a --- /dev/null +++ b/scene/resources/importer_mesh.h @@ -0,0 +1,133 @@ +/*************************************************************************/ +/* importer_mesh.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SCENE_IMPORTER_MESH_H +#define SCENE_IMPORTER_MESH_H + +#include "core/io/resource.h" +#include "core/templates/local_vector.h" +#include "scene/resources/concave_polygon_shape_3d.h" +#include "scene/resources/convex_polygon_shape_3d.h" +#include "scene/resources/mesh.h" +#include "scene/resources/navigation_mesh.h" + +#include <cstdint> + +// The following classes are used by importers instead of ArrayMesh and MeshInstance3D +// so the data is not registered (hence, quality loss), importing happens faster and +// its easier to modify before saving + +class ImporterMesh : public Resource { + GDCLASS(ImporterMesh, Resource) + + struct Surface { + Mesh::PrimitiveType primitive; + Array arrays; + struct BlendShape { + Array arrays; + }; + Vector<BlendShape> blend_shape_data; + struct LOD { + Vector<int> indices; + float distance = 0.0f; + }; + Vector<LOD> lods; + Ref<Material> material; + String name; + uint32_t flags = 0; + + struct LODComparator { + _FORCE_INLINE_ bool operator()(const LOD &l, const LOD &r) const { + return l.distance < r.distance; + } + }; + + void split_normals(const LocalVector<int> &p_indices, const LocalVector<Vector3> &p_normals); + static void _split_normals(Array &r_arrays, const LocalVector<int> &p_indices, const LocalVector<Vector3> &p_normals); + }; + Vector<Surface> surfaces; + Vector<String> blend_shapes; + Mesh::BlendShapeMode blend_shape_mode = Mesh::BLEND_SHAPE_MODE_NORMALIZED; + + Ref<ArrayMesh> mesh; + + Ref<ImporterMesh> shadow_mesh; + + Size2i lightmap_size_hint; + +protected: + void _set_data(const Dictionary &p_data); + Dictionary _get_data() const; + + static void _bind_methods(); + +public: + void add_blend_shape(const String &p_name); + int get_blend_shape_count() const; + String get_blend_shape_name(int p_blend_shape) const; + + void add_surface(Mesh::PrimitiveType p_primitive, const Array &p_arrays, const Array &p_blend_shapes = Array(), const Dictionary &p_lods = Dictionary(), const Ref<Material> &p_material = Ref<Material>(), const String &p_name = String(), const uint32_t p_flags = 0); + int get_surface_count() const; + + void set_blend_shape_mode(Mesh::BlendShapeMode p_blend_shape_mode); + Mesh::BlendShapeMode get_blend_shape_mode() const; + + Mesh::PrimitiveType get_surface_primitive_type(int p_surface); + String get_surface_name(int p_surface) const; + void set_surface_name(int p_surface, const String &p_name); + Array get_surface_arrays(int p_surface) const; + Array get_surface_blend_shape_arrays(int p_surface, int p_blend_shape) const; + int get_surface_lod_count(int p_surface) const; + Vector<int> get_surface_lod_indices(int p_surface, int p_lod) const; + float get_surface_lod_size(int p_surface, int p_lod) const; + Ref<Material> get_surface_material(int p_surface) const; + uint32_t get_surface_format(int p_surface) const; + + void set_surface_material(int p_surface, const Ref<Material> &p_material); + + void generate_lods(float p_normal_merge_angle, float p_normal_split_angle); + + void create_shadow_mesh(); + Ref<ImporterMesh> get_shadow_mesh() const; + + Vector<Face3> get_faces() const; + Vector<Ref<Shape3D>> convex_decompose(const Mesh::ConvexDecompositionSettings &p_settings) const; + Ref<Shape3D> create_trimesh_shape() const; + Ref<NavigationMesh> create_navigation_mesh(); + Error lightmap_unwrap_cached(const Transform3D &p_base_transform, float p_texel_size, const Vector<uint8_t> &p_src_cache, Vector<uint8_t> &r_dst_cache); + + void set_lightmap_size_hint(const Size2i &p_size); + Size2i get_lightmap_size_hint() const; + + bool has_mesh() const; + Ref<ArrayMesh> get_mesh(const Ref<ArrayMesh> &p_base = Ref<ArrayMesh>()); + void clear(); +}; +#endif // SCENE_IMPORTER_MESH_H diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index 1d2a2ef26c..abb3381c4e 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -32,11 +32,6 @@ #include "core/config/engine.h" #include "core/version.h" - -#ifdef TOOLS_ENABLED -#include "editor/editor_settings.h" -#endif - #include "scene/main/scene_tree.h" #include "scene/scene_string_names.h" @@ -80,6 +75,9 @@ void Material::_validate_property(PropertyInfo &property) const { if (!_can_do_next_pass() && property.name == "next_pass") { property.usage = PROPERTY_USAGE_NONE; } + if (!_can_use_render_priority() && property.name == "render_priority") { + property.usage = PROPERTY_USAGE_NONE; + } } void Material::inspect_native_shader_code() { @@ -268,19 +266,13 @@ void ShaderMaterial::_bind_methods() { } void ShaderMaterial::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { -#ifdef TOOLS_ENABLED - const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\""; -#else - const String quote_style = "\""; -#endif - String f = p_function.operator String(); if ((f == "get_shader_param" || f == "set_shader_param") && p_idx == 0) { if (shader.is_valid()) { List<PropertyInfo> pl; shader->get_param_list(&pl); for (const PropertyInfo &E : pl) { - r_options->push_back(E.name.replace_first("shader_param/", "").quote(quote_style)); + r_options->push_back(E.name.replace_first("shader_param/", "").quote()); } } } @@ -291,6 +283,10 @@ bool ShaderMaterial::_can_do_next_pass() const { return shader.is_valid() && shader->get_mode() == Shader::MODE_SPATIAL; } +bool ShaderMaterial::_can_use_render_priority() const { + return shader.is_valid() && shader->get_mode() == Shader::MODE_SPATIAL; +} + Shader::Mode ShaderMaterial::get_shader_mode() const { if (shader.is_valid()) { return shader->get_mode(); @@ -790,12 +786,10 @@ void BaseMaterial3D::_update_shader() { } } break; case BILLBOARD_FIXED_Y: { - code += " MODELVIEW_MATRIX = INV_CAMERA_MATRIX * mat4(CAMERA_MATRIX[0],WORLD_MATRIX[1],vec4(normalize(cross(CAMERA_MATRIX[0].xyz,WORLD_MATRIX[1].xyz)), 0.0),WORLD_MATRIX[3]);\n"; + code += " MODELVIEW_MATRIX = INV_CAMERA_MATRIX * mat4(vec4(normalize(cross(vec3(0.0, 1.0, 0.0), CAMERA_MATRIX[2].xyz)),0.0),vec4(0.0, 1.0, 0.0, 0.0),vec4(normalize(cross(CAMERA_MATRIX[0].xyz, vec3(0.0, 1.0, 0.0))),0.0),WORLD_MATRIX[3]);\n"; if (flags[FLAG_BILLBOARD_KEEP_SCALE]) { - code += " MODELVIEW_MATRIX = MODELVIEW_MATRIX * mat4(vec4(length(WORLD_MATRIX[0].xyz), 0.0, 0.0, 0.0),vec4(0.0, 1.0, 0.0, 0.0),vec4(0.0, 0.0, length(WORLD_MATRIX[2].xyz), 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n"; - } else { - code += " MODELVIEW_MATRIX = MODELVIEW_MATRIX * mat4(vec4(1.0, 0.0, 0.0, 0.0),vec4(0.0, 1.0/length(WORLD_MATRIX[1].xyz), 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0),vec4(0.0, 0.0, 0.0 ,1.0));\n"; + code += " MODELVIEW_MATRIX = MODELVIEW_MATRIX * mat4(vec4(length(WORLD_MATRIX[0].xyz), 0.0, 0.0, 0.0),vec4(0.0, length(WORLD_MATRIX[1].xyz), 0.0, 0.0),vec4(0.0, 0.0, length(WORLD_MATRIX[2].xyz), 0.0),vec4(0.0, 0.0, 0.0, 1.0));\n"; } } break; case BILLBOARD_PARTICLES: { @@ -817,7 +811,7 @@ void BaseMaterial3D::_update_shader() { code += " particle_frame = mod(particle_frame, particle_total_frames);\n"; code += " }"; code += " UV /= vec2(h_frames, v_frames);\n"; - code += " UV += vec2(mod(particle_frame, h_frames) / h_frames, floor(particle_frame / h_frames) / v_frames);\n"; + code += " UV += vec2(mod(particle_frame, h_frames) / h_frames, floor((particle_frame + 0.5) / h_frames) / v_frames);\n"; } break; case BILLBOARD_MAX: break; // Internal value, skip. @@ -963,7 +957,9 @@ void BaseMaterial3D::_update_shader() { } else { code += " float depth = 1.0 - texture(texture_heightmap, base_uv).r;\n"; } - code += " vec2 ofs = base_uv - view_dir.xy / view_dir.z * (depth * heightmap_scale);\n"; + // Use offset limiting to improve the appearance of non-deep parallax. + // This reduces the impression of depth, but avoids visible warping in the distance. + code += " vec2 ofs = base_uv - view_dir.xy * depth * heightmap_scale;\n"; } code += " base_uv=ofs;\n"; @@ -1301,7 +1297,7 @@ void BaseMaterial3D::flush_changes() { void BaseMaterial3D::_queue_shader_change() { MutexLock lock(material_mutex); - if (!element.in_list()) { + if (is_initialized && !element.in_list()) { dirty_materials->add(&element); } } @@ -2777,6 +2773,7 @@ BaseMaterial3D::BaseMaterial3D(bool p_orm) : flags[FLAG_USE_TEXTURE_REPEAT] = true; + is_initialized = true; _queue_shader_change(); } diff --git a/scene/resources/material.h b/scene/resources/material.h index e2838e1399..798f7568df 100644 --- a/scene/resources/material.h +++ b/scene/resources/material.h @@ -53,6 +53,7 @@ protected: _FORCE_INLINE_ RID _get_material() const { return material; } static void _bind_methods(); virtual bool _can_do_next_pass() const { return false; } + virtual bool _can_use_render_priority() const { return false; } void _validate_property(PropertyInfo &property) const override; @@ -93,6 +94,7 @@ protected: void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override; virtual bool _can_do_next_pass() const override; + virtual bool _can_use_render_priority() const override; void _shader_changed(); @@ -440,6 +442,7 @@ private: _FORCE_INLINE_ void _queue_shader_change(); _FORCE_INLINE_ bool _is_shader_dirty() const; + bool is_initialized = false; bool orm; Color albedo; @@ -534,6 +537,7 @@ protected: static void _bind_methods(); void _validate_property(PropertyInfo &property) const override; virtual bool _can_do_next_pass() const override { return true; } + virtual bool _can_use_render_priority() const override { return true; } public: void set_albedo(const Color &p_albedo); diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index ad589a605e..7ffe0b03e1 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -38,7 +38,7 @@ #include <stdlib.h> -Mesh::ConvexDecompositionFunc Mesh::convex_composition_function = nullptr; +Mesh::ConvexDecompositionFunc Mesh::convex_decomposition_function = nullptr; Ref<TriangleMesh> Mesh::generate_triangle_mesh() const { if (triangle_mesh.is_valid()) { @@ -156,75 +156,19 @@ void Mesh::generate_debug_mesh_indices(Vector<Vector3> &r_points) { } } -bool Mesh::surface_is_softbody_friendly(int p_idx) const { - const uint32_t surface_format = surface_get_format(p_idx); - return (surface_format & Mesh::ARRAY_FLAG_USE_DYNAMIC_UPDATE); -} - Vector<Face3> Mesh::get_faces() const { Ref<TriangleMesh> tm = generate_triangle_mesh(); if (tm.is_valid()) { return tm->get_faces(); } return Vector<Face3>(); - /* - for (int i=0;i<surfaces.size();i++) { - if (RenderingServer::get_singleton()->mesh_surface_get_primitive_type( mesh, i ) != RenderingServer::PRIMITIVE_TRIANGLES ) - continue; - - Vector<int> indices; - Vector<Vector3> vertices; - - vertices=RenderingServer::get_singleton()->mesh_surface_get_array(mesh, i,RenderingServer::ARRAY_VERTEX); - - int len=RenderingServer::get_singleton()->mesh_surface_get_array_index_len(mesh, i); - bool has_indices; - - if (len>0) { - indices=RenderingServer::get_singleton()->mesh_surface_get_array(mesh, i,RenderingServer::ARRAY_INDEX); - has_indices=true; - - } else { - len=vertices.size(); - has_indices=false; - } - - if (len<=0) - continue; - - const int* indicesr = indices.ptr(); - const int *indicesptr = indicesr.ptr(); - - const Vector3* verticesr = vertices.ptr(); - const Vector3 *verticesptr = verticesr.ptr(); - - int old_faces=faces.size(); - int new_faces=old_faces+(len/3); - - faces.resize(new_faces); - - Face3* facesw = faces.ptrw(); - Face3 *facesptr=facesw.ptr(); - - - for (int i=0;i<len/3;i++) { - Face3 face; - - for (int j=0;j<3;j++) { - int idx=i*3+j; - face.vertex[j] = has_indices ? verticesptr[ indicesptr[ idx ] ] : verticesptr[idx]; - } - - facesptr[i+old_faces]=face; - } - - } -*/ } Ref<Shape3D> Mesh::create_convex_shape(bool p_clean, bool p_simplify) const { if (p_simplify) { - Vector<Ref<Shape3D>> decomposed = convex_decompose(1); + ConvexDecompositionSettings settings; + settings.max_convex_hulls = 1; + Vector<Ref<Shape3D>> decomposed = convex_decompose(settings); if (decomposed.size() == 1) { return decomposed[0]; } else { @@ -429,8 +373,8 @@ Ref<Mesh> Mesh::create_outline(float p_margin) const { //normalize - for (Map<Vector3, Vector3>::Element *E = normal_accum.front(); E; E = E->next()) { - E->get().normalize(); + for (KeyValue<Vector3, Vector3> &E : normal_accum) { + E.value.normalize(); } //displace normals @@ -543,6 +487,7 @@ void Mesh::_bind_methods() { BIND_ENUM_CONSTANT(ARRAY_FORMAT_BLEND_SHAPE_MASK); BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM_BASE); + BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM_BITS); BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM0_SHIFT); BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM1_SHIFT); BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM2_SHIFT); @@ -564,36 +509,37 @@ void Mesh::clear_cache() const { debug_lines.clear(); } -Vector<Ref<Shape3D>> Mesh::convex_decompose(int p_max_convex_hulls) const { - ERR_FAIL_COND_V(!convex_composition_function, Vector<Ref<Shape3D>>()); +Vector<Ref<Shape3D>> Mesh::convex_decompose(const ConvexDecompositionSettings &p_settings) const { + ERR_FAIL_COND_V(!convex_decomposition_function, Vector<Ref<Shape3D>>()); - const Vector<Face3> faces = get_faces(); - - Vector<Vector<Face3>> decomposed = convex_composition_function(faces, p_max_convex_hulls); + Ref<TriangleMesh> tm = generate_triangle_mesh(); + ERR_FAIL_COND_V(!tm.is_valid(), Vector<Ref<Shape3D>>()); - Vector<Ref<Shape3D>> ret; + const Vector<TriangleMesh::Triangle> &triangles = tm->get_triangles(); + int triangle_count = triangles.size(); - for (int i = 0; i < decomposed.size(); i++) { - Set<Vector3> points; - for (int j = 0; j < decomposed[i].size(); j++) { - points.insert(decomposed[i][j].vertex[0]); - points.insert(decomposed[i][j].vertex[1]); - points.insert(decomposed[i][j].vertex[2]); - } - - Vector<Vector3> convex_points; - convex_points.resize(points.size()); - { - Vector3 *w = convex_points.ptrw(); - int idx = 0; - for (Set<Vector3>::Element *E = points.front(); E; E = E->next()) { - w[idx++] = E->get(); + Vector<uint32_t> indices; + { + indices.resize(triangle_count * 3); + uint32_t *w = indices.ptrw(); + for (int i = 0; i < triangle_count; i++) { + for (int j = 0; j < 3; j++) { + w[i * 3 + j] = triangles[i].indices[j]; } } + } + + const Vector<Vector3> &vertices = tm->get_vertices(); + int vertex_count = vertices.size(); + Vector<Vector<Vector3>> decomposed = convex_decomposition_function((real_t *)vertices.ptr(), vertex_count, indices.ptr(), triangle_count, p_settings, nullptr); + + Vector<Ref<Shape3D>> ret; + + for (int i = 0; i < decomposed.size(); i++) { Ref<ConvexPolygonShape3D> shape; shape.instantiate(); - shape->set_points(convex_points); + shape->set_points(decomposed[i]); ret.push_back(shape); } @@ -1631,7 +1577,7 @@ void ArrayMesh::regen_normal_maps() { } //dirty hack -bool (*array_mesh_lightmap_unwrap_callback)(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, int p_index_count, const uint8_t *p_cache_data, bool *r_use_cache, uint8_t **r_mesh_cache, int *r_mesh_cache_size, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y) = NULL; +bool (*array_mesh_lightmap_unwrap_callback)(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, int p_index_count, const uint8_t *p_cache_data, bool *r_use_cache, uint8_t **r_mesh_cache, int *r_mesh_cache_size, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y) = nullptr; struct ArrayMeshLightmapSurface { Ref<Material> material; diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h index 27b0eb098b..8d5571d67c 100644 --- a/scene/resources/mesh.h +++ b/scene/resources/mesh.h @@ -105,6 +105,7 @@ public: ARRAY_FORMAT_BLEND_SHAPE_MASK = RS::ARRAY_FORMAT_BLEND_SHAPE_MASK, ARRAY_FORMAT_CUSTOM_BASE = RS::ARRAY_FORMAT_CUSTOM_BASE, + ARRAY_FORMAT_CUSTOM_BITS = RS::ARRAY_FORMAT_CUSTOM_BITS, ARRAY_FORMAT_CUSTOM0_SHIFT = RS::ARRAY_FORMAT_CUSTOM0_SHIFT, ARRAY_FORMAT_CUSTOM1_SHIFT = RS::ARRAY_FORMAT_CUSTOM1_SHIFT, ARRAY_FORMAT_CUSTOM2_SHIFT = RS::ARRAY_FORMAT_CUSTOM2_SHIFT, @@ -131,7 +132,6 @@ public: virtual int get_surface_count() const = 0; virtual int surface_get_array_len(int p_idx) const = 0; virtual int surface_get_array_index_len(int p_idx) const = 0; - virtual bool surface_is_softbody_friendly(int p_idx) const; virtual Array surface_get_arrays(int p_surface) const = 0; virtual Array surface_get_blend_shape_arrays(int p_surface) const = 0; virtual Dictionary surface_get_lods(int p_surface) const = 0; @@ -159,11 +159,42 @@ public: Size2i get_lightmap_size_hint() const; void clear_cache() const; - typedef Vector<Vector<Face3>> (*ConvexDecompositionFunc)(const Vector<Face3> &p_faces, int p_max_convex_hulls); + struct ConvexDecompositionSettings { + enum Mode : int { + CONVEX_DECOMPOSITION_MODE_VOXEL = 0, + CONVEX_DECOMPOSITION_MODE_TETRAHEDRON + }; + + /// Maximum concavity. [Range: 0.0 -> 1.0] + real_t max_concavity = 1.0; + /// Controls the bias toward clipping along symmetry planes. [Range: 0.0 -> 1.0] + real_t symmetry_planes_clipping_bias = 0.05; + /// Controls the bias toward clipping along revolution axes. [Range: 0.0 -> 1.0] + real_t revolution_axes_clipping_bias = 0.05; + real_t min_volume_per_convex_hull = 0.0001; + /// Maximum number of voxels generated during the voxelization stage. + uint32_t resolution = 10'000; + uint32_t max_num_vertices_per_convex_hull = 32; + /// Controls the granularity of the search for the "best" clipping plane. + /// [Range: 1 -> 16] + uint32_t plane_downsampling = 4; + /// Controls the precision of the convex-hull generation process during the + /// clipping plane selection stage. + /// [Range: 1 -> 16] + uint32_t convexhull_downsampling = 4; + /// enable/disable normalizing the mesh before applying the convex decomposition. + bool normalize_mesh = false; + Mode mode = CONVEX_DECOMPOSITION_MODE_VOXEL; + bool convexhull_approximation = true; + /// This is the maximum number of convex hulls to produce from the merge operation. + uint32_t max_convex_hulls = 1; + bool project_hull_vertices = true; + }; + typedef Vector<Vector<Vector3>> (*ConvexDecompositionFunc)(const real_t *p_vertices, int p_vertex_count, const uint32_t *p_triangles, int p_triangle_count, const ConvexDecompositionSettings &p_settings, Vector<Vector<uint32_t>> *r_convex_indices); - static ConvexDecompositionFunc convex_composition_function; + static ConvexDecompositionFunc convex_decomposition_function; - Vector<Ref<Shape3D>> convex_decompose(int p_max_convex_hulls = -1) const; + Vector<Ref<Shape3D>> convex_decompose(const ConvexDecompositionSettings &p_settings) const; virtual int get_builtin_bind_pose_count() const; virtual Transform3D get_builtin_bind_pose(int p_index) const; diff --git a/scene/resources/mesh_library.cpp b/scene/resources/mesh_library.cpp index 33c9ca6d1e..309670e0b1 100644 --- a/scene/resources/mesh_library.cpp +++ b/scene/resources/mesh_library.cpp @@ -43,6 +43,8 @@ bool MeshLibrary::_set(const StringName &p_name, const Variant &p_value) { set_item_name(idx, p_value); } else if (what == "mesh") { set_item_mesh(idx, p_value); + } else if (what == "mesh_transform") { + set_item_mesh_transform(idx, p_value); } else if (what == "shape") { Vector<ShapeData> shapes; ShapeData sd; @@ -77,6 +79,8 @@ bool MeshLibrary::_get(const StringName &p_name, Variant &r_ret) const { r_ret = get_item_name(idx); } else if (what == "mesh") { r_ret = get_item_mesh(idx); + } else if (what == "mesh_transform") { + r_ret = get_item_mesh_transform(idx); } else if (what == "shapes") { r_ret = _get_item_shapes(idx); } else if (what == "navmesh") { @@ -93,8 +97,8 @@ bool MeshLibrary::_get(const StringName &p_name, Variant &r_ret) const { } void MeshLibrary::_get_property_list(List<PropertyInfo> *p_list) const { - for (Map<int, Item>::Element *E = item_map.front(); E; E = E->next()) { - String name = "item/" + itos(E->key()) + "/"; + for (const KeyValue<int, Item> &E : item_map) { + String name = "item/" + itos(E.key) + "/"; p_list->push_back(PropertyInfo(Variant::STRING, name + "name")); p_list->push_back(PropertyInfo(Variant::OBJECT, name + "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh")); p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, name + "mesh_transform")); @@ -127,6 +131,14 @@ void MeshLibrary::set_item_mesh(int p_item, const Ref<Mesh> &p_mesh) { notify_property_list_changed(); } +void MeshLibrary::set_item_mesh_transform(int p_item, const Transform3D &p_transform) { + ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'."); + item_map[p_item].mesh_transform = p_transform; + notify_change_to_owners(); + emit_changed(); + notify_property_list_changed(); +} + void MeshLibrary::set_item_shapes(int p_item, const Vector<ShapeData> &p_shapes) { ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'."); item_map[p_item].shapes = p_shapes; @@ -170,6 +182,11 @@ Ref<Mesh> MeshLibrary::get_item_mesh(int p_item) const { return item_map[p_item].mesh; } +Transform3D MeshLibrary::get_item_mesh_transform(int p_item) const { + ERR_FAIL_COND_V_MSG(!item_map.has(p_item), Transform3D(), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'."); + return item_map[p_item].mesh_transform; +} + Vector<MeshLibrary::ShapeData> MeshLibrary::get_item_shapes(int p_item) const { ERR_FAIL_COND_V_MSG(!item_map.has(p_item), Vector<ShapeData>(), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'."); return item_map[p_item].shapes; @@ -213,17 +230,17 @@ Vector<int> MeshLibrary::get_item_list() const { Vector<int> ret; ret.resize(item_map.size()); int idx = 0; - for (Map<int, Item>::Element *E = item_map.front(); E; E = E->next()) { - ret.write[idx++] = E->key(); + for (const KeyValue<int, Item> &E : item_map) { + ret.write[idx++] = E.key; } return ret; } int MeshLibrary::find_item_by_name(const String &p_name) const { - for (Map<int, Item>::Element *E = item_map.front(); E; E = E->next()) { - if (E->get().name == p_name) { - return E->key(); + for (const KeyValue<int, Item> &E : item_map) { + if (E.value.name == p_name) { + return E.key; } } return -1; @@ -271,12 +288,14 @@ void MeshLibrary::_bind_methods() { ClassDB::bind_method(D_METHOD("create_item", "id"), &MeshLibrary::create_item); ClassDB::bind_method(D_METHOD("set_item_name", "id", "name"), &MeshLibrary::set_item_name); ClassDB::bind_method(D_METHOD("set_item_mesh", "id", "mesh"), &MeshLibrary::set_item_mesh); + ClassDB::bind_method(D_METHOD("set_item_mesh_transform", "id", "mesh_transform"), &MeshLibrary::set_item_mesh_transform); ClassDB::bind_method(D_METHOD("set_item_navmesh", "id", "navmesh"), &MeshLibrary::set_item_navmesh); ClassDB::bind_method(D_METHOD("set_item_navmesh_transform", "id", "navmesh"), &MeshLibrary::set_item_navmesh_transform); ClassDB::bind_method(D_METHOD("set_item_shapes", "id", "shapes"), &MeshLibrary::_set_item_shapes); ClassDB::bind_method(D_METHOD("set_item_preview", "id", "texture"), &MeshLibrary::set_item_preview); ClassDB::bind_method(D_METHOD("get_item_name", "id"), &MeshLibrary::get_item_name); ClassDB::bind_method(D_METHOD("get_item_mesh", "id"), &MeshLibrary::get_item_mesh); + ClassDB::bind_method(D_METHOD("get_item_mesh_transform", "id"), &MeshLibrary::get_item_mesh_transform); ClassDB::bind_method(D_METHOD("get_item_navmesh", "id"), &MeshLibrary::get_item_navmesh); ClassDB::bind_method(D_METHOD("get_item_navmesh_transform", "id"), &MeshLibrary::get_item_navmesh_transform); ClassDB::bind_method(D_METHOD("get_item_shapes", "id"), &MeshLibrary::_get_item_shapes); diff --git a/scene/resources/mesh_library.h b/scene/resources/mesh_library.h index 1e8a6bf3ff..c25df757e9 100644 --- a/scene/resources/mesh_library.h +++ b/scene/resources/mesh_library.h @@ -52,6 +52,7 @@ public: Vector<ShapeData> shapes; Ref<Texture2D> preview; Transform3D navmesh_transform; + Transform3D mesh_transform; Ref<NavigationMesh> navmesh; }; @@ -72,12 +73,14 @@ public: void create_item(int p_item); void set_item_name(int p_item, const String &p_name); void set_item_mesh(int p_item, const Ref<Mesh> &p_mesh); + void set_item_mesh_transform(int p_item, const Transform3D &p_transform); void set_item_navmesh(int p_item, const Ref<NavigationMesh> &p_navmesh); void set_item_navmesh_transform(int p_item, const Transform3D &p_transform); void set_item_shapes(int p_item, const Vector<ShapeData> &p_shapes); void set_item_preview(int p_item, const Ref<Texture2D> &p_preview); String get_item_name(int p_item) const; Ref<Mesh> get_item_mesh(int p_item) const; + Transform3D get_item_mesh_transform(int p_item) const; Ref<NavigationMesh> get_item_navmesh(int p_item) const; Transform3D get_item_navmesh_transform(int p_item) const; Vector<ShapeData> get_item_shapes(int p_item) const; diff --git a/scene/resources/navigation_mesh.cpp b/scene/resources/navigation_mesh.cpp index 00cee9269b..d87056f55d 100644 --- a/scene/resources/navigation_mesh.cpp +++ b/scene/resources/navigation_mesh.cpp @@ -41,6 +41,8 @@ void NavigationMesh::create_from_mesh(const Ref<Mesh> &p_mesh) { continue; } Array arr = p_mesh->surface_get_arrays(i); + ERR_CONTINUE(arr.size() != Mesh::ARRAY_MAX); + Vector<Vector3> varr = arr[Mesh::ARRAY_VERTEX]; Vector<int> iarr = arr[Mesh::ARRAY_INDEX]; if (varr.size() == 0 || iarr.size() == 0) { @@ -367,10 +369,10 @@ Ref<Mesh> NavigationMesh::get_debug_mesh() { } List<Vector3> lines; - for (Map<_EdgeKey, bool>::Element *E = edge_map.front(); E; E = E->next()) { - if (E->get()) { - lines.push_back(E->key().from); - lines.push_back(E->key().to); + for (const KeyValue<_EdgeKey, bool> &E : edge_map) { + if (E.value) { + lines.push_back(E.key.from); + lines.push_back(E.key.to); } } diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index 54bfc427c4..60cda637ca 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -99,8 +99,9 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { #endif parent = nparent; } else { - // i == 0 is root node. Confirm that it doesn't have a parent defined. + // 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])); } Node *node = nullptr; @@ -298,8 +299,8 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { } } - for (Map<Ref<Resource>, Ref<Resource>>::Element *E = resources_local_to_scene.front(); E; E = E->next()) { - E->get()->setup_local_to_scene(); + for (KeyValue<Ref<Resource>, Ref<Resource>> &E : resources_local_to_scene) { + E.value->setup_local_to_scene(); } //do connections @@ -379,10 +380,17 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map return OK; } + bool is_editable_instance = false; + // save the child instantiated scenes that are chosen as editable, so they can be restored // upon load back - if (p_node != p_owner && p_node->get_filename() != String() && p_owner->is_editable_instance(p_node)) { + if (p_node != p_owner && p_node->get_scene_file_path() != String() && p_owner->is_editable_instance(p_node)) { editable_instances.push_back(p_owner->get_path_to(p_node)); + // Node is the root of an editable instance. + is_editable_instance = true; + } else if (p_node->get_owner() && p_owner->is_ancestor_of(p_node->get_owner()) && p_owner->is_editable_instance(p_node->get_owner())) { + // Node is part of an editable instance. + is_editable_instance = true; } NodeData nd; @@ -429,14 +437,14 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map } } - if (p_node->get_filename() != String() && p_node->get_owner() == p_owner && instantiated_by_owner) { + if (p_node->get_scene_file_path() != String() && p_node->get_owner() == p_owner && instantiated_by_owner) { if (p_node->get_scene_instance_load_placeholder()) { //it's a placeholder, use the placeholder path - nd.instance = _vm_get_variant(p_node->get_filename(), variant_map); + nd.instance = _vm_get_variant(p_node->get_scene_file_path(), variant_map); nd.instance |= FLAG_INSTANCE_IS_PLACEHOLDER; } else { //must instance ourselves - Ref<PackedScene> instance = ResourceLoader::load(p_node->get_filename()); + Ref<PackedScene> instance = ResourceLoader::load(p_node->get_scene_file_path()); if (!instance.is_valid()) { return ERR_CANT_OPEN; } @@ -446,7 +454,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map } n = nullptr; } else { - if (n->get_filename() != String()) { + if (n->get_scene_file_path() != String()) { //is an instance Ref<SceneState> state = n->get_scene_instance_state(); if (state.is_valid()) { @@ -610,7 +618,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map // Save the right type. If this node was created by an instance // then flag that the node should not be created but reused - if (pack_state_stack.is_empty()) { + if (pack_state_stack.is_empty() && !is_editable_instance) { //this node is not part of an instancing process, so save the type nd.type = _nm_get_string(p_node->get_class(), name_map); } else { @@ -706,7 +714,7 @@ Error SceneState::_parse_connections(Node *p_owner, Node *p_node, Map<StringName ERR_CONTINUE(!common_parent); - if (common_parent != p_owner && common_parent->get_filename() == String()) { + if (common_parent != p_owner && common_parent->get_scene_file_path() == String()) { common_parent = common_parent->get_owner(); } @@ -766,7 +774,7 @@ Error SceneState::_parse_connections(Node *p_owner, Node *p_node, Map<StringName nl = nullptr; } else { - if (nl->get_filename() != String()) { + if (nl->get_scene_file_path() != String()) { //is an instance Ref<SceneState> state = nl->get_scene_instance_state(); if (state.is_valid()) { @@ -879,8 +887,8 @@ Error SceneState::pack(Node *p_scene) { names.resize(name_map.size()); - for (Map<StringName, int>::Element *E = name_map.front(); E; E = E->next()) { - names.write[E->get()] = E->key(); + for (const KeyValue<StringName, int> &E : name_map) { + names.write[E.value] = E.key; } variants.resize(variant_map.size()); @@ -891,14 +899,14 @@ Error SceneState::pack(Node *p_scene) { } node_paths.resize(nodepath_map.size()); - for (Map<Node *, int>::Element *E = nodepath_map.front(); E; E = E->next()) { - node_paths.write[E->get()] = scene->get_path_to(E->key()); + for (const KeyValue<Node *, int> &E : nodepath_map) { + node_paths.write[E.value] = scene->get_path_to(E.key); } if (Engine::get_singleton()->is_editor_hint()) { // Build node path cache - for (Map<Node *, int>::Element *E = node_map.front(); E; E = E->next()) { - node_path_cache[scene->get_path_to(E->key())] = E->get(); + for (const KeyValue<Node *, int> &E : node_map) { + node_path_cache[scene->get_path_to(E.key)] = E.value; } } @@ -969,9 +977,9 @@ int SceneState::find_node_by_path(const NodePath &p_node) const { } int SceneState::_find_base_scene_node_remap_key(int p_idx) const { - for (Map<int, int>::Element *E = base_scene_node_remap.front(); E; E = E->next()) { - if (E->value() == p_idx) { - return E->key(); + for (const KeyValue<int, int> &E : base_scene_node_remap) { + if (E.value == p_idx) { + return E.key; } } return -1; @@ -1644,7 +1652,7 @@ Node *PackedScene::instantiate(GenEditState p_edit_state) const { } if (get_path() != "" && get_path().find("::") == -1) { - s->set_filename(get_path()); + s->set_scene_file_path(get_path()); } s->notification(Node::NOTIFICATION_INSTANCED); diff --git a/scene/resources/particles_material.cpp b/scene/resources/particles_material.cpp index 34f4142a14..d9ec0bfd69 100644 --- a/scene/resources/particles_material.cpp +++ b/scene/resources/particles_material.cpp @@ -45,31 +45,31 @@ void ParticlesMaterial::init_shaders() { shader_names->direction = "direction"; shader_names->spread = "spread"; shader_names->flatness = "flatness"; - shader_names->initial_linear_velocity = "initial_linear_velocity"; - shader_names->initial_angle = "initial_angle"; - shader_names->angular_velocity = "angular_velocity"; - shader_names->orbit_velocity = "orbit_velocity"; - shader_names->linear_accel = "linear_accel"; - shader_names->radial_accel = "radial_accel"; - shader_names->tangent_accel = "tangent_accel"; - shader_names->damping = "damping"; - shader_names->scale = "scale"; - shader_names->hue_variation = "hue_variation"; - shader_names->anim_speed = "anim_speed"; - shader_names->anim_offset = "anim_offset"; - - shader_names->initial_linear_velocity_random = "initial_linear_velocity_random"; - shader_names->initial_angle_random = "initial_angle_random"; - shader_names->angular_velocity_random = "angular_velocity_random"; - shader_names->orbit_velocity_random = "orbit_velocity_random"; - shader_names->linear_accel_random = "linear_accel_random"; - shader_names->radial_accel_random = "radial_accel_random"; - shader_names->tangent_accel_random = "tangent_accel_random"; - shader_names->damping_random = "damping_random"; - shader_names->scale_random = "scale_random"; - shader_names->hue_variation_random = "hue_variation_random"; - shader_names->anim_speed_random = "anim_speed_random"; - shader_names->anim_offset_random = "anim_offset_random"; + shader_names->initial_linear_velocity_min = "initial_linear_velocity_min"; + shader_names->initial_angle_min = "initial_angle_min"; + shader_names->angular_velocity_min = "angular_velocity_min"; + shader_names->orbit_velocity_min = "orbit_velocity_min"; + shader_names->linear_accel_min = "linear_accel_min"; + shader_names->radial_accel_min = "radial_accel_min"; + shader_names->tangent_accel_min = "tangent_accel_min"; + shader_names->damping_min = "damping_min"; + shader_names->scale_min = "scale_min"; + shader_names->hue_variation_min = "hue_variation_min"; + shader_names->anim_speed_min = "anim_speed_min"; + shader_names->anim_offset_min = "anim_offset_min"; + + shader_names->initial_linear_velocity_max = "initial_linear_velocity_max"; + shader_names->initial_angle_max = "initial_angle_max"; + shader_names->angular_velocity_max = "angular_velocity_max"; + shader_names->orbit_velocity_max = "orbit_velocity_max"; + shader_names->linear_accel_max = "linear_accel_max"; + shader_names->radial_accel_max = "radial_accel_max"; + shader_names->tangent_accel_max = "tangent_accel_max"; + shader_names->damping_max = "damping_max"; + shader_names->scale_max = "scale_max"; + shader_names->hue_variation_max = "hue_variation_max"; + shader_names->anim_speed_max = "anim_speed_max"; + shader_names->anim_offset_max = "anim_offset_max"; shader_names->angle_texture = "angle_texture"; shader_names->angular_velocity_texture = "angular_velocity_texture"; @@ -155,31 +155,31 @@ void ParticlesMaterial::_update_shader() { code += "uniform vec3 direction;\n"; code += "uniform float spread;\n"; code += "uniform float flatness;\n"; - code += "uniform float initial_linear_velocity;\n"; - code += "uniform float initial_angle;\n"; - code += "uniform float angular_velocity;\n"; - code += "uniform float orbit_velocity;\n"; - code += "uniform float linear_accel;\n"; - code += "uniform float radial_accel;\n"; - code += "uniform float tangent_accel;\n"; - code += "uniform float damping;\n"; - code += "uniform float scale;\n"; - code += "uniform float hue_variation;\n"; - code += "uniform float anim_speed;\n"; - code += "uniform float anim_offset;\n"; - - code += "uniform float initial_linear_velocity_random;\n"; - code += "uniform float initial_angle_random;\n"; - code += "uniform float angular_velocity_random;\n"; - code += "uniform float orbit_velocity_random;\n"; - code += "uniform float linear_accel_random;\n"; - code += "uniform float radial_accel_random;\n"; - code += "uniform float tangent_accel_random;\n"; - code += "uniform float damping_random;\n"; - code += "uniform float scale_random;\n"; - code += "uniform float hue_variation_random;\n"; - code += "uniform float anim_speed_random;\n"; - code += "uniform float anim_offset_random;\n"; + code += "uniform float initial_linear_velocity_min;\n"; + code += "uniform float initial_angle_min;\n"; + code += "uniform float angular_velocity_min;\n"; + code += "uniform float orbit_velocity_min;\n"; + code += "uniform float linear_accel_min;\n"; + code += "uniform float radial_accel_min;\n"; + code += "uniform float tangent_accel_min;\n"; + code += "uniform float damping_min;\n"; + code += "uniform float scale_min;\n"; + code += "uniform float hue_variation_min;\n"; + code += "uniform float anim_speed_min;\n"; + code += "uniform float anim_offset_min;\n"; + + code += "uniform float initial_linear_velocity_max;\n"; + code += "uniform float initial_angle_max;\n"; + code += "uniform float angular_velocity_max;\n"; + code += "uniform float orbit_velocity_max;\n"; + code += "uniform float linear_accel_max;\n"; + code += "uniform float radial_accel_max;\n"; + code += "uniform float tangent_accel_max;\n"; + code += "uniform float damping_max;\n"; + code += "uniform float scale_max;\n"; + code += "uniform float hue_variation_max;\n"; + code += "uniform float anim_speed_max;\n"; + code += "uniform float anim_offset_max;\n"; code += "uniform float lifetime_randomness;\n"; switch (emission_shape) { @@ -329,7 +329,7 @@ void ParticlesMaterial::_update_shader() { if (tex_parameters[PARAM_ANIM_OFFSET].is_valid()) { code += " float tex_anim_offset = textureLod(anim_offset_texture, vec2(0.0, 0.0), 0.0).r;\n"; } else { - code += " float tex_anim_offset = 0.0;\n"; + code += " float tex_anim_offset = 1.0;\n"; } code += " float spread_rad = spread * degree_to_rad;\n"; @@ -339,7 +339,7 @@ void ParticlesMaterial::_update_shader() { if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) { code += " float tex_linear_velocity = textureLod(linear_velocity_texture, vec2(0.0, 0.0), 0.0).r;\n"; } else { - code += " float tex_linear_velocity = 0.0;\n"; + code += " float tex_linear_velocity = 1.0;\n"; } if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { @@ -347,7 +347,7 @@ void ParticlesMaterial::_update_shader() { code += " float angle1_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad;\n"; code += " angle1_rad += direction.x != 0.0 ? atan(direction.y, direction.x) : sign(direction.y) * (pi / 2.0);\n"; code += " vec3 rot = vec3(cos(angle1_rad), sin(angle1_rad), 0.0);\n"; - code += " VELOCITY = rot * initial_linear_velocity * mix(1.0, rand_from_seed(alt_seed), initial_linear_velocity_random);\n"; + code += " VELOCITY = rot * mix(initial_linear_velocity_min,initial_linear_velocity_max, rand_from_seed(alt_seed));\n"; code += " }\n"; } else { @@ -369,16 +369,16 @@ void ParticlesMaterial::_update_shader() { code += " binormal = normalize(binormal);\n"; code += " vec3 normal = cross(binormal, direction_nrm);\n"; code += " spread_direction = binormal * spread_direction.x + normal * spread_direction.y + direction_nrm * spread_direction.z;\n"; - code += " VELOCITY = spread_direction * initial_linear_velocity * mix(1.0, rand_from_seed(alt_seed), initial_linear_velocity_random);\n"; + code += " VELOCITY = spread_direction * mix(initial_linear_velocity_min, initial_linear_velocity_max,rand_from_seed(alt_seed));\n"; code += " }\n"; } code += " }\n"; - code += " float base_angle = (initial_angle + tex_angle) * mix(1.0, angle_rand, initial_angle_random);\n"; + code += " float base_angle = (tex_angle) * mix(initial_angle_min, initial_angle_max, angle_rand);\n"; code += " CUSTOM.x = base_angle * degree_to_rad;\n"; // angle code += " CUSTOM.y = 0.0;\n"; // phase code += " CUSTOM.w = (1.0 - lifetime_randomness * rand_from_seed(alt_seed));\n"; - code += " CUSTOM.z = (anim_offset + tex_anim_offset) * mix(1.0, anim_offset_rand, anim_offset_random);\n"; // animation offset (0-1) + code += " CUSTOM.z = (tex_anim_offset) * mix(anim_offset_min, anim_offset_max, anim_offset_rand);\n"; // animation offset (0-1) code += " if (RESTART_POSITION) {\n"; @@ -471,63 +471,63 @@ void ParticlesMaterial::_update_shader() { if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) { code += " float tex_linear_velocity = textureLod(linear_velocity_texture, vec2(tv, 0.0), 0.0).r;\n"; } else { - code += " float tex_linear_velocity = 0.0;\n"; + code += " float tex_linear_velocity = 1.0;\n"; } if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { if (tex_parameters[PARAM_ORBIT_VELOCITY].is_valid()) { code += " float tex_orbit_velocity = textureLod(orbit_velocity_texture, vec2(tv, 0.0), 0.0).r;\n"; } else { - code += " float tex_orbit_velocity = 0.0;\n"; + code += " float tex_orbit_velocity = 1.0;\n"; } } if (tex_parameters[PARAM_ANGULAR_VELOCITY].is_valid()) { code += " float tex_angular_velocity = textureLod(angular_velocity_texture, vec2(tv, 0.0), 0.0).r;\n"; } else { - code += " float tex_angular_velocity = 0.0;\n"; + code += " float tex_angular_velocity = 1.0;\n"; } if (tex_parameters[PARAM_LINEAR_ACCEL].is_valid()) { code += " float tex_linear_accel = textureLod(linear_accel_texture, vec2(tv, 0.0), 0.0).r;\n"; } else { - code += " float tex_linear_accel = 0.0;\n"; + code += " float tex_linear_accel = 1.0;\n"; } if (tex_parameters[PARAM_RADIAL_ACCEL].is_valid()) { code += " float tex_radial_accel = textureLod(radial_accel_texture, vec2(tv, 0.0), 0.0).r;\n"; } else { - code += " float tex_radial_accel = 0.0;\n"; + code += " float tex_radial_accel = 1.0;\n"; } if (tex_parameters[PARAM_TANGENTIAL_ACCEL].is_valid()) { code += " float tex_tangent_accel = textureLod(tangent_accel_texture, vec2(tv, 0.0), 0.0).r;\n"; } else { - code += " float tex_tangent_accel = 0.0;\n"; + code += " float tex_tangent_accel = 1.0;\n"; } if (tex_parameters[PARAM_DAMPING].is_valid()) { code += " float tex_damping = textureLod(damping_texture, vec2(tv, 0.0), 0.0).r;\n"; } else { - code += " float tex_damping = 0.0;\n"; + code += " float tex_damping = 1.0;\n"; } if (tex_parameters[PARAM_ANGLE].is_valid()) { code += " float tex_angle = textureLod(angle_texture, vec2(tv, 0.0), 0.0).r;\n"; } else { - code += " float tex_angle = 0.0;\n"; + code += " float tex_angle = 1.0;\n"; } if (tex_parameters[PARAM_ANIM_SPEED].is_valid()) { code += " float tex_anim_speed = textureLod(anim_speed_texture, vec2(tv, 0.0), 0.0).r;\n"; } else { - code += " float tex_anim_speed = 0.0;\n"; + code += " float tex_anim_speed = 1.0;\n"; } if (tex_parameters[PARAM_ANIM_OFFSET].is_valid()) { code += " float tex_anim_offset = textureLod(anim_offset_texture, vec2(tv, 0.0), 0.0).r;\n"; } else { - code += " float tex_anim_offset = 0.0;\n"; + code += " float tex_anim_offset = 1.0;\n"; } code += " vec3 force = gravity;\n"; @@ -536,18 +536,19 @@ void ParticlesMaterial::_update_shader() { code += " pos.z = 0.0;\n"; } code += " // apply linear acceleration\n"; - code += " force += length(VELOCITY) > 0.0 ? normalize(VELOCITY) * (linear_accel + tex_linear_accel) * mix(1.0, rand_from_seed(alt_seed), linear_accel_random) : vec3(0.0);\n"; + code += " force += length(VELOCITY) > 0.0 ? normalize(VELOCITY) * tex_linear_accel * mix(linear_accel_min, linear_accel_max, rand_from_seed(alt_seed)) : vec3(0.0);\n"; code += " // apply radial acceleration\n"; code += " vec3 org = EMISSION_TRANSFORM[3].xyz;\n"; code += " vec3 diff = pos - org;\n"; - code += " force += length(diff) > 0.0 ? normalize(diff) * (radial_accel + tex_radial_accel) * mix(1.0, rand_from_seed(alt_seed), radial_accel_random) : vec3(0.0);\n"; + code += " force += length(diff) > 0.0 ? normalize(diff) * tex_radial_accel * mix(radial_accel_min, radial_accel_max, rand_from_seed(alt_seed)) : vec3(0.0);\n"; code += " // apply tangential acceleration;\n"; + code += " float tangent_accel_val = tex_tangent_accel * mix(tangent_accel_min, tangent_accel_max, rand_from_seed(alt_seed))\n;"; if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { - code += " force += length(diff.yx) > 0.0 ? vec3(normalize(diff.yx * vec2(-1.0, 1.0)), 0.0) * ((tangent_accel + tex_tangent_accel) * mix(1.0, rand_from_seed(alt_seed), tangent_accel_random)) : vec3(0.0);\n"; + code += " force += length(diff.yx) > 0.0 ? vec3(normalize(diff.yx * vec2(-1.0, 1.0)), 0.0) * tangent_accel_val : vec3(0.0);\n"; } else { code += " vec3 crossDiff = cross(normalize(diff), normalize(gravity));\n"; - code += " force += length(crossDiff) > 0.0 ? normalize(crossDiff) * ((tangent_accel + tex_tangent_accel) * mix(1.0, rand_from_seed(alt_seed), tangent_accel_random)) : vec3(0.0);\n"; + code += " force += length(crossDiff) > 0.0 ? normalize(crossDiff) * tangent_accel_val : vec3(0.0);\n"; } if (attractor_interaction_enabled) { code += " force += ATTRACTOR_FORCE;\n\n"; @@ -557,7 +558,7 @@ void ParticlesMaterial::_update_shader() { code += " VELOCITY += force * DELTA;\n"; code += " // orbit velocity\n"; if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { - code += " float orbit_amount = (orbit_velocity + tex_orbit_velocity) * mix(1.0, rand_from_seed(alt_seed), orbit_velocity_random);\n"; + code += " float orbit_amount = tex_orbit_velocity * mix(orbit_velocity_min, orbit_velocity_max, rand_from_seed(alt_seed));\n"; code += " if (orbit_amount != 0.0) {\n"; code += " float ang = orbit_amount * DELTA * pi * 2.0;\n"; code += " mat2 rot = mat2(vec2(cos(ang), -sin(ang)), vec2(sin(ang), cos(ang)));\n"; @@ -569,9 +570,10 @@ void ParticlesMaterial::_update_shader() { if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) { code += " VELOCITY = normalize(VELOCITY) * tex_linear_velocity;\n"; } - code += " if (damping + tex_damping > 0.0) {\n"; + code += " float dmp = mix(damping_min, damping_max, rand_from_seed(alt_seed));\n"; + code += " if (dmp * tex_damping > 0.0) {\n"; code += " float v = length(VELOCITY);\n"; - code += " float damp = (damping + tex_damping) * mix(1.0, rand_from_seed(alt_seed), damping_random);\n"; + code += " float damp = tex_damping * dmp;\n"; code += " v -= damp * DELTA;\n"; code += " if (v < 0.0) {\n"; code += " VELOCITY = vec3(0.0);\n"; @@ -579,26 +581,26 @@ void ParticlesMaterial::_update_shader() { code += " VELOCITY = normalize(VELOCITY) * v;\n"; code += " }\n"; code += " }\n"; - code += " float base_angle = (initial_angle + tex_angle) * mix(1.0, angle_rand, initial_angle_random);\n"; - code += " base_angle += CUSTOM.y * LIFETIME * (angular_velocity + tex_angular_velocity) * mix(1.0, rand_from_seed(alt_seed) * 2.0 - 1.0, angular_velocity_random);\n"; + code += " float base_angle = (tex_angle) * mix(initial_angle_min, initial_angle_max, rand_from_seed(alt_seed));\n"; + code += " base_angle += CUSTOM.y * LIFETIME * (tex_angular_velocity) * mix(angular_velocity_min,angular_velocity_max, rand_from_seed(alt_seed));\n"; code += " CUSTOM.x = base_angle * degree_to_rad;\n"; // angle - code += " CUSTOM.z = (anim_offset + tex_anim_offset) * mix(1.0, anim_offset_rand, anim_offset_random) + CUSTOM.y * (anim_speed + tex_anim_speed) * mix(1.0, rand_from_seed(alt_seed), anim_speed_random);\n"; // angle + code += " CUSTOM.z = (tex_anim_offset) * mix(anim_offset_min, anim_offset_max, rand_from_seed(alt_seed)) + CUSTOM.y * tex_anim_speed * mix(anim_speed_min, anim_speed_max, rand_from_seed(alt_seed));\n"; // angle // apply color // apply hue rotation if (tex_parameters[PARAM_SCALE].is_valid()) { - code += " float tex_scale = textureLod(scale_texture, vec2(tv, 0.0), 0.0).r;\n"; + code += " vec3 tex_scale = textureLod(scale_texture, vec2(tv, 0.0), 0.0).rgb;\n"; } else { - code += " float tex_scale = 1.0;\n"; + code += " vec3 tex_scale = vec3(1.0);\n"; } if (tex_parameters[PARAM_HUE_VARIATION].is_valid()) { code += " float tex_hue_variation = textureLod(hue_variation_texture, vec2(tv, 0.0), 0.0).r;\n"; } else { - code += " float tex_hue_variation = 0.0;\n"; + code += " float tex_hue_variation = 1.0;\n"; } - code += " float hue_rot_angle = (hue_variation + tex_hue_variation) * pi * 2.0 * mix(1.0, hue_rot_rand * 2.0 - 1.0, hue_variation_random);\n"; + code += " float hue_rot_angle = (tex_hue_variation) * pi * 2.0 * mix(hue_variation_min, hue_variation_max, rand_from_seed(alt_seed));\n"; code += " float hue_rot_c = cos(hue_rot_angle);\n"; code += " float hue_rot_s = sin(hue_rot_angle);\n"; code += " mat4 hue_rot_mat = mat4(vec4(0.299, 0.587, 0.114, 0.0),\n"; @@ -660,18 +662,18 @@ void ParticlesMaterial::_update_shader() { } // turn particle by rotation in Y if (particle_flags[PARTICLE_FLAG_ROTATE_Y]) { + code += " vec4 origin = TRANSFORM[3];\n"; code += " TRANSFORM = mat4(vec4(cos(CUSTOM.x), 0.0, -sin(CUSTOM.x), 0.0), vec4(0.0, 1.0, 0.0, 0.0), vec4(sin(CUSTOM.x), 0.0, cos(CUSTOM.x), 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n"; + code += " TRANSFORM[3] = origin;\n"; } } //scale by scale - code += " float base_scale = tex_scale * mix(scale, 1.0, scale_random * scale_rand);\n"; - code += " if (base_scale < 0.000001) {\n"; - code += " base_scale = 0.000001;\n"; - code += " }\n"; + code += " float base_scale = mix(scale_min, scale_max, scale_rand);\n"; + code += " base_scale = sign(base_scale) * max(abs(base_scale), 0.001);\n"; - code += " TRANSFORM[0].xyz *= base_scale;\n"; - code += " TRANSFORM[1].xyz *= base_scale;\n"; - code += " TRANSFORM[2].xyz *= base_scale;\n"; + code += " TRANSFORM[0].xyz *= base_scale * sign(tex_scale.r) * max(abs(tex_scale.r), 0.001);\n"; + code += " TRANSFORM[1].xyz *= base_scale * sign(tex_scale.g) * max(abs(tex_scale.g), 0.001);\n"; + code += " TRANSFORM[2].xyz *= base_scale * sign(tex_scale.b) * max(abs(tex_scale.b), 0.001);\n"; if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { code += " VELOCITY.z = 0.0;\n"; code += " TRANSFORM[3].z = 0.0;\n"; @@ -739,7 +741,7 @@ void ParticlesMaterial::flush_changes() { void ParticlesMaterial::_queue_shader_change() { MutexLock lock(material_mutex); - if (!element.in_list()) { + if (is_initialized && !element.in_list()) { dirty_materials->add(&element); } } @@ -777,110 +779,116 @@ float ParticlesMaterial::get_flatness() const { return flatness; } -void ParticlesMaterial::set_param(Parameter p_param, float p_value) { +void ParticlesMaterial::set_param_min(Parameter p_param, float p_value) { ERR_FAIL_INDEX(p_param, PARAM_MAX); - parameters[p_param] = p_value; + params_min[p_param] = p_value; + if (params_min[p_param] > params_max[p_param]) { + set_param_max(p_param, p_value); + } switch (p_param) { case PARAM_INITIAL_LINEAR_VELOCITY: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->initial_linear_velocity, p_value); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->initial_linear_velocity_min, p_value); } break; case PARAM_ANGULAR_VELOCITY: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->angular_velocity, p_value); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->angular_velocity_min, p_value); } break; case PARAM_ORBIT_VELOCITY: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->orbit_velocity, p_value); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->orbit_velocity_min, p_value); } break; case PARAM_LINEAR_ACCEL: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->linear_accel, p_value); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->linear_accel_min, p_value); } break; case PARAM_RADIAL_ACCEL: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->radial_accel, p_value); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->radial_accel_min, p_value); } break; case PARAM_TANGENTIAL_ACCEL: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->tangent_accel, p_value); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->tangent_accel_min, p_value); } break; case PARAM_DAMPING: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->damping, p_value); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->damping_min, p_value); } break; case PARAM_ANGLE: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->initial_angle, p_value); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->initial_angle_min, p_value); } break; case PARAM_SCALE: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->scale, p_value); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->scale_min, p_value); } break; case PARAM_HUE_VARIATION: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->hue_variation, p_value); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->hue_variation_min, p_value); } break; case PARAM_ANIM_SPEED: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_speed, p_value); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_speed_min, p_value); } break; case PARAM_ANIM_OFFSET: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset, p_value); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset_min, p_value); } break; case PARAM_MAX: break; // Can't happen, but silences warning } } -float ParticlesMaterial::get_param(Parameter p_param) const { +float ParticlesMaterial::get_param_min(Parameter p_param) const { ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0); - return parameters[p_param]; + return params_min[p_param]; } -void ParticlesMaterial::set_param_randomness(Parameter p_param, float p_value) { +void ParticlesMaterial::set_param_max(Parameter p_param, float p_value) { ERR_FAIL_INDEX(p_param, PARAM_MAX); - randomness[p_param] = p_value; + params_max[p_param] = p_value; + if (params_min[p_param] > params_max[p_param]) { + set_param_min(p_param, p_value); + } switch (p_param) { case PARAM_INITIAL_LINEAR_VELOCITY: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->initial_linear_velocity_random, p_value); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->initial_linear_velocity_max, p_value); } break; case PARAM_ANGULAR_VELOCITY: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->angular_velocity_random, p_value); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->angular_velocity_max, p_value); } break; case PARAM_ORBIT_VELOCITY: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->orbit_velocity_random, p_value); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->orbit_velocity_max, p_value); } break; case PARAM_LINEAR_ACCEL: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->linear_accel_random, p_value); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->linear_accel_max, p_value); } break; case PARAM_RADIAL_ACCEL: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->radial_accel_random, p_value); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->radial_accel_max, p_value); } break; case PARAM_TANGENTIAL_ACCEL: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->tangent_accel_random, p_value); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->tangent_accel_max, p_value); } break; case PARAM_DAMPING: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->damping_random, p_value); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->damping_max, p_value); } break; case PARAM_ANGLE: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->initial_angle_random, p_value); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->initial_angle_max, p_value); } break; case PARAM_SCALE: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->scale_random, p_value); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->scale_max, p_value); } break; case PARAM_HUE_VARIATION: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->hue_variation_random, p_value); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->hue_variation_max, p_value); } break; case PARAM_ANIM_SPEED: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_speed_random, p_value); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_speed_max, p_value); } break; case PARAM_ANIM_OFFSET: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset_random, p_value); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset_max, p_value); } break; case PARAM_MAX: break; // Can't happen, but silences warning } } -float ParticlesMaterial::get_param_randomness(Parameter p_param) const { +float ParticlesMaterial::get_param_max(Parameter p_param) const { ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0); - return randomness[p_param]; + return params_max[p_param]; } static void _adjust_curve_range(const Ref<Texture2D> &p_texture, float p_min, float p_max) { @@ -1259,11 +1267,11 @@ void ParticlesMaterial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_flatness", "amount"), &ParticlesMaterial::set_flatness); ClassDB::bind_method(D_METHOD("get_flatness"), &ParticlesMaterial::get_flatness); - ClassDB::bind_method(D_METHOD("set_param", "param", "value"), &ParticlesMaterial::set_param); - ClassDB::bind_method(D_METHOD("get_param", "param"), &ParticlesMaterial::get_param); + ClassDB::bind_method(D_METHOD("set_param_min", "param", "value"), &ParticlesMaterial::set_param_min); + ClassDB::bind_method(D_METHOD("get_param_min", "param"), &ParticlesMaterial::get_param_min); - ClassDB::bind_method(D_METHOD("set_param_randomness", "param", "randomness"), &ParticlesMaterial::set_param_randomness); - ClassDB::bind_method(D_METHOD("get_param_randomness", "param"), &ParticlesMaterial::get_param_randomness); + ClassDB::bind_method(D_METHOD("set_param_max", "param", "value"), &ParticlesMaterial::set_param_max); + ClassDB::bind_method(D_METHOD("get_param_max", "param"), &ParticlesMaterial::get_param_max); ClassDB::bind_method(D_METHOD("set_param_texture", "param", "texture"), &ParticlesMaterial::set_param_texture); ClassDB::bind_method(D_METHOD("get_param_texture", "param"), &ParticlesMaterial::get_param_texture); @@ -1369,54 +1377,54 @@ void ParticlesMaterial::_bind_methods() { ADD_GROUP("Gravity", ""); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "gravity"), "set_gravity", "get_gravity"); ADD_GROUP("Initial Velocity", "initial_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "initial_velocity", PROPERTY_HINT_RANGE, "0,1000,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_INITIAL_LINEAR_VELOCITY); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "initial_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_INITIAL_LINEAR_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "initial_velocity_min", PROPERTY_HINT_RANGE, "0,1000,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_INITIAL_LINEAR_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "initial_velocity_max", PROPERTY_HINT_RANGE, "0,1000,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_INITIAL_LINEAR_VELOCITY); ADD_GROUP("Angular Velocity", "angular_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_velocity", PROPERTY_HINT_RANGE, "-720,720,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_ANGULAR_VELOCITY); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANGULAR_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_velocity_min", PROPERTY_HINT_RANGE, "-720,720,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_ANGULAR_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_velocity_max", PROPERTY_HINT_RANGE, "-720,720,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_ANGULAR_VELOCITY); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angular_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANGULAR_VELOCITY); ADD_GROUP("Orbit Velocity", "orbit_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "orbit_velocity", PROPERTY_HINT_RANGE, "-1000,1000,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_ORBIT_VELOCITY); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "orbit_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ORBIT_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "orbit_velocity_min", PROPERTY_HINT_RANGE, "-1000,1000,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_ORBIT_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "orbit_velocity_max", PROPERTY_HINT_RANGE, "-1000,1000,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_ORBIT_VELOCITY); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "orbit_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ORBIT_VELOCITY); ADD_GROUP("Linear Accel", "linear_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_accel", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_LINEAR_ACCEL); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_LINEAR_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_accel_min", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_LINEAR_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_accel_max", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_LINEAR_ACCEL); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "linear_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_LINEAR_ACCEL); ADD_GROUP("Radial Accel", "radial_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "radial_accel", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_RADIAL_ACCEL); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "radial_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_RADIAL_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "radial_accel_min", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_RADIAL_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "radial_accel_max", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_RADIAL_ACCEL); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "radial_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_RADIAL_ACCEL); ADD_GROUP("Tangential Accel", "tangential_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "tangential_accel", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_TANGENTIAL_ACCEL); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "tangential_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_TANGENTIAL_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "tangential_accel_min", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_TANGENTIAL_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "tangential_accel_max", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_TANGENTIAL_ACCEL); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "tangential_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_TANGENTIAL_ACCEL); ADD_GROUP("Damping", ""); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "damping", PROPERTY_HINT_RANGE, "0,100,0.01,or_greater"), "set_param", "get_param", PARAM_DAMPING); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "damping_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_DAMPING); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "damping_min", PROPERTY_HINT_RANGE, "0,100,0.01,or_greater"), "set_param_min", "get_param_min", PARAM_DAMPING); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "damping_max", PROPERTY_HINT_RANGE, "0,100,0.01,or_greater"), "set_param_max", "get_param_max", PARAM_DAMPING); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "damping_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_DAMPING); ADD_GROUP("Angle", ""); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angle", PROPERTY_HINT_RANGE, "-720,720,0.1,or_lesser,or_greater,degrees"), "set_param", "get_param", PARAM_ANGLE); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angle_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANGLE); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angle_min", PROPERTY_HINT_RANGE, "-720,720,0.1,or_lesser,or_greater,degrees"), "set_param_min", "get_param_min", PARAM_ANGLE); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angle_max", PROPERTY_HINT_RANGE, "-720,720,0.1,or_lesser,or_greater,degrees"), "set_param_max", "get_param_max", PARAM_ANGLE); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angle_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANGLE); ADD_GROUP("Scale", ""); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "scale", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param", "get_param", PARAM_SCALE); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "scale_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_SCALE); - ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "scale_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_SCALE); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "scale_min", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param_min", "get_param_min", PARAM_SCALE); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "scale_max", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param_max", "get_param_max", PARAM_SCALE); + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "scale_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture,CurveXYZTexture"), "set_param_texture", "get_param_texture", PARAM_SCALE); ADD_GROUP("Color", ""); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_ramp", PROPERTY_HINT_RESOURCE_TYPE, "GradientTexture"), "set_color_ramp", "get_color_ramp"); ADD_GROUP("Hue Variation", "hue_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "hue_variation", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_param", "get_param", PARAM_HUE_VARIATION); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "hue_variation_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_HUE_VARIATION); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "hue_variation_min", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_param_min", "get_param_min", PARAM_HUE_VARIATION); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "hue_variation_max", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_param_max", "get_param_max", PARAM_HUE_VARIATION); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "hue_variation_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_HUE_VARIATION); ADD_GROUP("Animation", "anim_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater"), "set_param", "get_param", PARAM_ANIM_SPEED); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANIM_SPEED); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed_min", PROPERTY_HINT_RANGE, "0,16,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_ANIM_SPEED); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed_max", PROPERTY_HINT_RANGE, "0,16,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_ANIM_SPEED); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_speed_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANIM_SPEED); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_ANIM_OFFSET); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANIM_OFFSET); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_min", PROPERTY_HINT_RANGE, "0,16,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_ANIM_OFFSET); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_max", PROPERTY_HINT_RANGE, "0,16,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_ANIM_OFFSET); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_offset_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANIM_OFFSET); ADD_GROUP("Sub Emitter", "sub_emitter_"); @@ -1472,18 +1480,30 @@ ParticlesMaterial::ParticlesMaterial() : set_direction(Vector3(1, 0, 0)); set_spread(45); set_flatness(0); - set_param(PARAM_INITIAL_LINEAR_VELOCITY, 0); - set_param(PARAM_ANGULAR_VELOCITY, 0); - set_param(PARAM_ORBIT_VELOCITY, 0); - set_param(PARAM_LINEAR_ACCEL, 0); - set_param(PARAM_RADIAL_ACCEL, 0); - set_param(PARAM_TANGENTIAL_ACCEL, 0); - set_param(PARAM_DAMPING, 0); - set_param(PARAM_ANGLE, 0); - set_param(PARAM_SCALE, 1); - set_param(PARAM_HUE_VARIATION, 0); - set_param(PARAM_ANIM_SPEED, 0); - set_param(PARAM_ANIM_OFFSET, 0); + set_param_min(PARAM_INITIAL_LINEAR_VELOCITY, 0); + set_param_min(PARAM_ANGULAR_VELOCITY, 0); + set_param_min(PARAM_ORBIT_VELOCITY, 0); + set_param_min(PARAM_LINEAR_ACCEL, 0); + set_param_min(PARAM_RADIAL_ACCEL, 0); + set_param_min(PARAM_TANGENTIAL_ACCEL, 0); + set_param_min(PARAM_DAMPING, 0); + set_param_min(PARAM_ANGLE, 0); + set_param_min(PARAM_SCALE, 1); + set_param_min(PARAM_HUE_VARIATION, 0); + set_param_min(PARAM_ANIM_SPEED, 0); + set_param_min(PARAM_ANIM_OFFSET, 0); + set_param_max(PARAM_INITIAL_LINEAR_VELOCITY, 0); + set_param_max(PARAM_ANGULAR_VELOCITY, 0); + set_param_max(PARAM_ORBIT_VELOCITY, 0); + set_param_max(PARAM_LINEAR_ACCEL, 0); + set_param_max(PARAM_RADIAL_ACCEL, 0); + set_param_max(PARAM_TANGENTIAL_ACCEL, 0); + set_param_max(PARAM_DAMPING, 0); + set_param_max(PARAM_ANGLE, 0); + set_param_max(PARAM_SCALE, 1); + set_param_max(PARAM_HUE_VARIATION, 0); + set_param_max(PARAM_ANIM_SPEED, 0); + set_param_max(PARAM_ANIM_OFFSET, 0); set_emission_shape(EMISSION_SHAPE_POINT); set_emission_sphere_radius(1); set_emission_box_extents(Vector3(1, 1, 1)); @@ -1505,10 +1525,6 @@ ParticlesMaterial::ParticlesMaterial() : set_collision_friction(0.0); set_collision_use_scale(false); - for (int i = 0; i < PARAM_MAX; i++) { - set_param_randomness(Parameter(i), 0); - } - for (int i = 0; i < PARTICLE_FLAG_MAX; i++) { particle_flags[i] = false; } @@ -1517,6 +1533,7 @@ ParticlesMaterial::ParticlesMaterial() : current_key.invalid_key = 1; + is_initialized = true; _queue_shader_change(); } diff --git a/scene/resources/particles_material.h b/scene/resources/particles_material.h index 1e1821024e..36bc456978 100644 --- a/scene/resources/particles_material.h +++ b/scene/resources/particles_material.h @@ -154,31 +154,31 @@ private: StringName direction; StringName spread; StringName flatness; - StringName initial_linear_velocity; - StringName initial_angle; - StringName angular_velocity; - StringName orbit_velocity; - StringName linear_accel; - StringName radial_accel; - StringName tangent_accel; - StringName damping; - StringName scale; - StringName hue_variation; - StringName anim_speed; - StringName anim_offset; - - StringName initial_linear_velocity_random; - StringName initial_angle_random; - StringName angular_velocity_random; - StringName orbit_velocity_random; - StringName linear_accel_random; - StringName radial_accel_random; - StringName tangent_accel_random; - StringName damping_random; - StringName scale_random; - StringName hue_variation_random; - StringName anim_speed_random; - StringName anim_offset_random; + StringName initial_linear_velocity_min; + StringName initial_angle_min; + StringName angular_velocity_min; + StringName orbit_velocity_min; + StringName linear_accel_min; + StringName radial_accel_min; + StringName tangent_accel_min; + StringName damping_min; + StringName scale_min; + StringName hue_variation_min; + StringName anim_speed_min; + StringName anim_offset_min; + + StringName initial_linear_velocity_max; + StringName initial_angle_max; + StringName angular_velocity_max; + StringName orbit_velocity_max; + StringName linear_accel_max; + StringName radial_accel_max; + StringName tangent_accel_max; + StringName damping_max; + StringName scale_max; + StringName hue_variation_max; + StringName anim_speed_max; + StringName anim_offset_max; StringName angle_texture; StringName angular_velocity_texture; @@ -226,12 +226,13 @@ private: _FORCE_INLINE_ void _queue_shader_change(); _FORCE_INLINE_ bool _is_shader_dirty() const; + bool is_initialized = false; Vector3 direction; float spread; float flatness; - float parameters[PARAM_MAX]; - float randomness[PARAM_MAX]; + float params_min[PARAM_MAX]; + float params_max[PARAM_MAX]; Ref<Texture2D> tex_parameters[PARAM_MAX]; Color color; @@ -283,11 +284,11 @@ public: void set_flatness(float p_flatness); float get_flatness() const; - void set_param(Parameter p_param, float p_value); - float get_param(Parameter p_param) const; + void set_param_min(Parameter p_param, float p_value); + float get_param_min(Parameter p_param) const; - void set_param_randomness(Parameter p_param, float p_value); - float get_param_randomness(Parameter p_param) const; + void set_param_max(Parameter p_param, float p_value); + float get_param_max(Parameter p_param) const; void set_param_texture(Parameter p_param, const Ref<Texture2D> &p_texture); Ref<Texture2D> get_param_texture(Parameter p_param) const; diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp index ba85ea4a6c..f8be00f5fb 100644 --- a/scene/resources/primitive_meshes.cpp +++ b/scene/resources/primitive_meshes.cpp @@ -1390,6 +1390,12 @@ void QuadMesh::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "center_offset"), "set_center_offset", "get_center_offset"); } +uint32_t QuadMesh::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; +} + QuadMesh::QuadMesh() { primitive_type = PRIMITIVE_TRIANGLES; } @@ -1420,6 +1426,8 @@ void SphereMesh::_create_mesh_array(Array &p_arr) const { int i, j, prevrow, thisrow, point; float x, y, z; + float scale = height * (is_hemisphere ? 1.0 : 0.5); + // set our bounding box Vector<Vector3> points; @@ -1443,7 +1451,7 @@ void SphereMesh::_create_mesh_array(Array &p_arr) const { v /= (rings + 1); w = sin(Math_PI * v); - y = height * (is_hemisphere ? 1.0 : 0.5) * cos(Math_PI * v); + y = scale * cos(Math_PI * v); for (i = 0; i <= radial_segments; i++) { float u = i; @@ -1458,7 +1466,8 @@ void SphereMesh::_create_mesh_array(Array &p_arr) const { } else { Vector3 p = Vector3(x * radius * w, y, z * radius * w); points.push_back(p); - normals.push_back(p.normalized()); + 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)); diff --git a/scene/resources/primitive_meshes.h b/scene/resources/primitive_meshes.h index 7915cb0028..d447dad97a 100644 --- a/scene/resources/primitive_meshes.h +++ b/scene/resources/primitive_meshes.h @@ -285,6 +285,8 @@ protected: virtual void _create_mesh_array(Array &p_arr) const override; public: + virtual uint32_t surface_get_format(int p_idx) const override; + QuadMesh(); void set_size(const Size2 &p_size); diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp index dbe118a262..77d915aef9 100644 --- a/scene/resources/resource_format_text.cpp +++ b/scene/resources/resource_format_text.cpp @@ -424,7 +424,7 @@ Error ResourceLoaderText::load() { } } - if (path.find("://") == -1 && path.is_rel_path()) { + if (path.find("://") == -1 && path.is_relative_path()) { // path is relative to file being loaded, so convert to a resource path path = ProjectSettings::get_singleton()->localize_path(local_path.get_base_dir().plus_file(path)); } @@ -768,7 +768,7 @@ void ResourceLoaderText::get_dependencies(FileAccess *p_f, List<String> *p_depen } } - if (!using_uid && path.find("://") == -1 && path.is_rel_path()) { + if (!using_uid && path.find("://") == -1 && path.is_relative_path()) { // path is relative to file being loaded, so convert to a resource path path = ProjectSettings::get_singleton()->localize_path(local_path.get_base_dir().plus_file(path)); } @@ -1653,55 +1653,55 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r #ifdef TOOLS_ENABLED // Keep order from cached ids. Set<String> cached_ids_found; - for (Map<RES, String>::Element *E = external_resources.front(); E; E = E->next()) { - String cached_id = E->key()->get_id_for_path(local_path); + for (KeyValue<RES, String> &E : external_resources) { + String cached_id = E.key->get_id_for_path(local_path); if (cached_id == "" || cached_ids_found.has(cached_id)) { - int sep_pos = E->get().find("_"); + int sep_pos = E.value.find("_"); if (sep_pos != -1) { - E->get() = E->get().substr(0, sep_pos + 1); // Keep the order found, for improved thread loading performance. + E.value = E.value.substr(0, sep_pos + 1); // Keep the order found, for improved thread loading performance. } else { - E->get() = ""; + E.value = ""; } } else { - E->get() = cached_id; + E.value = cached_id; cached_ids_found.insert(cached_id); } } // Create IDs for non cached resources. - for (Map<RES, String>::Element *E = external_resources.front(); E; E = E->next()) { - if (cached_ids_found.has(E->get())) { // Already cached, go on. + for (KeyValue<RES, String> &E : external_resources) { + if (cached_ids_found.has(E.value)) { // Already cached, go on. continue; } String attempt; while (true) { - attempt = E->get() + Resource::generate_scene_unique_id(); + attempt = E.value + Resource::generate_scene_unique_id(); if (!cached_ids_found.has(attempt)) { break; } } cached_ids_found.insert(attempt); - E->get() = attempt; + E.value = attempt; // Update also in resource. - Ref<Resource> res = E->key(); + Ref<Resource> res = E.key; res->set_id_for_path(local_path, attempt); } #else // Make sure to start from one, as it makes format more readable. int counter = 1; - for (Map<RES, String>::Element *E = external_resources.front(); E; E = E->next()) { - E->get() = itos(counter++); + for (KeyValue<RES, String> &E : external_resources) { + E.value = itos(counter++); } #endif Vector<ResourceSort> sorted_er; - for (Map<RES, String>::Element *E = external_resources.front(); E; E = E->next()) { + for (const KeyValue<RES, String> &E : external_resources) { ResourceSort rs; - rs.resource = E->key(); - rs.id = E->get(); + rs.resource = E.key; + rs.id = E.value; sorted_er.push_back(rs); } @@ -1849,10 +1849,16 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r } if (groups.size()) { + // Write all groups on the same line as they're part of a section header. + // This improves readability while not impacting VCS friendliness too much, + // since it's rare to have more than 5 groups assigned to a single node. groups.sort_custom<StringName::AlphCompare>(); - String sgroups = " groups=[\n"; + String sgroups = " groups=["; for (int j = 0; j < groups.size(); j++) { - sgroups += "\"" + String(groups[j]).c_escape() + "\",\n"; + sgroups += "\"" + String(groups[j]).c_escape() + "\""; + if (j < groups.size() - 1) { + sgroups += ", "; + } } sgroups += "]"; header += sgroups; diff --git a/scene/resources/separation_ray_shape_2d.cpp b/scene/resources/separation_ray_shape_2d.cpp new file mode 100644 index 0000000000..0acd6d268d --- /dev/null +++ b/scene/resources/separation_ray_shape_2d.cpp @@ -0,0 +1,119 @@ +/*************************************************************************/ +/* separation_ray_shape_2d.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "separation_ray_shape_2d.h" + +#include "servers/physics_server_2d.h" +#include "servers/rendering_server.h" + +void SeparationRayShape2D::_update_shape() { + Dictionary d; + d["length"] = length; + d["slide_on_slope"] = slide_on_slope; + PhysicsServer2D::get_singleton()->shape_set_data(get_rid(), d); + emit_changed(); +} + +void SeparationRayShape2D::draw(const RID &p_to_rid, const Color &p_color) { + const Vector2 target_position = Vector2(0, get_length()); + + const float max_arrow_size = 6; + const float line_width = 1.4; + bool no_line = target_position.length() < line_width; + float arrow_size = CLAMP(target_position.length() * 2 / 3, line_width, max_arrow_size); + + if (no_line) { + arrow_size = target_position.length(); + } else { + RS::get_singleton()->canvas_item_add_line(p_to_rid, Vector2(), target_position - target_position.normalized() * arrow_size, p_color, line_width); + } + + Transform2D xf; + xf.rotate(target_position.angle()); + xf.translate(Vector2(no_line ? 0 : target_position.length() - arrow_size, 0)); + + Vector<Vector2> pts; + pts.push_back(xf.xform(Vector2(arrow_size, 0))); + pts.push_back(xf.xform(Vector2(0, 0.5 * arrow_size))); + pts.push_back(xf.xform(Vector2(0, -0.5 * arrow_size))); + + Vector<Color> cols; + for (int i = 0; i < 3; i++) { + cols.push_back(p_color); + } + + RS::get_singleton()->canvas_item_add_primitive(p_to_rid, pts, cols, Vector<Point2>(), RID()); +} + +Rect2 SeparationRayShape2D::get_rect() const { + Rect2 rect; + rect.position = Vector2(); + rect.expand_to(Vector2(0, length)); + rect = rect.grow(Math_SQRT12 * 4); + return rect; +} + +real_t SeparationRayShape2D::get_enclosing_radius() const { + return length; +} + +void SeparationRayShape2D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_length", "length"), &SeparationRayShape2D::set_length); + ClassDB::bind_method(D_METHOD("get_length"), &SeparationRayShape2D::get_length); + + ClassDB::bind_method(D_METHOD("set_slide_on_slope", "active"), &SeparationRayShape2D::set_slide_on_slope); + ClassDB::bind_method(D_METHOD("get_slide_on_slope"), &SeparationRayShape2D::get_slide_on_slope); + + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "length"), "set_length", "get_length"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slide_on_slope"), "set_slide_on_slope", "get_slide_on_slope"); +} + +void SeparationRayShape2D::set_length(real_t p_length) { + length = p_length; + _update_shape(); +} + +real_t SeparationRayShape2D::get_length() const { + return length; +} + +void SeparationRayShape2D::set_slide_on_slope(bool p_active) { + slide_on_slope = p_active; + _update_shape(); +} + +bool SeparationRayShape2D::get_slide_on_slope() const { + return slide_on_slope; +} + +SeparationRayShape2D::SeparationRayShape2D() : + Shape2D(PhysicsServer2D::get_singleton()->separation_ray_shape_create()) { + _update_shape(); +} diff --git a/scene/resources/separation_ray_shape_2d.h b/scene/resources/separation_ray_shape_2d.h new file mode 100644 index 0000000000..5b74e6c727 --- /dev/null +++ b/scene/resources/separation_ray_shape_2d.h @@ -0,0 +1,61 @@ +/*************************************************************************/ +/* separation_ray_shape_2d.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SEPARATION_RAY_SHAPE_2D_H +#define SEPARATION_RAY_SHAPE_2D_H + +#include "scene/resources/shape_2d.h" + +class SeparationRayShape2D : public Shape2D { + GDCLASS(SeparationRayShape2D, Shape2D); + + real_t length = 20.0; + bool slide_on_slope = false; + + void _update_shape(); + +protected: + static void _bind_methods(); + +public: + void set_length(real_t p_length); + real_t get_length() const; + + void set_slide_on_slope(bool p_active); + bool get_slide_on_slope() const; + + virtual void draw(const RID &p_to_rid, const Color &p_color) override; + virtual Rect2 get_rect() const override; + virtual real_t get_enclosing_radius() const override; + + SeparationRayShape2D(); +}; + +#endif // SEPARATION_RAY_SHAPE_2D_H diff --git a/scene/resources/separation_ray_shape_3d.cpp b/scene/resources/separation_ray_shape_3d.cpp new file mode 100644 index 0000000000..376e04c844 --- /dev/null +++ b/scene/resources/separation_ray_shape_3d.cpp @@ -0,0 +1,91 @@ +/*************************************************************************/ +/* separation_ray_shape_3d.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "separation_ray_shape_3d.h" + +#include "servers/physics_server_3d.h" + +Vector<Vector3> SeparationRayShape3D::get_debug_mesh_lines() const { + Vector<Vector3> points; + points.push_back(Vector3()); + points.push_back(Vector3(0, 0, get_length())); + + return points; +} + +real_t SeparationRayShape3D::get_enclosing_radius() const { + return length; +} + +void SeparationRayShape3D::_update_shape() { + Dictionary d; + d["length"] = length; + d["slide_on_slope"] = slide_on_slope; + PhysicsServer3D::get_singleton()->shape_set_data(get_shape(), d); + Shape3D::_update_shape(); +} + +void SeparationRayShape3D::set_length(float p_length) { + length = p_length; + _update_shape(); + notify_change_to_owners(); +} + +float SeparationRayShape3D::get_length() const { + return length; +} + +void SeparationRayShape3D::set_slide_on_slope(bool p_active) { + slide_on_slope = p_active; + _update_shape(); + notify_change_to_owners(); +} + +bool SeparationRayShape3D::get_slide_on_slope() const { + return slide_on_slope; +} + +void SeparationRayShape3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_length", "length"), &SeparationRayShape3D::set_length); + ClassDB::bind_method(D_METHOD("get_length"), &SeparationRayShape3D::get_length); + + ClassDB::bind_method(D_METHOD("set_slide_on_slope", "active"), &SeparationRayShape3D::set_slide_on_slope); + ClassDB::bind_method(D_METHOD("get_slide_on_slope"), &SeparationRayShape3D::get_slide_on_slope); + + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "length", PROPERTY_HINT_RANGE, "0,4096,0.001"), "set_length", "get_length"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slide_on_slope"), "set_slide_on_slope", "get_slide_on_slope"); +} + +SeparationRayShape3D::SeparationRayShape3D() : + Shape3D(PhysicsServer3D::get_singleton()->shape_create(PhysicsServer3D::SHAPE_SEPARATION_RAY)) { + /* Code copied from setters to prevent the use of uninitialized variables */ + _update_shape(); + notify_change_to_owners(); +} diff --git a/scene/resources/separation_ray_shape_3d.h b/scene/resources/separation_ray_shape_3d.h new file mode 100644 index 0000000000..54058b6095 --- /dev/null +++ b/scene/resources/separation_ray_shape_3d.h @@ -0,0 +1,56 @@ +/*************************************************************************/ +/* separation_ray_shape_3d.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SEPARATION_RAY_SHAPE_H +#define SEPARATION_RAY_SHAPE_H +#include "scene/resources/shape_3d.h" + +class SeparationRayShape3D : public Shape3D { + GDCLASS(SeparationRayShape3D, Shape3D); + float length = 1.0; + bool slide_on_slope = false; + +protected: + static void _bind_methods(); + virtual void _update_shape() override; + +public: + void set_length(float p_length); + float get_length() const; + + void set_slide_on_slope(bool p_active); + bool get_slide_on_slope() const; + + virtual Vector<Vector3> get_debug_mesh_lines() const override; + virtual real_t get_enclosing_radius() const override; + + SeparationRayShape3D(); +}; +#endif // SEPARATION_RAY_SHAPE_H diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp index 44d524f142..242e20f3b0 100644 --- a/scene/resources/shader.cpp +++ b/scene/resources/shader.cpp @@ -116,8 +116,8 @@ Ref<Texture2D> Shader::get_default_texture_param(const StringName &p_param) cons } void Shader::get_default_texture_param_list(List<StringName> *r_textures) const { - for (const Map<StringName, Ref<Texture2D>>::Element *E = default_textures.front(); E; E = E->next()) { - r_textures->push_back(E->key()); + for (const KeyValue<StringName, Ref<Texture2D>> &E : default_textures) { + r_textures->push_back(E.key); } } diff --git a/scene/resources/skeleton_modification_2d.cpp b/scene/resources/skeleton_modification_2d.cpp index 2d5b42ddf4..7ac40b497d 100644 --- a/scene/resources/skeleton_modification_2d.cpp +++ b/scene/resources/skeleton_modification_2d.cpp @@ -44,7 +44,7 @@ /////////////////////////////////////// void SkeletonModification2D::_execute(float p_delta) { - call("_execute", p_delta); + GDVIRTUAL_CALL(_execute, p_delta); if (!enabled) { return; @@ -59,11 +59,11 @@ void SkeletonModification2D::_setup_modification(SkeletonModificationStack2D *p_ WARN_PRINT("Could not setup modification with name " + get_name()); } - call("_setup_modification", p_stack); + GDVIRTUAL_CALL(_setup_modification, Ref<SkeletonModificationStack2D>(p_stack)); } void SkeletonModification2D::_draw_editor_gizmo() { - call("_draw_editor_gizmo"); + GDVIRTUAL_CALL(_draw_editor_gizmo); } void SkeletonModification2D::set_enabled(bool p_enabled) { @@ -96,37 +96,25 @@ float SkeletonModification2D::clamp_angle(float p_angle, float p_min_bound, floa p_max_bound = Math_TAU + p_max_bound; } if (p_min_bound > p_max_bound) { - float tmp = p_min_bound; - p_min_bound = p_max_bound; - p_max_bound = tmp; + SWAP(p_min_bound, p_max_bound); } + bool is_beyond_bounds = (p_angle < p_min_bound || p_angle > p_max_bound); + bool is_within_bounds = (p_angle > p_min_bound && p_angle < p_max_bound); + // Note: May not be the most optimal way to clamp, but it always constraints to the nearest angle. - if (p_invert == false) { - if (p_angle < p_min_bound || p_angle > p_max_bound) { - Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound)); - Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound)); - Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle)); - - if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) { - p_angle = p_min_bound; - } else { - p_angle = p_max_bound; - } - } - } else { - if (p_angle > p_min_bound && p_angle < p_max_bound) { - Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound)); - Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound)); - Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle)); + if ((!p_invert && is_beyond_bounds) || (p_invert && is_within_bounds)) { + Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound)); + Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound)); + Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle)); - if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) { - p_angle = p_min_bound; - } else { - p_angle = p_max_bound; - } + if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) { + p_angle = p_min_bound; + } else { + p_angle = p_max_bound; } } + return p_angle; } @@ -152,9 +140,7 @@ void SkeletonModification2D::editor_draw_angle_constraints(Bone2D *p_operation_b arc_angle_max = (Math_PI * 2) + arc_angle_max; } if (arc_angle_min > arc_angle_max) { - float tmp = arc_angle_min; - arc_angle_min = arc_angle_max; - arc_angle_max = tmp; + SWAP(arc_angle_min, arc_angle_max); } arc_angle_min += p_operation_bone->get_bone_angle(); arc_angle_max += p_operation_bone->get_bone_angle(); @@ -228,9 +214,9 @@ bool SkeletonModification2D::get_editor_draw_gizmo() const { } void SkeletonModification2D::_bind_methods() { - BIND_VMETHOD(MethodInfo("_execute", PropertyInfo(Variant::FLOAT, "delta"))); - BIND_VMETHOD(MethodInfo("_setup_modification", PropertyInfo(Variant::OBJECT, "modification_stack", PROPERTY_HINT_RESOURCE_TYPE, "SkeletonModificationStack2D"))); - BIND_VMETHOD(MethodInfo("_draw_editor_gizmo")); + GDVIRTUAL_BIND(_execute, "delta"); + GDVIRTUAL_BIND(_setup_modification, "modification_stack") + GDVIRTUAL_BIND(_draw_editor_gizmo) ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &SkeletonModification2D::set_enabled); ClassDB::bind_method(D_METHOD("get_enabled"), &SkeletonModification2D::get_enabled); diff --git a/scene/resources/skeleton_modification_2d.h b/scene/resources/skeleton_modification_2d.h index 18633e55cb..aaddb9136e 100644 --- a/scene/resources/skeleton_modification_2d.h +++ b/scene/resources/skeleton_modification_2d.h @@ -57,6 +57,10 @@ protected: bool _print_execution_error(bool p_condition, String p_message); + GDVIRTUAL1(_execute, double) + GDVIRTUAL1(_setup_modification, Ref<SkeletonModificationStack2D>) + GDVIRTUAL0(_draw_editor_gizmo) + public: virtual void _execute(float _delta); virtual void _setup_modification(SkeletonModificationStack2D *p_stack); diff --git a/scene/resources/skeleton_modification_2d_fabrik.cpp b/scene/resources/skeleton_modification_2d_fabrik.cpp index 6e9429034f..3b5c555f89 100644 --- a/scene/resources/skeleton_modification_2d_fabrik.cpp +++ b/scene/resources/skeleton_modification_2d_fabrik.cpp @@ -247,7 +247,7 @@ void SkeletonModification2DFABRIK::chain_backwards() { } float current_bone2d_node_length = current_bone2d_node->get_length() * MIN(current_bone2d_node->get_global_scale().x, current_bone2d_node->get_global_scale().y); - float length = current_bone2d_node_length / (previous_pose.get_origin() - current_pose.get_origin()).length(); + float length = current_bone2d_node_length / (current_pose.get_origin().distance_to(previous_pose.get_origin())); Vector2 finish_position = previous_pose.get_origin().lerp(current_pose.get_origin(), length); current_pose.set_origin(finish_position); @@ -268,7 +268,7 @@ void SkeletonModification2DFABRIK::chain_forwards() { Transform2D next_pose = fabrik_transform_chain[i + 1]; float current_bone2d_node_length = current_bone2d_node->get_length() * MIN(current_bone2d_node->get_global_scale().x, current_bone2d_node->get_global_scale().y); - float length = current_bone2d_node_length / (current_pose.get_origin() - next_pose.get_origin()).length(); + float length = current_bone2d_node_length / (next_pose.get_origin().distance_to(current_pose.get_origin())); Vector2 finish_position = current_pose.get_origin().lerp(next_pose.get_origin(), length); current_pose.set_origin(finish_position); diff --git a/scene/resources/skeleton_modification_2d_twoboneik.cpp b/scene/resources/skeleton_modification_2d_twoboneik.cpp index 88d80a501f..4f752896a9 100644 --- a/scene/resources/skeleton_modification_2d_twoboneik.cpp +++ b/scene/resources/skeleton_modification_2d_twoboneik.cpp @@ -144,7 +144,7 @@ void SkeletonModification2DTwoBoneIK::_execute(float p_delta) { // With modifications by TwistedTwigleg Vector2 target_difference = target->get_global_position() - joint_one_bone->get_global_position(); float joint_one_to_target = target_difference.length(); - float angle_atan = Math::atan2(target_difference.y, target_difference.x); + float angle_atan = target_difference.angle(); float bone_one_length = joint_one_bone->get_length() * MIN(joint_one_bone->get_global_scale().x, joint_one_bone->get_global_scale().y); float bone_two_length = joint_two_bone->get_length() * MIN(joint_two_bone->get_global_scale().x, joint_two_bone->get_global_scale().y); diff --git a/scene/resources/skeleton_modification_3d.cpp b/scene/resources/skeleton_modification_3d.cpp index 9306ee14cd..b476952d86 100644 --- a/scene/resources/skeleton_modification_3d.cpp +++ b/scene/resources/skeleton_modification_3d.cpp @@ -32,11 +32,7 @@ #include "scene/3d/skeleton_3d.h" void SkeletonModification3D::_execute(real_t p_delta) { - if (get_script_instance()) { - if (get_script_instance()->has_method("execute")) { - get_script_instance()->call("execute", p_delta); - } - } + GDVIRTUAL_CALL(_execute, p_delta); if (!enabled) return; @@ -50,11 +46,7 @@ void SkeletonModification3D::_setup_modification(SkeletonModificationStack3D *p_ WARN_PRINT("Could not setup modification with name " + this->get_name()); } - if (get_script_instance()) { - if (get_script_instance()->has_method("setup_modification")) { - get_script_instance()->call("setup_modification", p_stack); - } - } + GDVIRTUAL_CALL(_setup_modification, Ref<SkeletonModificationStack3D>(p_stack)); } void SkeletonModification3D::set_enabled(bool p_enabled) { @@ -80,37 +72,25 @@ real_t SkeletonModification3D::clamp_angle(real_t p_angle, real_t p_min_bound, r p_max_bound = Math_TAU + p_max_bound; } if (p_min_bound > p_max_bound) { - real_t tmp = p_min_bound; - p_min_bound = p_max_bound; - p_max_bound = tmp; + SWAP(p_min_bound, p_max_bound); } + bool is_beyond_bounds = (p_angle < p_min_bound || p_angle > p_max_bound); + bool is_within_bounds = (p_angle > p_min_bound && p_angle < p_max_bound); + // Note: May not be the most optimal way to clamp, but it always constraints to the nearest angle. - if (p_invert == false) { - if (p_angle < p_min_bound || p_angle > p_max_bound) { - Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound)); - Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound)); - Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle)); - - if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) { - p_angle = p_min_bound; - } else { - p_angle = p_max_bound; - } - } - } else { - if (p_angle > p_min_bound && p_angle < p_max_bound) { - Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound)); - Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound)); - Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle)); - - if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) { - p_angle = p_min_bound; - } else { - p_angle = p_max_bound; - } + if ((!p_invert && is_beyond_bounds) || (p_invert && is_within_bounds)) { + Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound)); + Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound)); + Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle)); + + if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) { + p_angle = p_min_bound; + } else { + p_angle = p_max_bound; } } + return p_angle; } @@ -148,8 +128,8 @@ int SkeletonModification3D::get_execution_mode() const { } void SkeletonModification3D::_bind_methods() { - BIND_VMETHOD(MethodInfo("_execute", PropertyInfo(Variant::FLOAT, "delta"))); - BIND_VMETHOD(MethodInfo("_setup_modification", PropertyInfo(Variant::OBJECT, "modification_stack", PROPERTY_HINT_RESOURCE_TYPE, "SkeletonModificationStack3D"))); + GDVIRTUAL_BIND(_execute, "delta"); + GDVIRTUAL_BIND(_setup_modification, "modification_stack") ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &SkeletonModification3D::set_enabled); ClassDB::bind_method(D_METHOD("get_enabled"), &SkeletonModification3D::get_enabled); diff --git a/scene/resources/skeleton_modification_3d.h b/scene/resources/skeleton_modification_3d.h index 94ab0bf32c..fb1f3d33d1 100644 --- a/scene/resources/skeleton_modification_3d.h +++ b/scene/resources/skeleton_modification_3d.h @@ -53,6 +53,9 @@ protected: bool _print_execution_error(bool p_condition, String p_message); + GDVIRTUAL1(_execute, double) + GDVIRTUAL1(_setup_modification, Ref<SkeletonModificationStack3D>) + public: virtual void _execute(real_t p_delta); virtual void _setup_modification(SkeletonModificationStack3D *p_stack); diff --git a/scene/resources/skeleton_modification_3d_fabrik.cpp b/scene/resources/skeleton_modification_3d_fabrik.cpp index 69f75eb7b5..e615615924 100644 --- a/scene/resources/skeleton_modification_3d_fabrik.cpp +++ b/scene/resources/skeleton_modification_3d_fabrik.cpp @@ -232,7 +232,7 @@ void SkeletonModification3DFABRIK::chain_backwards() { int current_bone_idx = fabrik_data_chain[i].bone_idx; Transform3D current_trans = stack->skeleton->local_pose_to_global_pose(current_bone_idx, stack->skeleton->get_bone_local_pose_override(current_bone_idx)); - real_t length = fabrik_data_chain[i].length / (next_bone_trans.origin - current_trans.origin).length(); + real_t length = fabrik_data_chain[i].length / (current_trans.origin.distance_to(next_bone_trans.origin)); current_trans.origin = next_bone_trans.origin.lerp(current_trans.origin, length); // Apply it back to the skeleton @@ -253,7 +253,7 @@ void SkeletonModification3DFABRIK::chain_forwards() { int next_bone_idx = fabrik_data_chain[i + 1].bone_idx; Transform3D next_bone_trans = stack->skeleton->local_pose_to_global_pose(next_bone_idx, stack->skeleton->get_bone_local_pose_override(next_bone_idx)); - real_t length = fabrik_data_chain[i].length / (current_trans.origin - next_bone_trans.origin).length(); + real_t length = fabrik_data_chain[i].length / (next_bone_trans.origin.distance_to(current_trans.origin)); next_bone_trans.origin = current_trans.origin.lerp(next_bone_trans.origin, length); // Apply it back to the skeleton diff --git a/scene/resources/skeleton_modification_stack_2d.cpp b/scene/resources/skeleton_modification_stack_2d.cpp index 72c1c330ef..db9fe62b4d 100644 --- a/scene/resources/skeleton_modification_stack_2d.cpp +++ b/scene/resources/skeleton_modification_stack_2d.cpp @@ -247,7 +247,7 @@ void SkeletonModificationStack2D::_bind_methods() { ClassDB::bind_method(D_METHOD("delete_modification", "mod_idx"), &SkeletonModificationStack2D::delete_modification); ClassDB::bind_method(D_METHOD("set_modification", "mod_idx", "modification"), &SkeletonModificationStack2D::set_modification); - ClassDB::bind_method(D_METHOD("set_modification_count"), &SkeletonModificationStack2D::set_modification_count); + ClassDB::bind_method(D_METHOD("set_modification_count", "count"), &SkeletonModificationStack2D::set_modification_count); ClassDB::bind_method(D_METHOD("get_modification_count"), &SkeletonModificationStack2D::get_modification_count); ClassDB::bind_method(D_METHOD("get_is_setup"), &SkeletonModificationStack2D::get_is_setup); diff --git a/scene/resources/skeleton_modification_stack_3d.cpp b/scene/resources/skeleton_modification_stack_3d.cpp index 3fce0e5dbd..a724b732b9 100644 --- a/scene/resources/skeleton_modification_stack_3d.cpp +++ b/scene/resources/skeleton_modification_stack_3d.cpp @@ -200,7 +200,7 @@ void SkeletonModificationStack3D::_bind_methods() { ClassDB::bind_method(D_METHOD("delete_modification", "mod_idx"), &SkeletonModificationStack3D::delete_modification); ClassDB::bind_method(D_METHOD("set_modification", "mod_idx", "modification"), &SkeletonModificationStack3D::set_modification); - ClassDB::bind_method(D_METHOD("set_modification_count"), &SkeletonModificationStack3D::set_modification_count); + ClassDB::bind_method(D_METHOD("set_modification_count", "count"), &SkeletonModificationStack3D::set_modification_count); ClassDB::bind_method(D_METHOD("get_modification_count"), &SkeletonModificationStack3D::get_modification_count); ClassDB::bind_method(D_METHOD("get_is_setup"), &SkeletonModificationStack3D::get_is_setup); diff --git a/scene/resources/sky_material.cpp b/scene/resources/sky_material.cpp index 39082b6f7a..de94c92cac 100644 --- a/scene/resources/sky_material.cpp +++ b/scene/resources/sky_material.cpp @@ -125,10 +125,6 @@ float ProceduralSkyMaterial::get_sun_curve() const { return sun_curve; } -bool ProceduralSkyMaterial::_can_do_next_pass() const { - return false; -} - Shader::Mode ProceduralSkyMaterial::get_shader_mode() const { return Shader::MODE_SKY; } @@ -179,14 +175,14 @@ void ProceduralSkyMaterial::_bind_methods() { ClassDB::bind_method(D_METHOD("get_sun_curve"), &ProceduralSkyMaterial::get_sun_curve); ADD_GROUP("Sky", "sky_"); - ADD_PROPERTY(PropertyInfo(Variant::COLOR, "sky_top_color"), "set_sky_top_color", "get_sky_top_color"); - ADD_PROPERTY(PropertyInfo(Variant::COLOR, "sky_horizon_color"), "set_sky_horizon_color", "get_sky_horizon_color"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "sky_top_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_sky_top_color", "get_sky_top_color"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "sky_horizon_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_sky_horizon_color", "get_sky_horizon_color"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sky_curve", PROPERTY_HINT_EXP_EASING), "set_sky_curve", "get_sky_curve"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sky_energy", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_sky_energy", "get_sky_energy"); ADD_GROUP("Ground", "ground_"); - ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ground_bottom_color"), "set_ground_bottom_color", "get_ground_bottom_color"); - ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ground_horizon_color"), "set_ground_horizon_color", "get_ground_horizon_color"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ground_bottom_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_ground_bottom_color", "get_ground_bottom_color"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ground_horizon_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_ground_horizon_color", "get_ground_horizon_color"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ground_curve", PROPERTY_HINT_EXP_EASING), "set_ground_curve", "get_ground_curve"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ground_energy", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_ground_energy", "get_ground_energy"); @@ -312,10 +308,6 @@ Ref<Texture2D> PanoramaSkyMaterial::get_panorama() const { return panorama; } -bool PanoramaSkyMaterial::_can_do_next_pass() const { - return false; -} - Shader::Mode PanoramaSkyMaterial::get_shader_mode() const { return Shader::MODE_SKY; } @@ -482,10 +474,6 @@ Ref<Texture2D> PhysicalSkyMaterial::get_night_sky() const { return night_sky; } -bool PhysicalSkyMaterial::_can_do_next_pass() const { - return false; -} - Shader::Mode PhysicalSkyMaterial::get_shader_mode() const { return Shader::MODE_SKY; } @@ -543,16 +531,16 @@ void PhysicalSkyMaterial::_bind_methods() { ADD_GROUP("Rayleigh", "rayleigh_"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rayleigh_coefficient", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_rayleigh_coefficient", "get_rayleigh_coefficient"); - ADD_PROPERTY(PropertyInfo(Variant::COLOR, "rayleigh_color"), "set_rayleigh_color", "get_rayleigh_color"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "rayleigh_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_rayleigh_color", "get_rayleigh_color"); ADD_GROUP("Mie", "mie_"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mie_coefficient", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_mie_coefficient", "get_mie_coefficient"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mie_eccentricity", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_mie_eccentricity", "get_mie_eccentricity"); - ADD_PROPERTY(PropertyInfo(Variant::COLOR, "mie_color"), "set_mie_color", "get_mie_color"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "mie_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_mie_color", "get_mie_color"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "turbidity", PROPERTY_HINT_RANGE, "0,1000,0.01"), "set_turbidity", "get_turbidity"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sun_disk_scale", PROPERTY_HINT_RANGE, "0,360,0.01"), "set_sun_disk_scale", "get_sun_disk_scale"); - ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ground_color"), "set_ground_color", "get_ground_color"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ground_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_ground_color", "get_ground_color"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "exposure", PROPERTY_HINT_RANGE, "0,128,0.01"), "set_exposure", "get_exposure"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dither_strength", PROPERTY_HINT_RANGE, "0,10,0.01"), "set_dither_strength", "get_dither_strength"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "night_sky", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_night_sky", "get_night_sky"); diff --git a/scene/resources/sky_material.h b/scene/resources/sky_material.h index 63e730617b..daeda212d4 100644 --- a/scene/resources/sky_material.h +++ b/scene/resources/sky_material.h @@ -58,7 +58,6 @@ private: protected: static void _bind_methods(); - virtual bool _can_do_next_pass() const override; public: void set_sky_top_color(const Color &p_sky_top); @@ -117,7 +116,6 @@ private: protected: static void _bind_methods(); - virtual bool _can_do_next_pass() const override; public: void set_panorama(const Ref<Texture2D> &p_panorama); @@ -159,7 +157,6 @@ private: protected: static void _bind_methods(); - virtual bool _can_do_next_pass() const override; public: void set_rayleigh_coefficient(float p_rayleigh); diff --git a/scene/resources/sprite_frames.cpp b/scene/resources/sprite_frames.cpp index 140c6f821f..01afb00283 100644 --- a/scene/resources/sprite_frames.cpp +++ b/scene/resources/sprite_frames.cpp @@ -108,15 +108,15 @@ Vector<String> SpriteFrames::_get_animation_list() const { } void SpriteFrames::get_animation_list(List<StringName> *r_animations) const { - for (const Map<StringName, Anim>::Element *E = animations.front(); E; E = E->next()) { - r_animations->push_back(E->key()); + for (const KeyValue<StringName, Anim> &E : animations) { + r_animations->push_back(E.key); } } Vector<String> SpriteFrames::get_animation_names() const { Vector<String> names; - for (const Map<StringName, Anim>::Element *E = animations.front(); E; E = E->next()) { - names.push_back(E->key()); + for (const KeyValue<StringName, Anim> &E : animations) { + names.push_back(E.key); } names.sort(); return names; @@ -164,14 +164,14 @@ Array SpriteFrames::_get_frames() const { Array SpriteFrames::_get_animations() const { Array anims; - for (Map<StringName, Anim>::Element *E = animations.front(); E; E = E->next()) { + for (const KeyValue<StringName, Anim> &E : animations) { Dictionary d; - d["name"] = E->key(); - d["speed"] = E->get().speed; - d["loop"] = E->get().loop; + d["name"] = E.key; + d["speed"] = E.value.speed; + d["loop"] = E.value.loop; Array frames; - for (int i = 0; i < E->get().frames.size(); i++) { - frames.push_back(E->get().frames[i]); + for (int i = 0; i < E.value.frames.size(); i++) { + frames.push_back(E.value.frames[i]); } d["frames"] = frames; anims.push_back(d); diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp index 875aa30824..a8cd872408 100644 --- a/scene/resources/surface_tool.cpp +++ b/scene/resources/surface_tool.cpp @@ -409,7 +409,7 @@ Array SurfaceTool::commit_to_arrays() { for (uint32_t idx = 0; idx < vertex_array.size(); idx++) { const Vertex &v = vertex_array[idx]; - const Color &c = v.custom[idx]; + const Color &c = v.custom[fmt]; w[idx * 4 + 0] = CLAMP(int32_t(c.r * 255.0), 0, 255); w[idx * 4 + 1] = CLAMP(int32_t(c.g * 255.0), 0, 255); w[idx * 4 + 2] = CLAMP(int32_t(c.b * 255.0), 0, 255); @@ -426,7 +426,7 @@ Array SurfaceTool::commit_to_arrays() { for (uint32_t idx = 0; idx < vertex_array.size(); idx++) { const Vertex &v = vertex_array[idx]; - const Color &c = v.custom[idx]; + const Color &c = v.custom[fmt]; w[idx * 4 + 0] = uint8_t(int8_t(CLAMP(int32_t(c.r * 127.0), -128, 127))); w[idx * 4 + 1] = uint8_t(int8_t(CLAMP(int32_t(c.g * 127.0), -128, 127))); w[idx * 4 + 2] = uint8_t(int8_t(CLAMP(int32_t(c.b * 127.0), -128, 127))); @@ -443,7 +443,7 @@ Array SurfaceTool::commit_to_arrays() { for (uint32_t idx = 0; idx < vertex_array.size(); idx++) { const Vertex &v = vertex_array[idx]; - const Color &c = v.custom[idx]; + const Color &c = v.custom[fmt]; w[idx * 2 + 0] = Math::make_half_float(c.r); w[idx * 2 + 1] = Math::make_half_float(c.g); } @@ -458,7 +458,7 @@ Array SurfaceTool::commit_to_arrays() { for (uint32_t idx = 0; idx < vertex_array.size(); idx++) { const Vertex &v = vertex_array[idx]; - const Color &c = v.custom[idx]; + const Color &c = v.custom[fmt]; w[idx * 4 + 0] = Math::make_half_float(c.r); w[idx * 4 + 1] = Math::make_half_float(c.g); w[idx * 4 + 2] = Math::make_half_float(c.b); @@ -475,7 +475,7 @@ Array SurfaceTool::commit_to_arrays() { for (uint32_t idx = 0; idx < vertex_array.size(); idx++) { const Vertex &v = vertex_array[idx]; - const Color &c = v.custom[idx]; + const Color &c = v.custom[fmt]; w[idx] = c.r; } @@ -489,7 +489,7 @@ Array SurfaceTool::commit_to_arrays() { for (uint32_t idx = 0; idx < vertex_array.size(); idx++) { const Vertex &v = vertex_array[idx]; - const Color &c = v.custom[idx]; + const Color &c = v.custom[fmt]; w[idx * 2 + 0] = c.r; w[idx * 2 + 1] = c.g; } @@ -504,7 +504,7 @@ Array SurfaceTool::commit_to_arrays() { for (uint32_t idx = 0; idx < vertex_array.size(); idx++) { const Vertex &v = vertex_array[idx]; - const Color &c = v.custom[idx]; + const Color &c = v.custom[fmt]; w[idx * 3 + 0] = c.r; w[idx * 3 + 1] = c.g; w[idx * 3 + 2] = c.b; @@ -520,7 +520,7 @@ Array SurfaceTool::commit_to_arrays() { for (uint32_t idx = 0; idx < vertex_array.size(); idx++) { const Vertex &v = vertex_array[idx]; - const Color &c = v.custom[idx]; + const Color &c = v.custom[fmt]; w[idx * 4 + 0] = c.r; w[idx * 4 + 1] = c.g; w[idx * 4 + 2] = c.b; @@ -679,6 +679,9 @@ void SurfaceTool::_create_list(const Ref<Mesh> &p_existing, int p_surface, Local _create_list_from_arrays(arr, r_vertex, r_index, lformat); } +static const uint32_t custom_mask[RS::ARRAY_CUSTOM_COUNT] = { Mesh::ARRAY_FORMAT_CUSTOM0, Mesh::ARRAY_FORMAT_CUSTOM1, Mesh::ARRAY_FORMAT_CUSTOM2, Mesh::ARRAY_FORMAT_CUSTOM3 }; +static const uint32_t custom_shift[RS::ARRAY_CUSTOM_COUNT] = { Mesh::ARRAY_FORMAT_CUSTOM0_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM1_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM2_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM3_SHIFT }; + void SurfaceTool::create_vertex_array_from_triangle_arrays(const Array &p_arrays, LocalVector<SurfaceTool::Vertex> &ret, uint32_t *r_format) { ret.clear(); @@ -733,8 +736,6 @@ void SurfaceTool::create_vertex_array_from_triangle_arrays(const Array &p_arrays if (warr.size()) { lformat |= RS::ARRAY_FORMAT_WEIGHTS; } - static const uint32_t custom_mask[RS::ARRAY_CUSTOM_COUNT] = { Mesh::ARRAY_FORMAT_CUSTOM0, Mesh::ARRAY_FORMAT_CUSTOM1, Mesh::ARRAY_FORMAT_CUSTOM2, Mesh::ARRAY_FORMAT_CUSTOM3 }; - static const uint32_t custom_shift[RS::ARRAY_CUSTOM_COUNT] = { Mesh::ARRAY_FORMAT_CUSTOM0_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM1_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM2_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM3_SHIFT }; for (int i = 0; i < RS::ARRAY_CUSTOM_COUNT; i++) { ERR_CONTINUE_MSG(p_arrays[RS::ARRAY_CUSTOM0 + i].get_type() == Variant::PACKED_BYTE_ARRAY, "Extracting Byte/Half formats is not supported"); @@ -832,6 +833,12 @@ void SurfaceTool::create_from_triangle_arrays(const Array &p_arrays) { clear(); primitive = Mesh::PRIMITIVE_TRIANGLES; _create_list_from_arrays(p_arrays, &vertex_array, &index_array, format); + + for (int j = 0; j < RS::ARRAY_CUSTOM_COUNT; j++) { + if (format & custom_mask[j]) { + last_custom_format[j] = (CustomFormat)((format >> custom_shift[j]) & RS::ARRAY_FORMAT_CUSTOM_MASK); + } + } } void SurfaceTool::create_from(const Ref<Mesh> &p_existing, int p_surface) { @@ -841,6 +848,12 @@ void SurfaceTool::create_from(const Ref<Mesh> &p_existing, int p_surface) { primitive = p_existing->surface_get_primitive_type(p_surface); _create_list(p_existing, p_surface, &vertex_array, &index_array, format); material = p_existing->surface_get_material(p_surface); + + for (int j = 0; j < RS::ARRAY_CUSTOM_COUNT; j++) { + if (format & custom_mask[j]) { + last_custom_format[j] = (CustomFormat)((format >> custom_shift[j]) & RS::ARRAY_FORMAT_CUSTOM_MASK); + } + } } void SurfaceTool::create_from_blend_shape(const Ref<Mesh> &p_existing, int p_surface, const String &p_blend_shape_name) { @@ -863,6 +876,12 @@ void SurfaceTool::create_from_blend_shape(const Ref<Mesh> &p_existing, int p_sur Array mesh = arr[shape_idx]; ERR_FAIL_COND(mesh.size() != RS::ARRAY_MAX); _create_list_from_arrays(arr[shape_idx], &vertex_array, &index_array, format); + + for (int j = 0; j < RS::ARRAY_CUSTOM_COUNT; j++) { + if (format & custom_mask[j]) { + last_custom_format[j] = (CustomFormat)((format >> custom_shift[j]) & RS::ARRAY_FORMAT_CUSTOM_MASK); + } + } } void SurfaceTool::append_from(const Ref<Mesh> &p_existing, int p_surface, const Transform3D &p_xform) { @@ -878,6 +897,16 @@ void SurfaceTool::append_from(const Ref<Mesh> &p_existing, int p_surface, const LocalVector<int> nindices; _create_list(p_existing, p_surface, &nvertices, &nindices, nformat); format |= nformat; + + for (int j = 0; j < RS::ARRAY_CUSTOM_COUNT; j++) { + if (format & custom_mask[j]) { + CustomFormat new_format = (CustomFormat)((format >> custom_shift[j]) & RS::ARRAY_FORMAT_CUSTOM_MASK); + if (last_custom_format[j] != CUSTOM_MAX && last_custom_format[j] != new_format) { + WARN_PRINT(vformat("Custom %d format %d mismatch when appending format %d", j, last_custom_format[j], new_format)); + } + last_custom_format[j] = new_format; + } + } int vfrom = vertex_array.size(); for (uint32_t vi = 0; vi < nvertices.size(); vi++) { @@ -1147,6 +1176,7 @@ Vector<int> SurfaceTool::generate_lod(float p_threshold, int p_target_index_coun ERR_FAIL_COND_V(simplify_func == nullptr, lod); ERR_FAIL_COND_V(vertex_array.size() == 0, lod); ERR_FAIL_COND_V(index_array.size() == 0, lod); + ERR_FAIL_COND_V(index_array.size() % 3 != 0, lod); lod.resize(index_array.size()); LocalVector<float> vertices; //uses floats diff --git a/scene/resources/syntax_highlighter.cpp b/scene/resources/syntax_highlighter.cpp index 173ce2adce..52a3abf74d 100644 --- a/scene/resources/syntax_highlighter.cpp +++ b/scene/resources/syntax_highlighter.cpp @@ -43,12 +43,10 @@ Dictionary SyntaxHighlighter::get_line_syntax_highlighting(int p_line) { return color_map; } - ScriptInstance *si = get_script_instance(); - if (si && si->has_method("_get_line_syntax_highlighting")) { - color_map = si->call("_get_line_syntax_highlighting", p_line); - } else { - color_map = _get_line_syntax_highlighting(p_line); + if (!GDVIRTUAL_CALL(_get_line_syntax_highlighting, p_line, color_map)) { + color_map = _get_line_syntax_highlighting_impl(p_line); } + highlighting_cache[p_line] = color_map; return color_map; } @@ -69,9 +67,7 @@ void SyntaxHighlighter::_lines_edited_from(int p_from_line, int p_to_line) { void SyntaxHighlighter::clear_highlighting_cache() { highlighting_cache.clear(); - ScriptInstance *si = get_script_instance(); - if (si && si->has_method("_clear_highlighting_cache")) { - si->call("_clear_highlighting_cache"); + if (GDVIRTUAL_CALL(_clear_highlighting_cache)) { return; } _clear_highlighting_cache(); @@ -83,9 +79,7 @@ void SyntaxHighlighter::update_cache() { if (text_edit == nullptr) { return; } - ScriptInstance *si = get_script_instance(); - if (si && si->has_method("_update_cache")) { - si->call("_update_cache"); + if (GDVIRTUAL_CALL(_update_cache)) { return; } _update_cache(); @@ -115,9 +109,9 @@ void SyntaxHighlighter::_bind_methods() { ClassDB::bind_method(D_METHOD("clear_highlighting_cache"), &SyntaxHighlighter::clear_highlighting_cache); ClassDB::bind_method(D_METHOD("get_text_edit"), &SyntaxHighlighter::get_text_edit); - BIND_VMETHOD(MethodInfo(Variant::DICTIONARY, "_get_line_syntax_highlighting", PropertyInfo(Variant::INT, "line"))); - BIND_VMETHOD(MethodInfo("_clear_highlighting_cache")); - BIND_VMETHOD(MethodInfo("_update_cache")); + GDVIRTUAL_BIND(_get_line_syntax_highlighting, "line") + GDVIRTUAL_BIND(_clear_highlighting_cache) + GDVIRTUAL_BIND(_update_cache) } //////////////////////////////////////////////////////////////////////////////// @@ -130,7 +124,7 @@ static bool _is_hex_symbol(char32_t c) { return ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')); } -Dictionary CodeHighlighter::_get_line_syntax_highlighting(int p_line) { +Dictionary CodeHighlighter::_get_line_syntax_highlighting_impl(int p_line) { Dictionary color_map; bool prev_is_char = false; diff --git a/scene/resources/syntax_highlighter.h b/scene/resources/syntax_highlighter.h index f3964b0c8f..0fe39ccff6 100644 --- a/scene/resources/syntax_highlighter.h +++ b/scene/resources/syntax_highlighter.h @@ -32,6 +32,8 @@ #define SYNTAX_HIGHLIGHTER_H #include "core/io/resource.h" +#include "core/object/gdvirtual.gen.inc" +#include "core/object/script_language.h" class TextEdit; @@ -48,9 +50,12 @@ protected: static void _bind_methods(); + GDVIRTUAL1RC(Dictionary, _get_line_syntax_highlighting, int) + GDVIRTUAL0(_clear_highlighting_cache) + GDVIRTUAL0(_update_cache) public: Dictionary get_line_syntax_highlighting(int p_line); - virtual Dictionary _get_line_syntax_highlighting(int p_line) { return Dictionary(); } + virtual Dictionary _get_line_syntax_highlighting_impl(int p_line) { return Dictionary(); } void clear_highlighting_cache(); virtual void _clear_highlighting_cache() {} @@ -93,7 +98,7 @@ protected: static void _bind_methods(); public: - virtual Dictionary _get_line_syntax_highlighting(int p_line) override; + virtual Dictionary _get_line_syntax_highlighting_impl(int p_line) override; virtual void _clear_highlighting_cache() override; virtual void _update_cache() override; diff --git a/scene/resources/text_line.cpp b/scene/resources/text_line.cpp index 0807a062f2..0094518967 100644 --- a/scene/resources/text_line.cpp +++ b/scene/resources/text_line.cpp @@ -53,7 +53,7 @@ void TextLine::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "preserve_control"), "set_preserve_control", "get_preserve_control"); - ClassDB::bind_method(D_METHOD("set_bidi_override", "override"), &TextLine::_set_bidi_override); + ClassDB::bind_method(D_METHOD("set_bidi_override", "override"), &TextLine::set_bidi_override); ClassDB::bind_method(D_METHOD("add_string", "text", "fonts", "size", "opentype_features", "language"), &TextLine::add_string, DEFVAL(Dictionary()), DEFVAL("")); ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length"), &TextLine::add_object, DEFVAL(INLINE_ALIGN_CENTER), DEFVAL(1)); @@ -112,7 +112,7 @@ void TextLine::_shape() { TS->shaped_text_tab_align(rid, tab_stops); } - uint8_t overrun_flags = TextServer::OVERRUN_NO_TRIMMING; + uint16_t overrun_flags = TextServer::OVERRUN_NO_TRIMMING; if (overrun_behavior != OVERRUN_NO_TRIMMING) { switch (overrun_behavior) { case OVERRUN_TRIM_WORD_ELLIPSIS: @@ -195,15 +195,7 @@ TextServer::Orientation TextLine::get_orientation() const { return TS->shaped_text_get_orientation(rid); } -void TextLine::_set_bidi_override(const Array &p_override) { - Vector<Vector2i> overrides; - for (int i = 0; i < p_override.size(); i++) { - overrides.push_back(p_override[i]); - } - set_bidi_override(overrides); -} - -void TextLine::set_bidi_override(const Vector<Vector2i> &p_override) { +void TextLine::set_bidi_override(const Array &p_override) { TS->shaped_text_set_bidi_override(rid, p_override); dirty = true; } @@ -211,8 +203,8 @@ void TextLine::set_bidi_override(const Vector<Vector2i> &p_override) { bool TextLine::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) { ERR_FAIL_COND_V(p_fonts.is_null(), false); bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language); - spacing_top = p_fonts->get_spacing(Font::SPACING_TOP); - spacing_bottom = p_fonts->get_spacing(Font::SPACING_BOTTOM); + spacing_top = p_fonts->get_spacing(TextServer::SPACING_TOP); + spacing_bottom = p_fonts->get_spacing(TextServer::SPACING_BOTTOM); dirty = true; return res; } @@ -256,14 +248,14 @@ void TextLine::tab_align(const Vector<float> &p_tab_stops) { dirty = true; } -void TextLine::set_flags(uint8_t p_flags) { +void TextLine::set_flags(uint16_t p_flags) { if (flags != p_flags) { flags = p_flags; dirty = true; } } -uint8_t TextLine::get_flags() const { +uint16_t TextLine::get_flags() const { return flags; } @@ -409,8 +401,8 @@ int TextLine::hit_test(float p_coords) const { TextLine::TextLine(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, TextServer::Direction p_direction, TextServer::Orientation p_orientation) { rid = TS->create_shaped_text(p_direction, p_orientation); - spacing_top = p_fonts->get_spacing(Font::SPACING_TOP); - spacing_bottom = p_fonts->get_spacing(Font::SPACING_BOTTOM); + spacing_top = p_fonts->get_spacing(TextServer::SPACING_TOP); + spacing_bottom = p_fonts->get_spacing(TextServer::SPACING_BOTTOM); TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language); } diff --git a/scene/resources/text_line.h b/scene/resources/text_line.h index 9ed9c2f177..43739f27ec 100644 --- a/scene/resources/text_line.h +++ b/scene/resources/text_line.h @@ -56,7 +56,7 @@ private: bool dirty = true; float width = -1.0; - uint8_t flags = TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA; + uint16_t flags = TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA; HAlign align = HALIGN_LEFT; OverrunBehavior overrun_behavior = OVERRUN_TRIM_ELLIPSIS; @@ -75,7 +75,7 @@ public: void set_direction(TextServer::Direction p_direction); TextServer::Direction get_direction() const; - void set_bidi_override(const Vector<Vector2i> &p_override); + void set_bidi_override(const Array &p_override); void set_orientation(TextServer::Orientation p_orientation); TextServer::Orientation get_orientation() const; @@ -95,8 +95,8 @@ public: void tab_align(const Vector<float> &p_tab_stops); - void set_flags(uint8_t p_flags); - uint8_t get_flags() const; + void set_flags(uint16_t p_flags); + uint16_t get_flags() const; void set_text_overrun_behavior(OverrunBehavior p_behavior); OverrunBehavior get_text_overrun_behavior() const; @@ -120,8 +120,6 @@ public: int hit_test(float p_coords) const; - void _set_bidi_override(const Array &p_override); - TextLine(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL); TextLine(); ~TextLine(); diff --git a/scene/resources/text_paragraph.cpp b/scene/resources/text_paragraph.cpp index 357411ae04..b2e18e2451 100644 --- a/scene/resources/text_paragraph.cpp +++ b/scene/resources/text_paragraph.cpp @@ -53,7 +53,7 @@ void TextParagraph::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "preserve_control"), "set_preserve_control", "get_preserve_control"); - ClassDB::bind_method(D_METHOD("set_bidi_override", "override"), &TextParagraph::_set_bidi_override); + ClassDB::bind_method(D_METHOD("set_bidi_override", "override"), &TextParagraph::set_bidi_override); ClassDB::bind_method(D_METHOD("set_dropcap", "text", "fonts", "size", "dropcap_margins", "opentype_features", "language"), &TextParagraph::set_dropcap, DEFVAL(Rect2()), DEFVAL(Dictionary()), DEFVAL("")); ClassDB::bind_method(D_METHOD("clear_dropcap"), &TextParagraph::clear_dropcap); @@ -158,9 +158,9 @@ void TextParagraph::_shape_lines() { if (h_offset > 0) { // Dropcap, flow around. - Vector<Vector2i> line_breaks = TS->shaped_text_get_line_breaks(rid, width - h_offset, 0, flags); - for (int i = 0; i < line_breaks.size(); i++) { - RID line = TS->shaped_text_substr(rid, line_breaks[i].x, line_breaks[i].y - line_breaks[i].x); + PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(rid, width - h_offset, 0, flags); + for (int i = 0; i < line_breaks.size(); i = i + 2) { + RID line = TS->shaped_text_substr(rid, line_breaks[i], line_breaks[i + 1] - line_breaks[i]); float h = (TS->shaped_text_get_orientation(line) == TextServer::ORIENTATION_HORIZONTAL) ? TS->shaped_text_get_size(line).y : TS->shaped_text_get_size(line).x; if (v_offset < h) { TS->free(line); @@ -171,21 +171,21 @@ void TextParagraph::_shape_lines() { } dropcap_lines++; v_offset -= h; - start = line_breaks[i].y; + start = line_breaks[i + 1]; lines_rid.push_back(line); } } // Use fixed for the rest of lines. - Vector<Vector2i> line_breaks = TS->shaped_text_get_line_breaks(rid, width, start, flags); - for (int i = 0; i < line_breaks.size(); i++) { - RID line = TS->shaped_text_substr(rid, line_breaks[i].x, line_breaks[i].y - line_breaks[i].x); + PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(rid, width, start, flags); + for (int i = 0; i < line_breaks.size(); i = i + 2) { + RID line = TS->shaped_text_substr(rid, line_breaks[i], line_breaks[i + 1] - line_breaks[i]); if (!tab_stops.is_empty()) { TS->shaped_text_tab_align(line, tab_stops); } lines_rid.push_back(line); } - uint8_t overrun_flags = TextServer::OVERRUN_NO_TRIMMING; + uint16_t overrun_flags = TextServer::OVERRUN_NO_TRIMMING; if (overrun_behavior != OVERRUN_NO_TRIMMING) { switch (overrun_behavior) { case OVERRUN_TRIM_WORD_ELLIPSIS: @@ -333,8 +333,8 @@ void TextParagraph::clear_dropcap() { bool TextParagraph::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) { ERR_FAIL_COND_V(p_fonts.is_null(), false); bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language); - spacing_top = p_fonts->get_spacing(Font::SPACING_TOP); - spacing_bottom = p_fonts->get_spacing(Font::SPACING_BOTTOM); + spacing_top = p_fonts->get_spacing(TextServer::SPACING_TOP); + spacing_bottom = p_fonts->get_spacing(TextServer::SPACING_BOTTOM); lines_dirty = true; return res; } @@ -347,15 +347,7 @@ int TextParagraph::get_spacing_bottom() const { return spacing_bottom; } -void TextParagraph::_set_bidi_override(const Array &p_override) { - Vector<Vector2i> overrides; - for (int i = 0; i < p_override.size(); i++) { - overrides.push_back(p_override[i]); - } - set_bidi_override(overrides); -} - -void TextParagraph::set_bidi_override(const Vector<Vector2i> &p_override) { +void TextParagraph::set_bidi_override(const Array &p_override) { TS->shaped_text_set_bidi_override(rid, p_override); lines_dirty = true; } @@ -392,14 +384,14 @@ void TextParagraph::tab_align(const Vector<float> &p_tab_stops) { lines_dirty = true; } -void TextParagraph::set_flags(uint8_t p_flags) { +void TextParagraph::set_flags(uint16_t p_flags) { if (flags != p_flags) { flags = p_flags; lines_dirty = true; } } -uint8_t TextParagraph::get_flags() const { +uint16_t TextParagraph::get_flags() const { return flags; } @@ -437,7 +429,8 @@ Size2 TextParagraph::get_non_wraped_size() const { Size2 TextParagraph::get_size() const { const_cast<TextParagraph *>(this)->_shape_lines(); Size2 size; - for (int i = 0; i < lines_rid.size(); i++) { + int visible_lines = (max_lines_visible >= 0) ? MIN(max_lines_visible, lines_rid.size()) : lines_rid.size(); + for (int i = 0; i < visible_lines; i++) { Size2 lsize = TS->shaped_text_get_size(lines_rid[i]); if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { size.x = MAX(size.x, lsize.x); @@ -587,15 +580,15 @@ void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_colo l_width -= h_offset; } } - float length = TS->shaped_text_get_width(lines_rid[i]); + float line_width = TS->shaped_text_get_width(lines_rid[i]); if (width > 0) { switch (align) { case HALIGN_FILL: if (TS->shaped_text_get_direction(lines_rid[i]) == TextServer::DIRECTION_RTL) { if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { - ofs.x += l_width - length; + ofs.x += l_width - line_width; } else { - ofs.y += l_width - length; + ofs.y += l_width - line_width; } } break; @@ -603,16 +596,16 @@ void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_colo break; case HALIGN_CENTER: { if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { - ofs.x += Math::floor((l_width - length) / 2.0); + ofs.x += Math::floor((l_width - line_width) / 2.0); } else { - ofs.y += Math::floor((l_width - length) / 2.0); + ofs.y += Math::floor((l_width - line_width) / 2.0); } } break; case HALIGN_RIGHT: { if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { - ofs.x += l_width - length; + ofs.x += l_width - line_width; } else { - ofs.y += l_width - length; + ofs.y += l_width - line_width; } } break; } @@ -828,8 +821,8 @@ void TextParagraph::draw_line_outline(RID p_canvas, const Vector2 &p_pos, int p_ TextParagraph::TextParagraph(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, float p_width, TextServer::Direction p_direction, TextServer::Orientation p_orientation) { rid = TS->create_shaped_text(p_direction, p_orientation); TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language); - spacing_top = p_fonts->get_spacing(Font::SPACING_TOP); - spacing_bottom = p_fonts->get_spacing(Font::SPACING_BOTTOM); + spacing_top = p_fonts->get_spacing(TextServer::SPACING_TOP); + spacing_bottom = p_fonts->get_spacing(TextServer::SPACING_BOTTOM); width = p_width; } diff --git a/scene/resources/text_paragraph.h b/scene/resources/text_paragraph.h index ee7bbab9c5..69c50559df 100644 --- a/scene/resources/text_paragraph.h +++ b/scene/resources/text_paragraph.h @@ -63,7 +63,7 @@ private: float width = -1.0; int max_lines_visible = -1; - uint8_t flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA; + uint16_t flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA; OverrunBehavior overrun_behavior = OVERRUN_NO_TRIMMING; HAlign align = HALIGN_LEFT; @@ -94,7 +94,7 @@ public: void set_preserve_control(bool p_enabled); bool get_preserve_control() const; - void set_bidi_override(const Vector<Vector2i> &p_override); + void set_bidi_override(const Array &p_override); bool set_dropcap(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Rect2 &p_dropcap_margins = Rect2(), const Dictionary &p_opentype_features = Dictionary(), const String &p_language = ""); void clear_dropcap(); @@ -108,8 +108,8 @@ public: void tab_align(const Vector<float> &p_tab_stops); - void set_flags(uint8_t p_flags); - uint8_t get_flags() const; + void set_flags(uint16_t p_flags); + uint16_t get_flags() const; void set_text_overrun_behavior(OverrunBehavior p_behavior); OverrunBehavior get_text_overrun_behavior() const; @@ -153,8 +153,6 @@ public: int hit_test(const Point2 &p_coords) const; - void _set_bidi_override(const Array &p_override); - TextParagraph(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", float p_width = -1.f, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL); TextParagraph(); ~TextParagraph(); diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index 4ad5f2a506..1b3a7a5f2a 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -131,25 +131,6 @@ void ImageTexture::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::VECTOR2, "size", PROPERTY_HINT_NONE, "")); } -void ImageTexture::_reload_hook(const RID &p_hook) { - String path = get_path(); - if (!path.is_resource_file()) { - return; - } - - Ref<Image> img; - img.instantiate(); - Error err = ImageLoader::load_image(path, img); - - ERR_FAIL_COND_MSG(err != OK, "Cannot load image from path '" + path + "'."); - - RID new_texture = RenderingServer::get_singleton()->texture_2d_create(img); - RenderingServer::get_singleton()->texture_replace(texture, new_texture); - - notify_property_list_changed(); - emit_changed(); -} - void ImageTexture::create_from_image(const Ref<Image> &p_image) { ERR_FAIL_COND_MSG(p_image.is_null() || p_image->is_empty(), "Invalid image"); w = p_image->get_width(); @@ -192,10 +173,6 @@ void ImageTexture::update(const Ref<Image> &p_image) { image_stored = true; } -void ImageTexture::_resource_path_changed() { - String path = get_path(); -} - Ref<Image> ImageTexture::get_image() const { if (image_stored) { return RenderingServer::get_singleton()->texture_2d_get(texture); @@ -303,7 +280,6 @@ void ImageTexture::_bind_methods() { ClassDB::bind_method(D_METHOD("update", "image"), &ImageTexture::update); ClassDB::bind_method(D_METHOD("set_size_override", "size"), &ImageTexture::set_size_override); - ClassDB::bind_method(D_METHOD("_reload_hook", "rid"), &ImageTexture::_reload_hook); } ImageTexture::ImageTexture() {} @@ -1272,6 +1248,14 @@ bool AtlasTexture::is_pixel_opaque(int p_x, int p_y) const { return atlas->is_pixel_opaque(x, y); } +Ref<Image> AtlasTexture::get_image() const { + if (!atlas.is_valid() || !atlas->get_image().is_valid()) { + return Ref<Image>(); + } + + return atlas->get_image()->get_rect(region); +} + AtlasTexture::AtlasTexture() {} ///////////////////////////////////////// @@ -1758,11 +1742,16 @@ void GradientTexture::_bind_methods() { ClassDB::bind_method(D_METHOD("get_gradient"), &GradientTexture::get_gradient); ClassDB::bind_method(D_METHOD("set_width", "width"), &GradientTexture::set_width); + // The `get_width()` method is already exposed by the parent class Texture2D. + + ClassDB::bind_method(D_METHOD("set_use_hdr", "enabled"), &GradientTexture::set_use_hdr); + ClassDB::bind_method(D_METHOD("is_using_hdr"), &GradientTexture::is_using_hdr); ClassDB::bind_method(D_METHOD("_update"), &GradientTexture::_update); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gradient", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_gradient", "get_gradient"); ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,4096"), "set_width", "get_width"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_hdr"), "set_use_hdr", "is_using_hdr"); } void GradientTexture::set_gradient(Ref<Gradient> p_gradient) { @@ -1800,30 +1789,49 @@ void GradientTexture::_update() { return; } - Vector<uint8_t> data; - data.resize(width * 4); - { - uint8_t *wd8 = data.ptrw(); + if (use_hdr) { + // High dynamic range. + Ref<Image> image = memnew(Image(width, 1, false, Image::FORMAT_RGBAF)); Gradient &g = **gradient; - + // `create()` isn't available for non-uint8_t data, so fill in the data manually. for (int i = 0; i < width; i++) { float ofs = float(i) / (width - 1); - Color color = g.get_color_at_offset(ofs); + image->set_pixel(i, 0, g.get_color_at_offset(ofs)); + } - wd8[i * 4 + 0] = uint8_t(CLAMP(color.r * 255.0, 0, 255)); - wd8[i * 4 + 1] = uint8_t(CLAMP(color.g * 255.0, 0, 255)); - wd8[i * 4 + 2] = uint8_t(CLAMP(color.b * 255.0, 0, 255)); - wd8[i * 4 + 3] = uint8_t(CLAMP(color.a * 255.0, 0, 255)); + if (texture.is_valid()) { + RID new_texture = RS::get_singleton()->texture_2d_create(image); + RS::get_singleton()->texture_replace(texture, new_texture); + } else { + texture = RS::get_singleton()->texture_2d_create(image); + } + } else { + // Low dynamic range. "Overbright" colors will be clamped. + Vector<uint8_t> data; + data.resize(width * 4); + { + uint8_t *wd8 = data.ptrw(); + Gradient &g = **gradient; + + for (int i = 0; i < width; i++) { + float ofs = float(i) / (width - 1); + Color color = g.get_color_at_offset(ofs); + + wd8[i * 4 + 0] = uint8_t(CLAMP(color.r * 255.0, 0, 255)); + wd8[i * 4 + 1] = uint8_t(CLAMP(color.g * 255.0, 0, 255)); + wd8[i * 4 + 2] = uint8_t(CLAMP(color.b * 255.0, 0, 255)); + wd8[i * 4 + 3] = uint8_t(CLAMP(color.a * 255.0, 0, 255)); + } } - } - Ref<Image> image = memnew(Image(width, 1, false, Image::FORMAT_RGBA8, data)); + Ref<Image> image = memnew(Image(width, 1, false, Image::FORMAT_RGBA8, data)); - if (texture.is_valid()) { - RID new_texture = RS::get_singleton()->texture_2d_create(image); - RS::get_singleton()->texture_replace(texture, new_texture); - } else { - texture = RS::get_singleton()->texture_2d_create(image); + if (texture.is_valid()) { + RID new_texture = RS::get_singleton()->texture_2d_create(image); + RS::get_singleton()->texture_replace(texture, new_texture); + } else { + texture = RS::get_singleton()->texture_2d_create(image); + } } emit_changed(); @@ -1839,6 +1847,19 @@ int GradientTexture::get_width() const { return width; } +void GradientTexture::set_use_hdr(bool p_enabled) { + if (p_enabled == use_hdr) { + return; + } + + use_hdr = p_enabled; + _queue_update(); +} + +bool GradientTexture::is_using_hdr() const { + return use_hdr; +} + Ref<Image> GradientTexture::get_image() const { if (!texture.is_valid()) { return Ref<Image>(); diff --git a/scene/resources/texture.h b/scene/resources/texture.h index 2e97c2deb1..862b9a47a6 100644 --- a/scene/resources/texture.h +++ b/scene/resources/texture.h @@ -98,8 +98,6 @@ protected: bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List<PropertyInfo> *p_list) const; - void _reload_hook(const RID &p_hook); - virtual void _resource_path_changed() override; static void _bind_methods(); public: @@ -252,6 +250,8 @@ public: bool is_pixel_opaque(int p_x, int p_y) const override; + virtual Ref<Image> get_image() const override; + AtlasTexture(); }; @@ -686,6 +686,7 @@ private: bool update_pending = false; RID texture; int width = 2048; + bool use_hdr = false; void _queue_update(); void _update(); @@ -700,6 +701,9 @@ public: void set_width(int p_width); int get_width() const override; + void set_use_hdr(bool p_enabled); + bool is_using_hdr() const; + virtual RID get_rid() const override { return texture; } virtual int get_height() const override { return 1; } virtual bool has_alpha() const override { return true; } diff --git a/scene/resources/theme.cpp b/scene/resources/theme.cpp index e49d883ba4..65cdc1e24e 100644 --- a/scene/resources/theme.cpp +++ b/scene/resources/theme.cpp @@ -29,270 +29,20 @@ /*************************************************************************/ #include "theme.h" -#include "core/io/file_access.h" #include "core/string/print_string.h" -void Theme::_emit_theme_changed() { - if (no_change_propagation) { - return; - } - - notify_property_list_changed(); - emit_changed(); -} - -Vector<String> Theme::_get_icon_list(const String &p_theme_type) const { - Vector<String> ilret; - List<StringName> il; - - get_icon_list(p_theme_type, &il); - ilret.resize(il.size()); - - int i = 0; - String *w = ilret.ptrw(); - for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { - w[i] = E->get(); - } - return ilret; -} - -Vector<String> Theme::_get_icon_type_list() const { - Vector<String> ilret; - List<StringName> il; - - get_icon_type_list(&il); - ilret.resize(il.size()); - - int i = 0; - String *w = ilret.ptrw(); - for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { - w[i] = E->get(); - } - return ilret; -} - -Vector<String> Theme::_get_stylebox_list(const String &p_theme_type) const { - Vector<String> ilret; - List<StringName> il; - - get_stylebox_list(p_theme_type, &il); - ilret.resize(il.size()); - - int i = 0; - String *w = ilret.ptrw(); - for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { - w[i] = E->get(); - } - return ilret; -} - -Vector<String> Theme::_get_stylebox_type_list() const { - Vector<String> ilret; - List<StringName> il; - - get_stylebox_type_list(&il); - ilret.resize(il.size()); - - int i = 0; - String *w = ilret.ptrw(); - for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { - w[i] = E->get(); - } - return ilret; -} - -Vector<String> Theme::_get_font_list(const String &p_theme_type) const { - Vector<String> ilret; - List<StringName> il; - - get_font_list(p_theme_type, &il); - ilret.resize(il.size()); - - int i = 0; - String *w = ilret.ptrw(); - for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { - w[i] = E->get(); - } - return ilret; -} - -Vector<String> Theme::_get_font_type_list() const { - Vector<String> ilret; - List<StringName> il; - - get_font_type_list(&il); - ilret.resize(il.size()); - - int i = 0; - String *w = ilret.ptrw(); - for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { - w[i] = E->get(); - } - return ilret; -} - -Vector<String> Theme::_get_font_size_list(const String &p_theme_type) const { - Vector<String> ilret; - List<StringName> il; - - get_font_size_list(p_theme_type, &il); - ilret.resize(il.size()); - - int i = 0; - String *w = ilret.ptrw(); - for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { - w[i] = E->get(); - } - return ilret; -} - -Vector<String> Theme::_get_font_size_type_list() const { - Vector<String> ilret; - List<StringName> il; - - get_font_size_type_list(&il); - ilret.resize(il.size()); - - int i = 0; - String *w = ilret.ptrw(); - for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { - w[i] = E->get(); - } - return ilret; -} - -Vector<String> Theme::_get_color_list(const String &p_theme_type) const { - Vector<String> ilret; - List<StringName> il; - - get_color_list(p_theme_type, &il); - ilret.resize(il.size()); - - int i = 0; - String *w = ilret.ptrw(); - for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { - w[i] = E->get(); - } - return ilret; -} - -Vector<String> Theme::_get_color_type_list() const { - Vector<String> ilret; - List<StringName> il; - - get_color_type_list(&il); - ilret.resize(il.size()); - - int i = 0; - String *w = ilret.ptrw(); - for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { - w[i] = E->get(); - } - return ilret; -} - -Vector<String> Theme::_get_constant_list(const String &p_theme_type) const { - Vector<String> ilret; - List<StringName> il; - - get_constant_list(p_theme_type, &il); - ilret.resize(il.size()); - - int i = 0; - String *w = ilret.ptrw(); - for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { - w[i] = E->get(); - } - return ilret; -} - -Vector<String> Theme::_get_constant_type_list() const { - Vector<String> ilret; - List<StringName> il; - - get_constant_type_list(&il); - ilret.resize(il.size()); - - int i = 0; - String *w = ilret.ptrw(); - for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { - w[i] = E->get(); - } - return ilret; -} - -Vector<String> Theme::_get_theme_item_list(DataType p_data_type, const String &p_theme_type) const { - switch (p_data_type) { - case DATA_TYPE_COLOR: - return _get_color_list(p_theme_type); - case DATA_TYPE_CONSTANT: - return _get_constant_list(p_theme_type); - case DATA_TYPE_FONT: - return _get_font_list(p_theme_type); - case DATA_TYPE_FONT_SIZE: - return _get_font_size_list(p_theme_type); - case DATA_TYPE_ICON: - return _get_icon_list(p_theme_type); - case DATA_TYPE_STYLEBOX: - return _get_stylebox_list(p_theme_type); - case DATA_TYPE_MAX: - break; // Can't happen, but silences warning. - } - - return Vector<String>(); -} - -Vector<String> Theme::_get_theme_item_type_list(DataType p_data_type) const { - switch (p_data_type) { - case DATA_TYPE_COLOR: - return _get_color_type_list(); - case DATA_TYPE_CONSTANT: - return _get_constant_type_list(); - case DATA_TYPE_FONT: - return _get_font_type_list(); - case DATA_TYPE_FONT_SIZE: - return _get_font_size_type_list(); - case DATA_TYPE_ICON: - return _get_icon_type_list(); - case DATA_TYPE_STYLEBOX: - return _get_stylebox_type_list(); - case DATA_TYPE_MAX: - break; // Can't happen, but silences warning. - } - - return Vector<String>(); -} - -Vector<String> Theme::_get_type_variation_list(const StringName &p_theme_type) const { - Vector<String> ilret; - List<StringName> il; - - get_type_variation_list(p_theme_type, &il); - ilret.resize(il.size()); - - int i = 0; - String *w = ilret.ptrw(); - for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { - w[i] = E->get(); - } - return ilret; -} - -Vector<String> Theme::_get_type_list() const { - Vector<String> ilret; - List<StringName> il; - - get_type_list(&il); - ilret.resize(il.size()); +// Universal Theme resources used when no other theme has the item. +Ref<Theme> Theme::default_theme; +Ref<Theme> Theme::project_default_theme; - int i = 0; - String *w = ilret.ptrw(); - for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { - w[i] = E->get(); - } - return ilret; -} +// Universal default values, final fallback for every theme. +float Theme::default_base_scale = 1.0; +Ref<Texture2D> Theme::default_icon; +Ref<StyleBox> Theme::default_style; +Ref<Font> Theme::default_font; +int Theme::default_font_size = 16; +// Dynamic properties. bool Theme::_set(const StringName &p_name, const Variant &p_value) { String sname = p_name; @@ -452,6 +202,63 @@ void Theme::_get_property_list(List<PropertyInfo> *p_list) const { } } +// Universal fallback Theme resources. +Ref<Theme> Theme::get_default() { + return default_theme; +} + +void Theme::set_default(const Ref<Theme> &p_default) { + default_theme = p_default; +} + +Ref<Theme> Theme::get_project_default() { + return project_default_theme; +} + +void Theme::set_project_default(const Ref<Theme> &p_project_default) { + project_default_theme = p_project_default; +} + +// Universal fallback values for theme item types. +void Theme::set_default_base_scale(float p_base_scale) { + default_base_scale = p_base_scale; +} + +void Theme::set_default_icon(const Ref<Texture2D> &p_icon) { + default_icon = p_icon; +} + +void Theme::set_default_style(const Ref<StyleBox> &p_style) { + default_style = p_style; +} + +void Theme::set_default_font(const Ref<Font> &p_font) { + default_font = p_font; +} + +void Theme::set_default_font_size(int p_font_size) { + default_font_size = p_font_size; +} + +// Fallback values for theme item types, configurable per theme. +void Theme::set_default_theme_base_scale(float p_base_scale) { + if (default_theme_base_scale == p_base_scale) { + return; + } + + default_theme_base_scale = p_base_scale; + + _emit_theme_changed(); +} + +float Theme::get_default_theme_base_scale() const { + return default_theme_base_scale; +} + +bool Theme::has_default_theme_base_scale() const { + return default_theme_base_scale > 0.0; +} + void Theme::set_default_theme_font(const Ref<Font> &p_default_font) { if (default_theme_font == p_default_font) { return; @@ -464,7 +271,7 @@ void Theme::set_default_theme_font(const Ref<Font> &p_default_font) { default_theme_font = p_default_font; if (default_theme_font.is_valid()) { - default_theme_font->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED); + default_theme_font->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(false), CONNECT_REFERENCE_COUNTED); } _emit_theme_changed(); @@ -474,6 +281,10 @@ Ref<Font> Theme::get_default_theme_font() const { return default_theme_font; } +bool Theme::has_default_theme_font() const { + return default_theme_font.is_valid(); +} + void Theme::set_default_theme_font_size(int p_font_size) { if (default_theme_font_size == p_font_size) { return; @@ -488,57 +299,25 @@ int Theme::get_default_theme_font_size() const { return default_theme_font_size; } -Ref<Theme> Theme::project_default_theme; -Ref<Theme> Theme::default_theme; -Ref<Texture2D> Theme::default_icon; -Ref<StyleBox> Theme::default_style; -Ref<Font> Theme::default_font; -int Theme::default_font_size = 16; - -Ref<Theme> Theme::get_default() { - return default_theme; -} - -void Theme::set_default(const Ref<Theme> &p_default) { - default_theme = p_default; -} - -Ref<Theme> Theme::get_project_default() { - return project_default_theme; -} - -void Theme::set_project_default(const Ref<Theme> &p_project_default) { - project_default_theme = p_project_default; -} - -void Theme::set_default_icon(const Ref<Texture2D> &p_icon) { - default_icon = p_icon; -} - -void Theme::set_default_style(const Ref<StyleBox> &p_style) { - default_style = p_style; -} - -void Theme::set_default_font(const Ref<Font> &p_font) { - default_font = p_font; -} - -void Theme::set_default_font_size(int p_font_size) { - default_font_size = p_font_size; +bool Theme::has_default_theme_font_size() const { + return default_theme_font_size > 0; } +// Icons. void Theme::set_icon(const StringName &p_name, const StringName &p_theme_type, const Ref<Texture2D> &p_icon) { + bool existing = false; if (icon_map[p_theme_type].has(p_name) && icon_map[p_theme_type][p_name].is_valid()) { + existing = true; icon_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); } icon_map[p_theme_type][p_name] = p_icon; if (p_icon.is_valid()) { - icon_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED); + icon_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(false), CONNECT_REFERENCE_COUNTED); } - _emit_theme_changed(); + _emit_theme_changed(!existing); } Ref<Texture2D> Theme::get_icon(const StringName &p_name, const StringName &p_theme_type) const { @@ -565,7 +344,7 @@ void Theme::rename_icon(const StringName &p_old_name, const StringName &p_name, icon_map[p_theme_type][p_name] = icon_map[p_theme_type][p_old_name]; icon_map[p_theme_type].erase(p_old_name); - _emit_theme_changed(); + _emit_theme_changed(true); } void Theme::clear_icon(const StringName &p_name, const StringName &p_theme_type) { @@ -578,7 +357,7 @@ void Theme::clear_icon(const StringName &p_name, const StringName &p_theme_type) icon_map[p_theme_type].erase(p_name); - _emit_theme_changed(); + _emit_theme_changed(true); } void Theme::get_icon_list(StringName p_theme_type, List<StringName> *p_list) const { @@ -611,18 +390,21 @@ void Theme::get_icon_type_list(List<StringName> *p_list) const { } } +// Styleboxes. void Theme::set_stylebox(const StringName &p_name, const StringName &p_theme_type, const Ref<StyleBox> &p_style) { + bool existing = false; if (style_map[p_theme_type].has(p_name) && style_map[p_theme_type][p_name].is_valid()) { + existing = true; style_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); } style_map[p_theme_type][p_name] = p_style; if (p_style.is_valid()) { - style_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED); + style_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(false), CONNECT_REFERENCE_COUNTED); } - _emit_theme_changed(); + _emit_theme_changed(!existing); } Ref<StyleBox> Theme::get_stylebox(const StringName &p_name, const StringName &p_theme_type) const { @@ -649,7 +431,7 @@ void Theme::rename_stylebox(const StringName &p_old_name, const StringName &p_na style_map[p_theme_type][p_name] = style_map[p_theme_type][p_old_name]; style_map[p_theme_type].erase(p_old_name); - _emit_theme_changed(); + _emit_theme_changed(true); } void Theme::clear_stylebox(const StringName &p_name, const StringName &p_theme_type) { @@ -662,7 +444,7 @@ void Theme::clear_stylebox(const StringName &p_name, const StringName &p_theme_t style_map[p_theme_type].erase(p_name); - _emit_theme_changed(); + _emit_theme_changed(true); } void Theme::get_stylebox_list(StringName p_theme_type, List<StringName> *p_list) const { @@ -695,24 +477,27 @@ void Theme::get_stylebox_type_list(List<StringName> *p_list) const { } } +// Fonts. void Theme::set_font(const StringName &p_name, const StringName &p_theme_type, const Ref<Font> &p_font) { + bool existing = false; if (font_map[p_theme_type][p_name].is_valid()) { + existing = true; font_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); } font_map[p_theme_type][p_name] = p_font; if (p_font.is_valid()) { - font_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED); + font_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(false), CONNECT_REFERENCE_COUNTED); } - _emit_theme_changed(); + _emit_theme_changed(!existing); } Ref<Font> Theme::get_font(const StringName &p_name, const StringName &p_theme_type) const { if (font_map.has(p_theme_type) && font_map[p_theme_type].has(p_name) && font_map[p_theme_type][p_name].is_valid()) { return font_map[p_theme_type][p_name]; - } else if (default_theme_font.is_valid()) { + } else if (has_default_theme_font()) { return default_theme_font; } else { return default_font; @@ -720,7 +505,7 @@ Ref<Font> Theme::get_font(const StringName &p_name, const StringName &p_theme_ty } bool Theme::has_font(const StringName &p_name, const StringName &p_theme_type) const { - return ((font_map.has(p_theme_type) && font_map[p_theme_type].has(p_name) && font_map[p_theme_type][p_name].is_valid()) || default_theme_font.is_valid()); + return ((font_map.has(p_theme_type) && font_map[p_theme_type].has(p_name) && font_map[p_theme_type][p_name].is_valid()) || has_default_theme_font()); } bool Theme::has_font_nocheck(const StringName &p_name, const StringName &p_theme_type) const { @@ -735,7 +520,7 @@ void Theme::rename_font(const StringName &p_old_name, const StringName &p_name, font_map[p_theme_type][p_name] = font_map[p_theme_type][p_old_name]; font_map[p_theme_type].erase(p_old_name); - _emit_theme_changed(); + _emit_theme_changed(true); } void Theme::clear_font(const StringName &p_name, const StringName &p_theme_type) { @@ -748,7 +533,7 @@ void Theme::clear_font(const StringName &p_name, const StringName &p_theme_type) font_map[p_theme_type].erase(p_name); - _emit_theme_changed(); + _emit_theme_changed(true); } void Theme::get_font_list(StringName p_theme_type, List<StringName> *p_list) const { @@ -781,16 +566,18 @@ void Theme::get_font_type_list(List<StringName> *p_list) const { } } +// Font sizes. void Theme::set_font_size(const StringName &p_name, const StringName &p_theme_type, int p_font_size) { + bool existing = has_font_size_nocheck(p_name, p_theme_type); font_size_map[p_theme_type][p_name] = p_font_size; - _emit_theme_changed(); + _emit_theme_changed(!existing); } int Theme::get_font_size(const StringName &p_name, const StringName &p_theme_type) const { if (font_size_map.has(p_theme_type) && font_size_map[p_theme_type].has(p_name) && (font_size_map[p_theme_type][p_name] > 0)) { return font_size_map[p_theme_type][p_name]; - } else if (default_theme_font_size > 0) { + } else if (has_default_theme_font_size()) { return default_theme_font_size; } else { return default_font_size; @@ -798,7 +585,7 @@ int Theme::get_font_size(const StringName &p_name, const StringName &p_theme_typ } bool Theme::has_font_size(const StringName &p_name, const StringName &p_theme_type) const { - return ((font_size_map.has(p_theme_type) && font_size_map[p_theme_type].has(p_name) && (font_size_map[p_theme_type][p_name] > 0)) || (default_theme_font_size > 0)); + return ((font_size_map.has(p_theme_type) && font_size_map[p_theme_type].has(p_name) && (font_size_map[p_theme_type][p_name] > 0)) || has_default_theme_font_size()); } bool Theme::has_font_size_nocheck(const StringName &p_name, const StringName &p_theme_type) const { @@ -813,7 +600,7 @@ void Theme::rename_font_size(const StringName &p_old_name, const StringName &p_n font_size_map[p_theme_type][p_name] = font_size_map[p_theme_type][p_old_name]; font_size_map[p_theme_type].erase(p_old_name); - _emit_theme_changed(); + _emit_theme_changed(true); } void Theme::clear_font_size(const StringName &p_name, const StringName &p_theme_type) { @@ -822,7 +609,7 @@ void Theme::clear_font_size(const StringName &p_name, const StringName &p_theme_ font_size_map[p_theme_type].erase(p_name); - _emit_theme_changed(); + _emit_theme_changed(true); } void Theme::get_font_size_list(StringName p_theme_type, List<StringName> *p_list) const { @@ -855,10 +642,12 @@ void Theme::get_font_size_type_list(List<StringName> *p_list) const { } } +// Colors. void Theme::set_color(const StringName &p_name, const StringName &p_theme_type, const Color &p_color) { + bool existing = has_color_nocheck(p_name, p_theme_type); color_map[p_theme_type][p_name] = p_color; - _emit_theme_changed(); + _emit_theme_changed(!existing); } Color Theme::get_color(const StringName &p_name, const StringName &p_theme_type) const { @@ -885,7 +674,7 @@ void Theme::rename_color(const StringName &p_old_name, const StringName &p_name, color_map[p_theme_type][p_name] = color_map[p_theme_type][p_old_name]; color_map[p_theme_type].erase(p_old_name); - _emit_theme_changed(); + _emit_theme_changed(true); } void Theme::clear_color(const StringName &p_name, const StringName &p_theme_type) { @@ -894,7 +683,7 @@ void Theme::clear_color(const StringName &p_name, const StringName &p_theme_type color_map[p_theme_type].erase(p_name); - _emit_theme_changed(); + _emit_theme_changed(true); } void Theme::get_color_list(StringName p_theme_type, List<StringName> *p_list) const { @@ -927,10 +716,12 @@ void Theme::get_color_type_list(List<StringName> *p_list) const { } } +// Theme constants. void Theme::set_constant(const StringName &p_name, const StringName &p_theme_type, int p_constant) { + bool existing = has_constant_nocheck(p_name, p_theme_type); constant_map[p_theme_type][p_name] = p_constant; - _emit_theme_changed(); + _emit_theme_changed(!existing); } int Theme::get_constant(const StringName &p_name, const StringName &p_theme_type) const { @@ -957,7 +748,7 @@ void Theme::rename_constant(const StringName &p_old_name, const StringName &p_na constant_map[p_theme_type][p_name] = constant_map[p_theme_type][p_old_name]; constant_map[p_theme_type].erase(p_old_name); - _emit_theme_changed(); + _emit_theme_changed(true); } void Theme::clear_constant(const StringName &p_name, const StringName &p_theme_type) { @@ -966,7 +757,7 @@ void Theme::clear_constant(const StringName &p_name, const StringName &p_theme_t constant_map[p_theme_type].erase(p_name); - _emit_theme_changed(); + _emit_theme_changed(true); } void Theme::get_constant_list(StringName p_theme_type, List<StringName> *p_list) const { @@ -999,6 +790,7 @@ void Theme::get_constant_type_list(List<StringName> *p_list) const { } } +// Generic methods for managing theme items. void Theme::set_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type, const Variant &p_value) { switch (p_data_type) { case DATA_TYPE_COLOR: { @@ -1230,6 +1022,7 @@ void Theme::get_theme_item_type_list(DataType p_data_type, List<StringName> *p_l } } +// Theme type variations. void Theme::set_type_variation(const StringName &p_theme_type, const StringName &p_base_type) { ERR_FAIL_COND_MSG(p_theme_type == StringName(), "An empty theme type cannot be marked as a variation of another type."); ERR_FAIL_COND_MSG(ClassDB::class_exists(p_theme_type), "A type associated with a built-in class cannot be marked as a variation of another type."); @@ -1243,7 +1036,7 @@ void Theme::set_type_variation(const StringName &p_theme_type, const StringName variation_map[p_theme_type] = p_base_type; variation_base_map[p_base_type].push_back(p_theme_type); - _emit_theme_changed(); + _emit_theme_changed(true); } bool Theme::is_type_variation(const StringName &p_theme_type, const StringName &p_base_type) const { @@ -1257,7 +1050,7 @@ void Theme::clear_type_variation(const StringName &p_theme_type) { variation_base_map[base_type].erase(p_theme_type); variation_map.erase(p_theme_type); - _emit_theme_changed(); + _emit_theme_changed(true); } StringName Theme::get_type_variation_base(const StringName &p_theme_type) const { @@ -1287,67 +1080,355 @@ void Theme::get_type_variation_list(const StringName &p_base_type, List<StringNa } } -void Theme::_freeze_change_propagation() { - no_change_propagation = true; -} +// Theme types. +void Theme::get_type_list(List<StringName> *p_list) const { + ERR_FAIL_NULL(p_list); -void Theme::_unfreeze_and_propagate_changes() { - no_change_propagation = false; - _emit_theme_changed(); + Set<StringName> types; + const StringName *key = nullptr; + + // Icons. + while ((key = icon_map.next(key))) { + types.insert(*key); + } + + key = nullptr; + + // StyleBoxes. + while ((key = style_map.next(key))) { + types.insert(*key); + } + + key = nullptr; + + // Fonts. + while ((key = font_map.next(key))) { + types.insert(*key); + } + + key = nullptr; + + // Font sizes. + while ((key = font_size_map.next(key))) { + types.insert(*key); + } + + key = nullptr; + + // Colors. + while ((key = color_map.next(key))) { + types.insert(*key); + } + + key = nullptr; + + // Constants. + while ((key = constant_map.next(key))) { + types.insert(*key); + } + + for (Set<StringName>::Element *E = types.front(); E; E = E->next()) { + p_list->push_back(E->get()); + } } -void Theme::clear() { - // These items need disconnecting. - { - const StringName *K = nullptr; - while ((K = icon_map.next(K))) { - const StringName *L = nullptr; - while ((L = icon_map[*K].next(L))) { - Ref<Texture2D> icon = icon_map[*K][*L]; - if (icon.is_valid()) { - icon->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); - } +void Theme::get_type_dependencies(const StringName &p_base_type, const StringName &p_type_variation, List<StringName> *p_list) { + ERR_FAIL_NULL(p_list); + + // Build the dependency chain for type variations. + if (p_type_variation != StringName()) { + StringName variation_name = p_type_variation; + while (variation_name != StringName()) { + p_list->push_back(variation_name); + variation_name = get_type_variation_base(variation_name); + + // If we have reached the base type dependency, it's safe to stop (assuming no funny business was done to the Theme). + if (variation_name == p_base_type) { + break; } } } - { - const StringName *K = nullptr; - while ((K = style_map.next(K))) { - const StringName *L = nullptr; - while ((L = style_map[*K].next(L))) { - Ref<StyleBox> style = style_map[*K][*L]; - if (style.is_valid()) { - style->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); - } - } - } + // Continue building the chain using native class hierarchy. + StringName class_name = p_base_type; + while (class_name != StringName()) { + p_list->push_back(class_name); + class_name = ClassDB::get_parent_class_nocheck(class_name); } +} - { - const StringName *K = nullptr; - while ((K = font_map.next(K))) { - const StringName *L = nullptr; - while ((L = font_map[*K].next(L))) { - Ref<Font> font = font_map[*K][*L]; - if (font.is_valid()) { - font->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); - } - } - } +// Internal methods for getting lists as a Vector of String (compatible with public API). +Vector<String> Theme::_get_icon_list(const String &p_theme_type) const { + Vector<String> ilret; + List<StringName> il; + + get_icon_list(p_theme_type, &il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); } + return ilret; +} - icon_map.clear(); - style_map.clear(); - font_map.clear(); - font_size_map.clear(); - color_map.clear(); - constant_map.clear(); +Vector<String> Theme::_get_icon_type_list() const { + Vector<String> ilret; + List<StringName> il; - variation_map.clear(); - variation_base_map.clear(); + get_icon_type_list(&il); + ilret.resize(il.size()); - _emit_theme_changed(); + int i = 0; + String *w = ilret.ptrw(); + for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + +Vector<String> Theme::_get_stylebox_list(const String &p_theme_type) const { + Vector<String> ilret; + List<StringName> il; + + get_stylebox_list(p_theme_type, &il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + +Vector<String> Theme::_get_stylebox_type_list() const { + Vector<String> ilret; + List<StringName> il; + + get_stylebox_type_list(&il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + +Vector<String> Theme::_get_font_list(const String &p_theme_type) const { + Vector<String> ilret; + List<StringName> il; + + get_font_list(p_theme_type, &il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + +Vector<String> Theme::_get_font_type_list() const { + Vector<String> ilret; + List<StringName> il; + + get_font_type_list(&il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + +Vector<String> Theme::_get_font_size_list(const String &p_theme_type) const { + Vector<String> ilret; + List<StringName> il; + + get_font_size_list(p_theme_type, &il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + +Vector<String> Theme::_get_font_size_type_list() const { + Vector<String> ilret; + List<StringName> il; + + get_font_size_type_list(&il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + +Vector<String> Theme::_get_color_list(const String &p_theme_type) const { + Vector<String> ilret; + List<StringName> il; + + get_color_list(p_theme_type, &il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + +Vector<String> Theme::_get_color_type_list() const { + Vector<String> ilret; + List<StringName> il; + + get_color_type_list(&il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + +Vector<String> Theme::_get_constant_list(const String &p_theme_type) const { + Vector<String> ilret; + List<StringName> il; + + get_constant_list(p_theme_type, &il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + +Vector<String> Theme::_get_constant_type_list() const { + Vector<String> ilret; + List<StringName> il; + + get_constant_type_list(&il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + +Vector<String> Theme::_get_theme_item_list(DataType p_data_type, const String &p_theme_type) const { + switch (p_data_type) { + case DATA_TYPE_COLOR: + return _get_color_list(p_theme_type); + case DATA_TYPE_CONSTANT: + return _get_constant_list(p_theme_type); + case DATA_TYPE_FONT: + return _get_font_list(p_theme_type); + case DATA_TYPE_FONT_SIZE: + return _get_font_size_list(p_theme_type); + case DATA_TYPE_ICON: + return _get_icon_list(p_theme_type); + case DATA_TYPE_STYLEBOX: + return _get_stylebox_list(p_theme_type); + case DATA_TYPE_MAX: + break; // Can't happen, but silences warning. + } + + return Vector<String>(); +} + +Vector<String> Theme::_get_theme_item_type_list(DataType p_data_type) const { + switch (p_data_type) { + case DATA_TYPE_COLOR: + return _get_color_type_list(); + case DATA_TYPE_CONSTANT: + return _get_constant_type_list(); + case DATA_TYPE_FONT: + return _get_font_type_list(); + case DATA_TYPE_FONT_SIZE: + return _get_font_size_type_list(); + case DATA_TYPE_ICON: + return _get_icon_type_list(); + case DATA_TYPE_STYLEBOX: + return _get_stylebox_type_list(); + case DATA_TYPE_MAX: + break; // Can't happen, but silences warning. + } + + return Vector<String>(); +} + +Vector<String> Theme::_get_type_variation_list(const StringName &p_theme_type) const { + Vector<String> ilret; + List<StringName> il; + + get_type_variation_list(p_theme_type, &il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + +Vector<String> Theme::_get_type_list() const { + Vector<String> ilret; + List<StringName> il; + + get_type_list(&il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + +// Theme bulk manipulations. +void Theme::_emit_theme_changed(bool p_notify_list_changed) { + if (no_change_propagation) { + return; + } + + if (p_notify_list_changed) { + notify_property_list_changed(); + } + emit_changed(); +} + +void Theme::_freeze_change_propagation() { + no_change_propagation = true; +} + +void Theme::_unfreeze_and_propagate_changes() { + no_change_propagation = false; + _emit_theme_changed(true); } void Theme::merge_with(const Ref<Theme> &p_other) { @@ -1434,80 +1515,58 @@ void Theme::merge_with(const Ref<Theme> &p_other) { _unfreeze_and_propagate_changes(); } -void Theme::get_type_list(List<StringName> *p_list) const { - ERR_FAIL_NULL(p_list); - - Set<StringName> types; - const StringName *key = nullptr; - - // Icons. - while ((key = icon_map.next(key))) { - types.insert(*key); - } - - key = nullptr; - - // StyleBoxes. - while ((key = style_map.next(key))) { - types.insert(*key); - } - - key = nullptr; - - // Fonts. - while ((key = font_map.next(key))) { - types.insert(*key); - } - - key = nullptr; - - // Font sizes. - while ((key = font_size_map.next(key))) { - types.insert(*key); - } - - key = nullptr; - - // Colors. - while ((key = color_map.next(key))) { - types.insert(*key); - } - - key = nullptr; - - // Constants. - while ((key = constant_map.next(key))) { - types.insert(*key); +void Theme::clear() { + // These items need disconnecting. + { + const StringName *K = nullptr; + while ((K = icon_map.next(K))) { + const StringName *L = nullptr; + while ((L = icon_map[*K].next(L))) { + Ref<Texture2D> icon = icon_map[*K][*L]; + if (icon.is_valid()) { + icon->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); + } + } + } } - for (Set<StringName>::Element *E = types.front(); E; E = E->next()) { - p_list->push_back(E->get()); + { + const StringName *K = nullptr; + while ((K = style_map.next(K))) { + const StringName *L = nullptr; + while ((L = style_map[*K].next(L))) { + Ref<StyleBox> style = style_map[*K][*L]; + if (style.is_valid()) { + style->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); + } + } + } } -} -void Theme::get_type_dependencies(const StringName &p_base_type, const StringName &p_type_variation, List<StringName> *p_list) { - ERR_FAIL_NULL(p_list); - - // Build the dependency chain for type variations. - if (p_type_variation != StringName()) { - StringName variation_name = p_type_variation; - while (variation_name != StringName()) { - p_list->push_back(variation_name); - variation_name = get_type_variation_base(variation_name); - - // If we have reached the base type dependency, it's safe to stop (assuming no funny business was done to the Theme). - if (variation_name == p_base_type) { - break; + { + const StringName *K = nullptr; + while ((K = font_map.next(K))) { + const StringName *L = nullptr; + while ((L = font_map[*K].next(L))) { + Ref<Font> font = font_map[*K][*L]; + if (font.is_valid()) { + font->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); + } } } } - // Continue building the chain using native class hierarchy. - StringName class_name = p_base_type; - while (class_name != StringName()) { - p_list->push_back(class_name); - class_name = ClassDB::get_parent_class_nocheck(class_name); - } + icon_map.clear(); + style_map.clear(); + font_map.clear(); + font_size_map.clear(); + color_map.clear(); + constant_map.clear(); + + variation_map.clear(); + variation_base_map.clear(); + + _emit_theme_changed(true); } void Theme::reset_state() { @@ -1563,11 +1622,17 @@ void Theme::_bind_methods() { ClassDB::bind_method(D_METHOD("get_constant_list", "theme_type"), &Theme::_get_constant_list); ClassDB::bind_method(D_METHOD("get_constant_type_list"), &Theme::_get_constant_type_list); + ClassDB::bind_method(D_METHOD("set_default_base_scale", "font_size"), &Theme::set_default_theme_base_scale); + ClassDB::bind_method(D_METHOD("get_default_base_scale"), &Theme::get_default_theme_base_scale); + ClassDB::bind_method(D_METHOD("has_default_base_scale"), &Theme::has_default_theme_base_scale); + ClassDB::bind_method(D_METHOD("set_default_font", "font"), &Theme::set_default_theme_font); ClassDB::bind_method(D_METHOD("get_default_font"), &Theme::get_default_theme_font); + ClassDB::bind_method(D_METHOD("has_default_font"), &Theme::has_default_theme_font); ClassDB::bind_method(D_METHOD("set_default_font_size", "font_size"), &Theme::set_default_theme_font_size); ClassDB::bind_method(D_METHOD("get_default_font_size"), &Theme::get_default_theme_font_size); + ClassDB::bind_method(D_METHOD("has_default_font_size"), &Theme::has_default_theme_font_size); ClassDB::bind_method(D_METHOD("set_theme_item", "data_type", "name", "theme_type", "value"), &Theme::set_theme_item); ClassDB::bind_method(D_METHOD("get_theme_item", "data_type", "name", "theme_type"), &Theme::get_theme_item); @@ -1588,6 +1653,7 @@ void Theme::_bind_methods() { ClassDB::bind_method(D_METHOD("merge_with", "other"), &Theme::merge_with); ClassDB::bind_method(D_METHOD("clear"), &Theme::clear); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "default_base_scale", PROPERTY_HINT_RANGE, "0.0,2.0,0.01,or_greater"), "set_default_base_scale", "get_default_base_scale"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "default_font", PROPERTY_HINT_RESOURCE_TYPE, "Font"), "set_default_font", "get_default_font"); ADD_PROPERTY(PropertyInfo(Variant::INT, "default_font_size"), "set_default_font_size", "get_default_font_size"); diff --git a/scene/resources/theme.h b/scene/resources/theme.h index 15f21b91b8..d170d53ae3 100644 --- a/scene/resources/theme.h +++ b/scene/resources/theme.h @@ -61,7 +61,7 @@ public: private: bool no_change_propagation = false; - void _emit_theme_changed(); + void _emit_theme_changed(bool p_notify_list_changed = false); HashMap<StringName, HashMap<StringName, Ref<Texture2D>>> icon_map; HashMap<StringName, HashMap<StringName, Ref<StyleBox>>> style_map; @@ -96,13 +96,19 @@ protected: bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List<PropertyInfo> *p_list) const; - static Ref<Theme> project_default_theme; + // Universal Theme resources used when no other theme has the item. static Ref<Theme> default_theme; + static Ref<Theme> project_default_theme; + + // Universal default values, final fallback for every theme. + static float default_base_scale; static Ref<Texture2D> default_icon; static Ref<StyleBox> default_style; static Ref<Font> default_font; static int default_font_size; + // Default values configurable for each individual theme. + float default_theme_base_scale = 0.0; Ref<Font> default_theme_font; int default_theme_font_size = -1; @@ -120,16 +126,23 @@ public: static Ref<Theme> get_project_default(); static void set_project_default(const Ref<Theme> &p_project_default); + static void set_default_base_scale(float p_base_scale); static void set_default_icon(const Ref<Texture2D> &p_icon); static void set_default_style(const Ref<StyleBox> &p_style); static void set_default_font(const Ref<Font> &p_font); static void set_default_font_size(int p_font_size); + void set_default_theme_base_scale(float p_base_scale); + float get_default_theme_base_scale() const; + bool has_default_theme_base_scale() const; + void set_default_theme_font(const Ref<Font> &p_default_font); Ref<Font> get_default_theme_font() const; + bool has_default_theme_font() const; void set_default_theme_font_size(int p_font_size); int get_default_theme_font_size() const; + bool has_default_theme_font_size() const; void set_icon(const StringName &p_name, const StringName &p_theme_type, const Ref<Texture2D> &p_icon); Ref<Texture2D> get_icon(const StringName &p_name, const StringName &p_theme_type) const; diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index fcd31143a8..f5e52b70e6 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -66,12 +66,13 @@ const char *TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[] = { void TileSet::set_tile_shape(TileSet::TileShape p_shape) { tile_shape = p_shape; - for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) { - E_source->get()->notify_tile_data_properties_should_change(); + for (KeyValue<int, Ref<TileSetSource>> &E_source : sources) { + E_source.value->notify_tile_data_properties_should_change(); } terrain_bits_meshes_dirty = true; tile_meshes_dirty = true; + notify_property_list_changed(); emit_changed(); } TileSet::TileShape TileSet::get_tile_shape() const { @@ -89,8 +90,8 @@ TileSet::TileLayout TileSet::get_tile_layout() const { void TileSet::set_tile_offset_axis(TileSet::TileOffsetAxis p_alignment) { tile_offset_axis = p_alignment; - for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) { - E_source->get()->notify_tile_data_properties_should_change(); + for (KeyValue<int, Ref<TileSetSource>> &E_source : sources) { + E_source.value->notify_tile_data_properties_should_change(); } terrain_bits_meshes_dirty = true; @@ -170,6 +171,8 @@ void TileSet::set_source_id(int p_source_id, int p_new_source_id) { source_ids.append(p_new_source_id); source_ids.sort(); + _compute_next_source_id(); + emit_changed(); } @@ -205,25 +208,46 @@ bool TileSet::is_uv_clipping() const { return uv_clipping; }; -void TileSet::set_occlusion_layers_count(int p_occlusion_layers_count) { - ERR_FAIL_COND(p_occlusion_layers_count < 0); - if (occlusion_layers.size() == p_occlusion_layers_count) { - return; - } +int TileSet::get_occlusion_layers_count() const { + return occlusion_layers.size(); +}; - occlusion_layers.resize(p_occlusion_layers_count); +void TileSet::add_occlusion_layer(int p_index) { + if (p_index < 0) { + p_index = occlusion_layers.size(); + } + ERR_FAIL_INDEX(p_index, occlusion_layers.size() + 1); + occlusion_layers.insert(p_index, OcclusionLayer()); - for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) { - E_source->get()->notify_tile_data_properties_should_change(); + for (KeyValue<int, Ref<TileSetSource>> source : sources) { + source.value->add_occlusion_layer(p_index); } notify_property_list_changed(); emit_changed(); } -int TileSet::get_occlusion_layers_count() const { - return occlusion_layers.size(); -}; +void TileSet::move_occlusion_layer(int p_from_index, int p_to_pos) { + ERR_FAIL_INDEX(p_from_index, occlusion_layers.size()); + ERR_FAIL_INDEX(p_to_pos, occlusion_layers.size() + 1); + occlusion_layers.insert(p_to_pos, occlusion_layers[p_from_index]); + occlusion_layers.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index); + for (KeyValue<int, Ref<TileSetSource>> source : sources) { + source.value->move_occlusion_layer(p_from_index, p_to_pos); + } + notify_property_list_changed(); + emit_changed(); +} + +void TileSet::remove_occlusion_layer(int p_index) { + ERR_FAIL_INDEX(p_index, occlusion_layers.size()); + occlusion_layers.remove(p_index); + for (KeyValue<int, Ref<TileSetSource>> source : sources) { + source.value->remove_occlusion_layer(p_index); + } + notify_property_list_changed(); + emit_changed(); +} void TileSet::set_occlusion_layer_light_mask(int p_layer_index, int p_light_mask) { ERR_FAIL_INDEX(p_layer_index, occlusion_layers.size()); @@ -236,7 +260,7 @@ int TileSet::get_occlusion_layer_light_mask(int p_layer_index) const { return occlusion_layers[p_layer_index].light_mask; } -void TileSet::set_occlusion_layer_sdf_collision(int p_layer_index, int p_sdf_collision) { +void TileSet::set_occlusion_layer_sdf_collision(int p_layer_index, bool p_sdf_collision) { ERR_FAIL_INDEX(p_layer_index, occlusion_layers.size()); occlusion_layers.write[p_layer_index].sdf_collision = p_sdf_collision; emit_changed(); @@ -247,25 +271,45 @@ bool TileSet::get_occlusion_layer_sdf_collision(int p_layer_index) const { return occlusion_layers[p_layer_index].sdf_collision; } -// Physics -void TileSet::set_physics_layers_count(int p_physics_layers_count) { - ERR_FAIL_COND(p_physics_layers_count < 0); - if (physics_layers.size() == p_physics_layers_count) { - return; - } +int TileSet::get_physics_layers_count() const { + return physics_layers.size(); +} - physics_layers.resize(p_physics_layers_count); +void TileSet::add_physics_layer(int p_index) { + if (p_index < 0) { + p_index = physics_layers.size(); + } + ERR_FAIL_INDEX(p_index, physics_layers.size() + 1); + physics_layers.insert(p_index, PhysicsLayer()); - for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) { - E_source->get()->notify_tile_data_properties_should_change(); + for (KeyValue<int, Ref<TileSetSource>> source : sources) { + source.value->add_physics_layer(p_index); } notify_property_list_changed(); emit_changed(); } -int TileSet::get_physics_layers_count() const { - return physics_layers.size(); +void TileSet::move_physics_layer(int p_from_index, int p_to_pos) { + ERR_FAIL_INDEX(p_from_index, physics_layers.size()); + ERR_FAIL_INDEX(p_to_pos, physics_layers.size() + 1); + physics_layers.insert(p_to_pos, physics_layers[p_from_index]); + physics_layers.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index); + for (KeyValue<int, Ref<TileSetSource>> source : sources) { + source.value->move_physics_layer(p_from_index, p_to_pos); + } + notify_property_list_changed(); + emit_changed(); +} + +void TileSet::remove_physics_layer(int p_index) { + ERR_FAIL_INDEX(p_index, physics_layers.size()); + physics_layers.remove(p_index); + for (KeyValue<int, Ref<TileSetSource>> source : sources) { + source.value->remove_physics_layer(p_index); + } + notify_property_list_changed(); + emit_changed(); } void TileSet::set_physics_layer_collision_layer(int p_layer_index, uint32_t p_layer) { @@ -301,24 +345,52 @@ Ref<PhysicsMaterial> TileSet::get_physics_layer_physics_material(int p_layer_ind } // Terrains -void TileSet::set_terrain_sets_count(int p_terrains_sets_count) { - ERR_FAIL_COND(p_terrains_sets_count < 0); +int TileSet::get_terrain_sets_count() const { + return terrain_sets.size(); +} - terrain_sets.resize(p_terrains_sets_count); +void TileSet::add_terrain_set(int p_index) { + if (p_index < 0) { + p_index = terrain_sets.size(); + } + ERR_FAIL_INDEX(p_index, terrain_sets.size() + 1); + terrain_sets.insert(p_index, TerrainSet()); + + for (KeyValue<int, Ref<TileSetSource>> source : sources) { + source.value->add_terrain_set(p_index); + } notify_property_list_changed(); emit_changed(); } -int TileSet::get_terrain_sets_count() const { - return terrain_sets.size(); +void TileSet::move_terrain_set(int p_from_index, int p_to_pos) { + ERR_FAIL_INDEX(p_from_index, terrain_sets.size()); + ERR_FAIL_INDEX(p_to_pos, terrain_sets.size() + 1); + terrain_sets.insert(p_to_pos, terrain_sets[p_from_index]); + terrain_sets.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index); + for (KeyValue<int, Ref<TileSetSource>> source : sources) { + source.value->move_terrain_set(p_from_index, p_to_pos); + } + notify_property_list_changed(); + emit_changed(); +} + +void TileSet::remove_terrain_set(int p_index) { + ERR_FAIL_INDEX(p_index, terrain_sets.size()); + terrain_sets.remove(p_index); + for (KeyValue<int, Ref<TileSetSource>> source : sources) { + source.value->remove_terrain_set(p_index); + } + notify_property_list_changed(); + emit_changed(); } void TileSet::set_terrain_set_mode(int p_terrain_set, TerrainMode p_terrain_mode) { ERR_FAIL_INDEX(p_terrain_set, terrain_sets.size()); terrain_sets.write[p_terrain_set].mode = p_terrain_mode; - for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) { - E_source->get()->notify_tile_data_properties_should_change(); + for (KeyValue<int, Ref<TileSetSource>> &E_source : sources) { + E_source.value->notify_tile_data_properties_should_change(); } notify_property_list_changed(); @@ -330,36 +402,61 @@ TileSet::TerrainMode TileSet::get_terrain_set_mode(int p_terrain_set) const { return terrain_sets[p_terrain_set].mode; } -void TileSet::set_terrains_count(int p_terrain_set, int p_terrains_layers_count) { +int TileSet::get_terrains_count(int p_terrain_set) const { + ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), -1); + return terrain_sets[p_terrain_set].terrains.size(); +} + +void TileSet::add_terrain(int p_terrain_set, int p_index) { ERR_FAIL_INDEX(p_terrain_set, terrain_sets.size()); - ERR_FAIL_COND(p_terrains_layers_count < 0); - if (terrain_sets[p_terrain_set].terrains.size() == p_terrains_layers_count) { - return; + Vector<Terrain> &terrains = terrain_sets.write[p_terrain_set].terrains; + if (p_index < 0) { + p_index = terrains.size(); } - - int old_size = terrain_sets[p_terrain_set].terrains.size(); - terrain_sets.write[p_terrain_set].terrains.resize(p_terrains_layers_count); + ERR_FAIL_INDEX(p_index, terrains.size() + 1); + terrains.insert(p_index, Terrain()); // Default name and color - for (int i = old_size; i < terrain_sets.write[p_terrain_set].terrains.size(); i++) { - float hue_rotate = (i * 2 % 16) / 16.0; - Color c; - c.set_hsv(Math::fmod(float(hue_rotate), float(1.0)), 0.5, 0.5); - terrain_sets.write[p_terrain_set].terrains.write[i].color = c; - terrain_sets.write[p_terrain_set].terrains.write[i].name = String(vformat("Terrain %d", i)); + float hue_rotate = (terrains.size() % 16) / 16.0; + Color c; + c.set_hsv(Math::fmod(float(hue_rotate), float(1.0)), 0.5, 0.5); + terrains.write[p_index].color = c; + terrains.write[p_index].name = String(vformat("Terrain %d", p_index)); + + for (KeyValue<int, Ref<TileSetSource>> source : sources) { + source.value->add_terrain(p_terrain_set, p_index); } - for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) { - E_source->get()->notify_tile_data_properties_should_change(); - } + notify_property_list_changed(); + emit_changed(); +} +void TileSet::move_terrain(int p_terrain_set, int p_from_index, int p_to_pos) { + ERR_FAIL_INDEX(p_terrain_set, terrain_sets.size()); + Vector<Terrain> &terrains = terrain_sets.write[p_terrain_set].terrains; + + ERR_FAIL_INDEX(p_from_index, terrains.size()); + ERR_FAIL_INDEX(p_to_pos, terrains.size() + 1); + terrains.insert(p_to_pos, terrains[p_from_index]); + terrains.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index); + for (KeyValue<int, Ref<TileSetSource>> source : sources) { + source.value->move_terrain(p_terrain_set, p_from_index, p_to_pos); + } notify_property_list_changed(); emit_changed(); } -int TileSet::get_terrains_count(int p_terrain_set) const { - ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), -1); - return terrain_sets[p_terrain_set].terrains.size(); +void TileSet::remove_terrain(int p_terrain_set, int p_index) { + ERR_FAIL_INDEX(p_terrain_set, terrain_sets.size()); + Vector<Terrain> &terrains = terrain_sets.write[p_terrain_set].terrains; + + ERR_FAIL_INDEX(p_index, terrains.size()); + terrains.remove(p_index); + for (KeyValue<int, Ref<TileSetSource>> source : sources) { + source.value->remove_terrain(p_terrain_set, p_index); + } + notify_property_list_changed(); + emit_changed(); } void TileSet::set_terrain_name(int p_terrain_set, int p_terrain_index, String p_name) { @@ -485,24 +582,45 @@ bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeigh } // Navigation -void TileSet::set_navigation_layers_count(int p_navigation_layers_count) { - ERR_FAIL_COND(p_navigation_layers_count < 0); - if (navigation_layers.size() == p_navigation_layers_count) { - return; - } +int TileSet::get_navigation_layers_count() const { + return navigation_layers.size(); +} - navigation_layers.resize(p_navigation_layers_count); +void TileSet::add_navigation_layer(int p_index) { + if (p_index < 0) { + p_index = navigation_layers.size(); + } + ERR_FAIL_INDEX(p_index, navigation_layers.size() + 1); + navigation_layers.insert(p_index, NavigationLayer()); - for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) { - E_source->get()->notify_tile_data_properties_should_change(); + for (KeyValue<int, Ref<TileSetSource>> source : sources) { + source.value->add_navigation_layer(p_index); } notify_property_list_changed(); emit_changed(); } -int TileSet::get_navigation_layers_count() const { - return navigation_layers.size(); +void TileSet::move_navigation_layer(int p_from_index, int p_to_pos) { + ERR_FAIL_INDEX(p_from_index, navigation_layers.size()); + ERR_FAIL_INDEX(p_to_pos, navigation_layers.size() + 1); + navigation_layers.insert(p_to_pos, navigation_layers[p_from_index]); + navigation_layers.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index); + for (KeyValue<int, Ref<TileSetSource>> source : sources) { + source.value->move_navigation_layer(p_from_index, p_to_pos); + } + notify_property_list_changed(); + emit_changed(); +} + +void TileSet::remove_navigation_layer(int p_index) { + ERR_FAIL_INDEX(p_index, navigation_layers.size()); + navigation_layers.remove(p_index); + for (KeyValue<int, Ref<TileSetSource>> source : sources) { + source.value->remove_navigation_layer(p_index); + } + notify_property_list_changed(); + emit_changed(); } void TileSet::set_navigation_layer_layers(int p_layer_index, uint32_t p_layers) { @@ -517,30 +635,52 @@ uint32_t TileSet::get_navigation_layer_layers(int p_layer_index) const { } // Custom data. -void TileSet::set_custom_data_layers_count(int p_custom_data_layers_count) { - ERR_FAIL_COND(p_custom_data_layers_count < 0); - if (custom_data_layers.size() == p_custom_data_layers_count) { - return; - } - - custom_data_layers.resize(p_custom_data_layers_count); +int TileSet::get_custom_data_layers_count() const { + return custom_data_layers.size(); +} - for (Map<String, int>::Element *E = custom_data_layers_by_name.front(); E; E = E->next()) { - if (E->get() >= custom_data_layers.size()) { - custom_data_layers_by_name.erase(E); - } +void TileSet::add_custom_data_layer(int p_index) { + if (p_index < 0) { + p_index = custom_data_layers.size(); } + ERR_FAIL_INDEX(p_index, custom_data_layers.size() + 1); + custom_data_layers.insert(p_index, CustomDataLayer()); - for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) { - E_source->get()->notify_tile_data_properties_should_change(); + for (KeyValue<int, Ref<TileSetSource>> source : sources) { + source.value->add_custom_data_layer(p_index); } notify_property_list_changed(); emit_changed(); } -int TileSet::get_custom_data_layers_count() const { - return custom_data_layers.size(); +void TileSet::move_custom_data_layer(int p_from_index, int p_to_pos) { + ERR_FAIL_INDEX(p_from_index, custom_data_layers.size()); + ERR_FAIL_INDEX(p_to_pos, custom_data_layers.size() + 1); + custom_data_layers.insert(p_to_pos, custom_data_layers[p_from_index]); + custom_data_layers.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index); + for (KeyValue<int, Ref<TileSetSource>> source : sources) { + source.value->move_custom_data_layer(p_from_index, p_to_pos); + } + notify_property_list_changed(); + emit_changed(); +} + +void TileSet::remove_custom_data_layer(int p_index) { + ERR_FAIL_INDEX(p_index, custom_data_layers.size()); + custom_data_layers.remove(p_index); + for (KeyValue<String, int> E : custom_data_layers_by_name) { + if (E.value == p_index) { + custom_data_layers_by_name.erase(E.key); + break; + } + } + + for (KeyValue<int, Ref<TileSetSource>> source : sources) { + source.value->remove_custom_data_layer(p_index); + } + notify_property_list_changed(); + emit_changed(); } int TileSet::get_custom_data_layer_by_name(String p_value) const { @@ -582,8 +722,8 @@ void TileSet::set_custom_data_type(int p_layer_id, Variant::Type p_value) { ERR_FAIL_INDEX(p_layer_id, custom_data_layers.size()); custom_data_layers.write[p_layer_id].type = p_value; - for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) { - E_source->get()->notify_tile_data_properties_should_change(); + for (KeyValue<int, Ref<TileSetSource>> &E_source : sources) { + E_source.value->notify_tile_data_properties_should_change(); } emit_changed(); @@ -721,10 +861,10 @@ void TileSet::remove_alternative_level_tile_proxy(int p_source_from, Vector2i p_ Array TileSet::get_source_level_tile_proxies() const { Array output; - for (Map<int, int>::Element *E = source_level_proxies.front(); E; E = E->next()) { + for (const KeyValue<int, int> &E : source_level_proxies) { Array proxy; - proxy.push_back(E->key()); - proxy.push_back(E->get()); + proxy.push_back(E.key); + proxy.push_back(E.value); output.push_back(proxy); } return output; @@ -732,10 +872,10 @@ Array TileSet::get_source_level_tile_proxies() const { Array TileSet::get_coords_level_tile_proxies() const { Array output; - for (Map<Array, Array>::Element *E = coords_level_proxies.front(); E; E = E->next()) { + for (const KeyValue<Array, Array> &E : coords_level_proxies) { Array proxy; - proxy.append_array(E->key()); - proxy.append_array(E->get()); + proxy.append_array(E.key); + proxy.append_array(E.value); output.push_back(proxy); } return output; @@ -743,10 +883,10 @@ Array TileSet::get_coords_level_tile_proxies() const { Array TileSet::get_alternative_level_tile_proxies() const { Array output; - for (Map<Array, Array>::Element *E = alternative_level_proxies.front(); E; E = E->next()) { + for (const KeyValue<Array, Array> &E : alternative_level_proxies) { Array proxy; - proxy.append_array(E->key()); - proxy.append_array(E->get()); + proxy.append_array(E.key); + proxy.append_array(E.value); output.push_back(proxy); } return output; @@ -798,9 +938,9 @@ Array TileSet::map_tile_proxy(int p_source_from, Vector2i p_coords_from, int p_a void TileSet::cleanup_invalid_tile_proxies() { // Source level. Vector<int> source_to_remove; - for (Map<int, int>::Element *E = source_level_proxies.front(); E; E = E->next()) { - if (has_source(E->key())) { - source_to_remove.append(E->key()); + for (const KeyValue<int, int> &E : source_level_proxies) { + if (has_source(E.key)) { + source_to_remove.append(E.key); } } for (int i = 0; i < source_to_remove.size(); i++) { @@ -809,8 +949,8 @@ void TileSet::cleanup_invalid_tile_proxies() { // Coords level. Vector<Array> coords_to_remove; - for (Map<Array, Array>::Element *E = coords_level_proxies.front(); E; E = E->next()) { - Array a = E->key(); + for (const KeyValue<Array, Array> &E : coords_level_proxies) { + Array a = E.key; if (has_source(a[0]) && get_source(a[0])->has_tile(a[1])) { coords_to_remove.append(a); } @@ -822,8 +962,8 @@ void TileSet::cleanup_invalid_tile_proxies() { // Alternative level. Vector<Array> alternative_to_remove; - for (Map<Array, Array>::Element *E = alternative_level_proxies.front(); E; E = E->next()) { - Array a = E->key(); + for (const KeyValue<Array, Array> &E : alternative_level_proxies) { + Array a = E.key; if (has_source(a[0]) && get_source(a[0])->has_tile(a[1]) && get_source(a[0])->has_alternative_tile(a[1], a[2])) { alternative_to_remove.append(a); } @@ -845,10 +985,10 @@ void TileSet::clear_tile_proxies() { Vector<Vector2> TileSet::get_tile_shape_polygon() { Vector<Vector2> points; if (tile_shape == TileSet::TILE_SHAPE_SQUARE) { - points.append(Vector2(0.0, 0.0)); - points.append(Vector2(1.0, 0.0)); - points.append(Vector2(1.0, 1.0)); - points.append(Vector2(0.0, 1.0)); + points.append(Vector2(-0.5, -0.5)); + points.append(Vector2(0.5, -0.5)); + points.append(Vector2(0.5, 0.5)); + points.append(Vector2(-0.5, 0.5)); } else { float overlap = 0.0; switch (tile_shape) { @@ -865,44 +1005,42 @@ Vector<Vector2> TileSet::get_tile_shape_polygon() { break; } - points.append(Vector2(0.5, 0.0)); - points.append(Vector2(0.0, overlap)); - points.append(Vector2(0.0, 1.0 - overlap)); - points.append(Vector2(0.5, 1.0)); - points.append(Vector2(1.0, 1.0 - overlap)); - points.append(Vector2(1.0, overlap)); - points.append(Vector2(0.5, 0.0)); + points.append(Vector2(0.0, -0.5)); + points.append(Vector2(-0.5, overlap - 0.5)); + points.append(Vector2(-0.5, 0.5 - overlap)); + points.append(Vector2(0.0, 0.5)); + points.append(Vector2(0.5, 0.5 - overlap)); + points.append(Vector2(0.5, overlap - 0.5)); if (get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL) { for (int i = 0; i < points.size(); i++) { points.write[i] = Vector2(points[i].y, points[i].x); } } } - for (int i = 0; i < points.size(); i++) { - points.write[i] = points[i] * tile_size - tile_size / 2; - } return points; } -void TileSet::draw_tile_shape(CanvasItem *p_canvas_item, Rect2 p_region, Color p_color, bool p_filled, Ref<Texture2D> p_texture) { +void TileSet::draw_tile_shape(CanvasItem *p_canvas_item, Transform2D p_transform, Color p_color, bool p_filled, Ref<Texture2D> p_texture) { if (tile_meshes_dirty) { - Vector<Vector2> uvs = get_tile_shape_polygon(); - for (int i = 0; i < uvs.size(); i++) { - uvs.write[i] = (uvs[i] + tile_size / 2) / tile_size; + Vector<Vector2> shape = get_tile_shape_polygon(); + Vector<Vector2> uvs; + uvs.resize(shape.size()); + for (int i = 0; i < shape.size(); i++) { + uvs.write[i] = shape[i] + Vector2(0.5, 0.5); } Vector<Color> colors; - colors.resize(uvs.size()); + colors.resize(shape.size()); colors.fill(Color(1.0, 1.0, 1.0, 1.0)); // Filled mesh. tile_filled_mesh->clear_surfaces(); Array a; a.resize(Mesh::ARRAY_MAX); - a[Mesh::ARRAY_VERTEX] = uvs; + a[Mesh::ARRAY_VERTEX] = shape; a[Mesh::ARRAY_TEX_UV] = uvs; a[Mesh::ARRAY_COLOR] = colors; - a[Mesh::ARRAY_INDEX] = Geometry2D::triangulate_polygon(uvs); + a[Mesh::ARRAY_INDEX] = Geometry2D::triangulate_polygon(shape); tile_filled_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a, Array(), Dictionary(), Mesh::ARRAY_FLAG_USE_2D_VERTICES); // Lines mesh. @@ -910,22 +1048,19 @@ void TileSet::draw_tile_shape(CanvasItem *p_canvas_item, Rect2 p_region, Color p a.clear(); a.resize(Mesh::ARRAY_MAX); // Add the first point again when drawing lines. - uvs.push_back(uvs[0]); + shape.push_back(shape[0]); colors.push_back(colors[0]); - a[Mesh::ARRAY_VERTEX] = uvs; + a[Mesh::ARRAY_VERTEX] = shape; a[Mesh::ARRAY_COLOR] = colors; tile_lines_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINE_STRIP, a, Array(), Dictionary(), Mesh::ARRAY_FLAG_USE_2D_VERTICES); tile_meshes_dirty = false; } - Transform2D xform; - xform.scale(p_region.size); - xform.set_origin(p_region.get_position()); if (p_filled) { - p_canvas_item->draw_mesh(tile_filled_mesh, p_texture, xform, p_color); + p_canvas_item->draw_mesh(tile_filled_mesh, p_texture, p_transform, p_color); } else { - p_canvas_item->draw_mesh(tile_lines_mesh, Ref<Texture2D>(), xform, p_color); + p_canvas_item->draw_mesh(tile_lines_mesh, Ref<Texture2D>(), p_transform, p_color); } } @@ -1110,7 +1245,11 @@ Vector<Vector<Ref<Texture2D>>> TileSet::generate_terrains_icons(Size2i p_size) { if (is_valid_peering_bit_terrain(terrain_set, cell_neighbor)) { int terrain = tile_data->get_peering_bit_terrain(cell_neighbor); if (terrain >= 0) { - bit_counts[terrain] += 1; + if (terrain >= (int)bit_counts.size()) { + WARN_PRINT(vformat("Invalid peering bit terrain: %d", terrain)); + } else { + bit_counts[terrain] += 1; + } } } } @@ -1775,8 +1914,8 @@ const int TileSetSource::INVALID_TILE_ALTERNATIVE = -1; #ifndef DISABLE_DEPRECATED void TileSet::_compatibility_conversion() { - for (Map<int, CompatibilityTileData *>::Element *E = compatibility_data.front(); E; E = E->next()) { - CompatibilityTileData *ctd = E->value(); + for (KeyValue<int, CompatibilityTileData *> &E : compatibility_data) { + CompatibilityTileData *ctd = E.value; // Add the texture TileSetAtlasSource *atlas_source = memnew(TileSetAtlasSource); @@ -1814,30 +1953,30 @@ void TileSet::_compatibility_conversion() { value_array.push_back(coords); value_array.push_back(alternative_tile); - if (!compatibility_tilemap_mapping.has(E->key())) { - compatibility_tilemap_mapping[E->key()] = Map<Array, Array>(); + if (!compatibility_tilemap_mapping.has(E.key)) { + compatibility_tilemap_mapping[E.key] = Map<Array, Array>(); } - compatibility_tilemap_mapping[E->key()][key_array] = value_array; - compatibility_tilemap_mapping_tile_modes[E->key()] = COMPATIBILITY_TILE_MODE_SINGLE_TILE; + compatibility_tilemap_mapping[E.key][key_array] = value_array; + compatibility_tilemap_mapping_tile_modes[E.key] = COMPATIBILITY_TILE_MODE_SINGLE_TILE; TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(coords, alternative_tile)); tile_data->set_flip_h(flip_h); tile_data->set_flip_v(flip_v); tile_data->set_transpose(transpose); - tile_data->tile_set_material(ctd->material); + tile_data->set_material(ctd->material); tile_data->set_modulate(ctd->modulate); tile_data->set_z_index(ctd->z_index); if (ctd->occluder.is_valid()) { if (get_occlusion_layers_count() < 1) { - set_occlusion_layers_count(1); + add_occlusion_layer(); } tile_data->set_occluder(0, ctd->occluder); } if (ctd->navigation.is_valid()) { if (get_navigation_layers_count() < 1) { - set_navigation_layers_count(1); + add_navigation_layer(); } tile_data->set_navigation_polygon(0, ctd->autotile_navpoly_map[coords]); } @@ -1847,7 +1986,7 @@ void TileSet::_compatibility_conversion() { // Add the shapes. if (ctd->shapes.size() > 0) { if (get_physics_layers_count() < 1) { - set_physics_layers_count(1); + add_physics_layer(); } } for (int k = 0; k < ctd->shapes.size(); k++) { @@ -1906,29 +2045,29 @@ void TileSet::_compatibility_conversion() { value_array.push_back(coords); value_array.push_back(alternative_tile); - if (!compatibility_tilemap_mapping.has(E->key())) { - compatibility_tilemap_mapping[E->key()] = Map<Array, Array>(); + if (!compatibility_tilemap_mapping.has(E.key)) { + compatibility_tilemap_mapping[E.key] = Map<Array, Array>(); } - compatibility_tilemap_mapping[E->key()][key_array] = value_array; - compatibility_tilemap_mapping_tile_modes[E->key()] = COMPATIBILITY_TILE_MODE_ATLAS_TILE; + compatibility_tilemap_mapping[E.key][key_array] = value_array; + compatibility_tilemap_mapping_tile_modes[E.key] = COMPATIBILITY_TILE_MODE_ATLAS_TILE; TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(coords, alternative_tile)); tile_data->set_flip_h(flip_h); tile_data->set_flip_v(flip_v); tile_data->set_transpose(transpose); - tile_data->tile_set_material(ctd->material); + tile_data->set_material(ctd->material); tile_data->set_modulate(ctd->modulate); tile_data->set_z_index(ctd->z_index); if (ctd->autotile_occluder_map.has(coords)) { if (get_occlusion_layers_count() < 1) { - set_occlusion_layers_count(1); + add_occlusion_layer(); } tile_data->set_occluder(0, ctd->autotile_occluder_map[coords]); } if (ctd->autotile_navpoly_map.has(coords)) { if (get_navigation_layers_count() < 1) { - set_navigation_layers_count(1); + add_navigation_layer(); } tile_data->set_navigation_polygon(0, ctd->autotile_navpoly_map[coords]); } @@ -1942,7 +2081,7 @@ void TileSet::_compatibility_conversion() { // Add the shapes. if (ctd->shapes.size() > 0) { if (get_physics_layers_count() < 1) { - set_physics_layers_count(1); + add_physics_layer(); } } for (int k = 0; k < ctd->shapes.size(); k++) { @@ -1992,8 +2131,8 @@ void TileSet::_compatibility_conversion() { } // Reset compatibility data - for (Map<int, CompatibilityTileData *>::Element *E = compatibility_data.front(); E; E = E->next()) { - memdelete(E->get()); + for (const KeyValue<int, CompatibilityTileData *> &E : compatibility_data) { + memdelete(E.value); } compatibility_data = Map<int, CompatibilityTileData *>(); } @@ -2206,15 +2345,15 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) { ERR_FAIL_COND_V(index < 0, false); if (components[1] == "light_mask") { ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false); - if (index >= occlusion_layers.size()) { - set_occlusion_layers_count(index + 1); + while (index >= occlusion_layers.size()) { + add_occlusion_layer(); } set_occlusion_layer_light_mask(index, p_value); return true; } else if (components[1] == "sdf_collision") { ERR_FAIL_COND_V(p_value.get_type() != Variant::BOOL, false); - if (index >= occlusion_layers.size()) { - set_occlusion_layers_count(index + 1); + while (index >= occlusion_layers.size()) { + add_occlusion_layer(); } set_occlusion_layer_sdf_collision(index, p_value); return true; @@ -2225,23 +2364,22 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) { ERR_FAIL_COND_V(index < 0, false); if (components[1] == "collision_layer") { ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false); - if (index >= physics_layers.size()) { - set_physics_layers_count(index + 1); + while (index >= physics_layers.size()) { + add_physics_layer(); } set_physics_layer_collision_layer(index, p_value); return true; } else if (components[1] == "collision_mask") { ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false); - if (index >= physics_layers.size()) { - set_physics_layers_count(index + 1); + while (index >= physics_layers.size()) { + add_physics_layer(); } set_physics_layer_collision_mask(index, p_value); return true; } else if (components[1] == "physics_material") { Ref<PhysicsMaterial> physics_material = p_value; - ERR_FAIL_COND_V(!physics_material.is_valid(), false); - if (index >= physics_layers.size()) { - set_physics_layers_count(index + 1); + while (index >= physics_layers.size()) { + add_physics_layer(); } set_physics_layer_physics_material(index, physics_material); return true; @@ -2252,37 +2390,30 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) { ERR_FAIL_COND_V(terrain_set_index < 0, false); if (components[1] == "mode") { ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false); - if (terrain_set_index >= terrain_sets.size()) { - set_terrain_sets_count(terrain_set_index + 1); + while (terrain_set_index >= terrain_sets.size()) { + add_terrain_set(); } set_terrain_set_mode(terrain_set_index, TerrainMode(int(p_value))); - } else if (components[1] == "terrains_count") { - ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false); - if (terrain_set_index >= terrain_sets.size()) { - set_terrain_sets_count(terrain_set_index + 1); - } - set_terrains_count(terrain_set_index, p_value); - return true; } else if (components.size() >= 3 && components[1].begins_with("terrain_") && components[1].trim_prefix("terrain_").is_valid_int()) { int terrain_index = components[1].trim_prefix("terrain_").to_int(); ERR_FAIL_COND_V(terrain_index < 0, false); if (components[2] == "name") { ERR_FAIL_COND_V(p_value.get_type() != Variant::STRING, false); - if (terrain_set_index >= terrain_sets.size()) { - set_terrain_sets_count(terrain_set_index + 1); + while (terrain_set_index >= terrain_sets.size()) { + add_terrain_set(); } - if (terrain_index >= terrain_sets[terrain_set_index].terrains.size()) { - set_terrains_count(terrain_set_index, terrain_index + 1); + while (terrain_index >= terrain_sets[terrain_set_index].terrains.size()) { + add_terrain(terrain_set_index); } set_terrain_name(terrain_set_index, terrain_index, p_value); return true; } else if (components[2] == "color") { ERR_FAIL_COND_V(p_value.get_type() != Variant::COLOR, false); - if (terrain_set_index >= terrain_sets.size()) { - set_terrain_sets_count(terrain_set_index + 1); + while (terrain_set_index >= terrain_sets.size()) { + add_terrain_set(); } - if (terrain_index >= terrain_sets[terrain_set_index].terrains.size()) { - set_terrains_count(terrain_set_index, terrain_index + 1); + while (terrain_index >= terrain_sets[terrain_set_index].terrains.size()) { + add_terrain(terrain_set_index); } set_terrain_color(terrain_set_index, terrain_index, p_value); return true; @@ -2294,8 +2425,8 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) { ERR_FAIL_COND_V(index < 0, false); if (components[1] == "layers") { ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false); - if (index >= navigation_layers.size()) { - set_navigation_layers_count(index + 1); + while (index >= navigation_layers.size()) { + add_navigation_layer(); } set_navigation_layer_layers(index, p_value); return true; @@ -2306,15 +2437,15 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) { ERR_FAIL_COND_V(index < 0, false); if (components[1] == "name") { ERR_FAIL_COND_V(p_value.get_type() != Variant::STRING, false); - if (index >= custom_data_layers.size()) { - set_custom_data_layers_count(index + 1); + while (index >= custom_data_layers.size()) { + add_custom_data_layer(); } set_custom_data_name(index, p_value); return true; } else if (components[1] == "type") { ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false); - if (index >= custom_data_layers.size()) { - set_custom_data_layers_count(index + 1); + while (index >= custom_data_layers.size()) { + add_custom_data_layer(); } set_custom_data_type(index, Variant::Type(int(p_value))); return true; @@ -2402,9 +2533,6 @@ bool TileSet::_get(const StringName &p_name, Variant &r_ret) const { if (components[1] == "mode") { r_ret = get_terrain_set_mode(terrain_set_index); return true; - } else if (components[1] == "terrains_count") { - r_ret = get_terrains_count(terrain_set_index); - return true; } else if (components.size() >= 3 && components[1].begins_with("terrain_") && components[1].trim_prefix("terrain_").is_valid_int()) { int terrain_index = components[1].trim_prefix("terrain_").to_int(); if (terrain_index < 0 || terrain_index >= terrain_sets[terrain_set_index].terrains.size()) { @@ -2454,25 +2582,25 @@ bool TileSet::_get(const StringName &p_name, Variant &r_ret) const { } else if (components.size() == 2 && components[0] == "tile_proxies") { if (components[1] == "source_level") { Array a; - for (Map<int, int>::Element *E = source_level_proxies.front(); E; E = E->next()) { - a.push_back(E->key()); - a.push_back(E->get()); + for (const KeyValue<int, int> &E : source_level_proxies) { + a.push_back(E.key); + a.push_back(E.value); } r_ret = a; return true; } else if (components[1] == "coords_level") { Array a; - for (Map<Array, Array>::Element *E = coords_level_proxies.front(); E; E = E->next()) { - a.push_back(E->key()); - a.push_back(E->get()); + for (const KeyValue<Array, Array> &E : coords_level_proxies) { + a.push_back(E.key); + a.push_back(E.value); } r_ret = a; return true; } else if (components[1] == "alternative_level") { Array a; - for (Map<Array, Array>::Element *E = alternative_level_proxies.front(); E; E = E->next()) { - a.push_back(E->key()); - a.push_back(E->get()); + for (const KeyValue<Array, Array> &E : alternative_level_proxies) { + a.push_back(E.key); + a.push_back(E.value); } r_ret = a; return true; @@ -2522,7 +2650,7 @@ void TileSet::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::NIL, "Terrains", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); for (int terrain_set_index = 0; terrain_set_index < terrain_sets.size(); terrain_set_index++) { p_list->push_back(PropertyInfo(Variant::INT, vformat("terrain_set_%d/mode", terrain_set_index), PROPERTY_HINT_ENUM, "Match corners and sides,Match corners,Match sides")); - p_list->push_back(PropertyInfo(Variant::INT, vformat("terrain_set_%d/terrains_count", terrain_set_index), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); + p_list->push_back(PropertyInfo(Variant::NIL, vformat("terrain_set_%d/terrains", terrain_set_index), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ARRAY, vformat("terrain_set_%d/terrain_", terrain_set_index))); for (int terrain_index = 0; terrain_index < terrain_sets[terrain_set_index].terrains.size(); terrain_index++) { p_list->push_back(PropertyInfo(Variant::STRING, vformat("terrain_set_%d/terrain_%d/name", terrain_set_index, terrain_index))); p_list->push_back(PropertyInfo(Variant::COLOR, vformat("terrain_set_%d/terrain_%d/color", terrain_set_index, terrain_index))); @@ -2548,8 +2676,8 @@ void TileSet::_get_property_list(List<PropertyInfo> *p_list) const { // Sources. // Note: sources have to be listed in at the end as some TileData rely on the TileSet properties being initialized first. - for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) { - p_list->push_back(PropertyInfo(Variant::INT, vformat("sources/%d", E_source->key()), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + for (const KeyValue<int, Ref<TileSetSource>> &E_source : sources) { + p_list->push_back(PropertyInfo(Variant::INT, vformat("sources/%d", E_source.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); } // Tile Proxies. @@ -2560,16 +2688,24 @@ void TileSet::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::ARRAY, "tile_proxies/alternative_level", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); } +void TileSet::_validate_property(PropertyInfo &property) const { + if (property.name == "tile_layout" && tile_shape == TILE_SHAPE_SQUARE) { + property.usage ^= PROPERTY_USAGE_READ_ONLY; + } else if (property.name == "tile_offset_axis" && tile_shape == TILE_SHAPE_SQUARE) { + property.usage ^= PROPERTY_USAGE_READ_ONLY; + } +} + void TileSet::_bind_methods() { // Sources management. ClassDB::bind_method(D_METHOD("get_next_source_id"), &TileSet::get_next_source_id); - ClassDB::bind_method(D_METHOD("add_source", "atlas_source_id_override"), &TileSet::add_source, DEFVAL(TileSet::INVALID_SOURCE)); + ClassDB::bind_method(D_METHOD("add_source", "source", "atlas_source_id_override"), &TileSet::add_source, DEFVAL(TileSet::INVALID_SOURCE)); ClassDB::bind_method(D_METHOD("remove_source", "source_id"), &TileSet::remove_source); - ClassDB::bind_method(D_METHOD("set_source_id", "source_id"), &TileSet::set_source_id); + ClassDB::bind_method(D_METHOD("set_source_id", "source_id", "new_source_id"), &TileSet::set_source_id); ClassDB::bind_method(D_METHOD("get_source_count"), &TileSet::get_source_count); ClassDB::bind_method(D_METHOD("get_source_id", "index"), &TileSet::get_source_id); - ClassDB::bind_method(D_METHOD("has_source", "index"), &TileSet::has_source); - ClassDB::bind_method(D_METHOD("get_source", "index"), &TileSet::get_source); + ClassDB::bind_method(D_METHOD("has_source", "source_id"), &TileSet::has_source); + ClassDB::bind_method(D_METHOD("get_source", "source_id"), &TileSet::get_source); // Shape and layout. ClassDB::bind_method(D_METHOD("set_tile_shape", "shape"), &TileSet::set_tile_shape); @@ -2590,16 +2726,20 @@ void TileSet::_bind_methods() { ClassDB::bind_method(D_METHOD("set_uv_clipping", "uv_clipping"), &TileSet::set_uv_clipping); ClassDB::bind_method(D_METHOD("is_uv_clipping"), &TileSet::is_uv_clipping); - ClassDB::bind_method(D_METHOD("set_occlusion_layers_count", "occlusion_layers_count"), &TileSet::set_occlusion_layers_count); ClassDB::bind_method(D_METHOD("get_occlusion_layers_count"), &TileSet::get_occlusion_layers_count); + ClassDB::bind_method(D_METHOD("add_occlusion_layer", "to_position"), &TileSet::add_occlusion_layer, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("move_occlusion_layer", "layer_index", "to_position"), &TileSet::move_occlusion_layer); + ClassDB::bind_method(D_METHOD("remove_occlusion_layer", "layer_index"), &TileSet::remove_occlusion_layer); ClassDB::bind_method(D_METHOD("set_occlusion_layer_light_mask", "layer_index", "light_mask"), &TileSet::set_occlusion_layer_light_mask); - ClassDB::bind_method(D_METHOD("get_occlusion_layer_light_mask"), &TileSet::get_occlusion_layer_light_mask); + ClassDB::bind_method(D_METHOD("get_occlusion_layer_light_mask", "layer_index"), &TileSet::get_occlusion_layer_light_mask); ClassDB::bind_method(D_METHOD("set_occlusion_layer_sdf_collision", "layer_index", "sdf_collision"), &TileSet::set_occlusion_layer_sdf_collision); - ClassDB::bind_method(D_METHOD("get_occlusion_layer_sdf_collision"), &TileSet::get_occlusion_layer_sdf_collision); + ClassDB::bind_method(D_METHOD("get_occlusion_layer_sdf_collision", "layer_index"), &TileSet::get_occlusion_layer_sdf_collision); // Physics - ClassDB::bind_method(D_METHOD("set_physics_layers_count", "physics_layers_count"), &TileSet::set_physics_layers_count); ClassDB::bind_method(D_METHOD("get_physics_layers_count"), &TileSet::get_physics_layers_count); + ClassDB::bind_method(D_METHOD("add_physics_layer", "to_position"), &TileSet::add_physics_layer, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("move_physics_layer", "layer_index", "to_position"), &TileSet::move_physics_layer); + ClassDB::bind_method(D_METHOD("remove_physics_layer", "layer_index"), &TileSet::remove_physics_layer); ClassDB::bind_method(D_METHOD("set_physics_layer_collision_layer", "layer_index", "layer"), &TileSet::set_physics_layer_collision_layer); ClassDB::bind_method(D_METHOD("get_physics_layer_collision_layer", "layer_index"), &TileSet::get_physics_layer_collision_layer); ClassDB::bind_method(D_METHOD("set_physics_layer_collision_mask", "layer_index", "mask"), &TileSet::set_physics_layer_collision_mask); @@ -2608,27 +2748,35 @@ void TileSet::_bind_methods() { ClassDB::bind_method(D_METHOD("get_physics_layer_physics_material", "layer_index"), &TileSet::get_physics_layer_physics_material); // Terrains - ClassDB::bind_method(D_METHOD("set_terrain_sets_count", "terrain_sets_count"), &TileSet::set_terrain_sets_count); ClassDB::bind_method(D_METHOD("get_terrain_sets_count"), &TileSet::get_terrain_sets_count); + ClassDB::bind_method(D_METHOD("add_terrain_set", "to_position"), &TileSet::add_terrain_set, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("move_terrain_set", "terrain_set", "to_position"), &TileSet::move_terrain_set); + ClassDB::bind_method(D_METHOD("remove_terrain_set", "terrain_set"), &TileSet::remove_terrain_set); ClassDB::bind_method(D_METHOD("set_terrain_set_mode", "terrain_set", "mode"), &TileSet::set_terrain_set_mode); ClassDB::bind_method(D_METHOD("get_terrain_set_mode", "terrain_set"), &TileSet::get_terrain_set_mode); - ClassDB::bind_method(D_METHOD("set_terrains_count", "terrain_set", "terrains_count"), &TileSet::set_terrains_count); ClassDB::bind_method(D_METHOD("get_terrains_count", "terrain_set"), &TileSet::get_terrains_count); + ClassDB::bind_method(D_METHOD("add_terrain", "terrain_set", "to_position"), &TileSet::add_terrain, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("move_terrain", "terrain_set", "terrain_index", "to_position"), &TileSet::move_terrain); + ClassDB::bind_method(D_METHOD("remove_terrain", "terrain_set", "terrain_index"), &TileSet::remove_terrain); ClassDB::bind_method(D_METHOD("set_terrain_name", "terrain_set", "terrain_index", "name"), &TileSet::set_terrain_name); ClassDB::bind_method(D_METHOD("get_terrain_name", "terrain_set", "terrain_index"), &TileSet::get_terrain_name); ClassDB::bind_method(D_METHOD("set_terrain_color", "terrain_set", "terrain_index", "color"), &TileSet::set_terrain_color); ClassDB::bind_method(D_METHOD("get_terrain_color", "terrain_set", "terrain_index"), &TileSet::get_terrain_color); // Navigation - ClassDB::bind_method(D_METHOD("set_navigation_layers_count", "navigation_layers_count"), &TileSet::set_navigation_layers_count); ClassDB::bind_method(D_METHOD("get_navigation_layers_count"), &TileSet::get_navigation_layers_count); + ClassDB::bind_method(D_METHOD("add_navigation_layer", "to_position"), &TileSet::add_navigation_layer, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("move_navigation_layer", "layer_index", "to_position"), &TileSet::move_navigation_layer); + ClassDB::bind_method(D_METHOD("remove_navigation_layer", "layer_index"), &TileSet::remove_navigation_layer); ClassDB::bind_method(D_METHOD("set_navigation_layer_layers", "layer_index", "layers"), &TileSet::set_navigation_layer_layers); ClassDB::bind_method(D_METHOD("get_navigation_layer_layers", "layer_index"), &TileSet::get_navigation_layer_layers); // Custom data - ClassDB::bind_method(D_METHOD("set_custom_data_layers_count", "custom_data_layers_count"), &TileSet::set_custom_data_layers_count); ClassDB::bind_method(D_METHOD("get_custom_data_layers_count"), &TileSet::get_custom_data_layers_count); + ClassDB::bind_method(D_METHOD("add_custom_data_layer", "to_position"), &TileSet::add_custom_data_layer, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("move_custom_data_layer", "layer_index", "to_position"), &TileSet::move_custom_data_layer); + ClassDB::bind_method(D_METHOD("remove_custom_data_layer", "layer_index"), &TileSet::remove_custom_data_layer); // Tile proxies ClassDB::bind_method(D_METHOD("set_source_level_tile_proxy", "source_from", "source_to"), &TileSet::set_source_level_tile_proxy); @@ -2653,19 +2801,19 @@ void TileSet::_bind_methods() { ADD_GROUP("Rendering", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uv_clipping"), "set_uv_clipping", "is_uv_clipping"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "occlusion_layers_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_occlusion_layers_count", "get_occlusion_layers_count"); + ADD_ARRAY("occlusion_layers", "occlusion_layer_"); ADD_GROUP("Physics", ""); - ADD_PROPERTY(PropertyInfo(Variant::INT, "physics_layers_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_physics_layers_count", "get_physics_layers_count"); + ADD_ARRAY("physics_layers", "physics_layer_"); ADD_GROUP("Terrains", ""); - ADD_PROPERTY(PropertyInfo(Variant::INT, "terrains_sets_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_terrain_sets_count", "get_terrain_sets_count"); + ADD_ARRAY("terrain_sets", "terrain_set_"); ADD_GROUP("Navigation", ""); - ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_navigation_layers_count", "get_navigation_layers_count"); + ADD_ARRAY("navigation_layers", "navigation_layer_"); ADD_GROUP("Custom data", ""); - ADD_PROPERTY(PropertyInfo(Variant::INT, "custom_data_layers_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_custom_data_layers_count", "get_custom_data_layers_count"); + ADD_ARRAY("custom_data_layers", "custom_data_layer_"); // -- Enum binding -- BIND_ENUM_CONSTANT(TILE_SHAPE_SQUARE); @@ -2713,8 +2861,8 @@ TileSet::TileSet() { TileSet::~TileSet() { #ifndef DISABLE_DEPRECATED - for (Map<int, CompatibilityTileData *>::Element *E = compatibility_data.front(); E; E = E->next()) { - memdelete(E->get()); + for (const KeyValue<int, CompatibilityTileData *> &E : compatibility_data) { + memdelete(E.value); } #endif // DISABLE_DEPRECATED while (!source_ids.is_empty()) { @@ -2728,33 +2876,189 @@ void TileSetSource::set_tile_set(const TileSet *p_tile_set) { tile_set = p_tile_set; } +void TileSetSource::_bind_methods() { + // Base tiles + ClassDB::bind_method(D_METHOD("get_tiles_count"), &TileSetSource::get_tiles_count); + ClassDB::bind_method(D_METHOD("get_tile_id", "index"), &TileSetSource::get_tile_id); + ClassDB::bind_method(D_METHOD("has_tile", "atlas_coords"), &TileSetSource::has_tile); + + // Alternative tiles + ClassDB::bind_method(D_METHOD("get_alternative_tiles_count", "atlas_coords"), &TileSetSource::get_alternative_tiles_count); + ClassDB::bind_method(D_METHOD("get_alternative_tile_id", "atlas_coords", "index"), &TileSetSource::get_alternative_tile_id); + ClassDB::bind_method(D_METHOD("has_alternative_tile", "atlas_coords", "alternative_tile"), &TileSetSource::has_alternative_tile); +} + /////////////////////////////// TileSetAtlasSource ////////////////////////////////////// void TileSetAtlasSource::set_tile_set(const TileSet *p_tile_set) { tile_set = p_tile_set; // Set the TileSet on all TileData. - for (Map<Vector2i, TileAlternativesData>::Element *E_tile = tiles.front(); E_tile; E_tile = E_tile->next()) { - for (Map<int, TileData *>::Element *E_alternative = E_tile->get().alternatives.front(); E_alternative; E_alternative = E_alternative->next()) { - E_alternative->get()->set_tile_set(tile_set); + for (KeyValue<Vector2i, TileAlternativesData> &E_tile : tiles) { + for (KeyValue<int, TileData *> &E_alternative : E_tile.value.alternatives) { + E_alternative.value->set_tile_set(tile_set); } } } void TileSetAtlasSource::notify_tile_data_properties_should_change() { // Set the TileSet on all TileData. - for (Map<Vector2i, TileAlternativesData>::Element *E_tile = tiles.front(); E_tile; E_tile = E_tile->next()) { - for (Map<int, TileData *>::Element *E_alternative = E_tile->get().alternatives.front(); E_alternative; E_alternative = E_alternative->next()) { - E_alternative->get()->notify_tile_data_properties_should_change(); + for (KeyValue<Vector2i, TileAlternativesData> &E_tile : tiles) { + for (KeyValue<int, TileData *> &E_alternative : E_tile.value.alternatives) { + E_alternative.value->notify_tile_data_properties_should_change(); + } + } +} + +void TileSetAtlasSource::add_occlusion_layer(int p_to_pos) { + for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) { + for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) { + E_alternative.value->add_occlusion_layer(p_to_pos); + } + } +} + +void TileSetAtlasSource::move_occlusion_layer(int p_from_index, int p_to_pos) { + for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) { + for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) { + E_alternative.value->move_occlusion_layer(p_from_index, p_to_pos); + } + } +} + +void TileSetAtlasSource::remove_occlusion_layer(int p_index) { + for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) { + for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) { + E_alternative.value->remove_occlusion_layer(p_index); + } + } +} + +void TileSetAtlasSource::add_physics_layer(int p_to_pos) { + for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) { + for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) { + E_alternative.value->add_physics_layer(p_to_pos); + } + } +} + +void TileSetAtlasSource::move_physics_layer(int p_from_index, int p_to_pos) { + for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) { + for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) { + E_alternative.value->move_physics_layer(p_from_index, p_to_pos); + } + } +} + +void TileSetAtlasSource::remove_physics_layer(int p_index) { + for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) { + for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) { + E_alternative.value->remove_physics_layer(p_index); + } + } +} + +void TileSetAtlasSource::add_terrain_set(int p_to_pos) { + for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) { + for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) { + E_alternative.value->add_terrain_set(p_to_pos); + } + } +} + +void TileSetAtlasSource::move_terrain_set(int p_from_index, int p_to_pos) { + for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) { + for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) { + E_alternative.value->move_terrain_set(p_from_index, p_to_pos); + } + } +} + +void TileSetAtlasSource::remove_terrain_set(int p_index) { + for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) { + for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) { + E_alternative.value->remove_terrain_set(p_index); + } + } +} + +void TileSetAtlasSource::add_terrain(int p_terrain_set, int p_to_pos) { + for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) { + for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) { + E_alternative.value->add_terrain(p_terrain_set, p_to_pos); + } + } +} + +void TileSetAtlasSource::move_terrain(int p_terrain_set, int p_from_index, int p_to_pos) { + for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) { + for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) { + E_alternative.value->move_terrain(p_terrain_set, p_from_index, p_to_pos); + } + } +} + +void TileSetAtlasSource::remove_terrain(int p_terrain_set, int p_index) { + for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) { + for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) { + E_alternative.value->remove_terrain(p_terrain_set, p_index); + } + } +} + +void TileSetAtlasSource::add_navigation_layer(int p_to_pos) { + for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) { + for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) { + E_alternative.value->add_navigation_layer(p_to_pos); + } + } +} + +void TileSetAtlasSource::move_navigation_layer(int p_from_index, int p_to_pos) { + for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) { + for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) { + E_alternative.value->move_navigation_layer(p_from_index, p_to_pos); + } + } +} + +void TileSetAtlasSource::remove_navigation_layer(int p_index) { + for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) { + for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) { + E_alternative.value->remove_navigation_layer(p_index); + } + } +} + +void TileSetAtlasSource::add_custom_data_layer(int p_to_pos) { + for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) { + for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) { + E_alternative.value->add_custom_data_layer(p_to_pos); + } + } +} + +void TileSetAtlasSource::move_custom_data_layer(int p_from_index, int p_to_pos) { + for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) { + for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) { + E_alternative.value->move_custom_data_layer(p_from_index, p_to_pos); + } + } +} + +void TileSetAtlasSource::remove_custom_data_layer(int p_index) { + for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) { + for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) { + E_alternative.value->remove_custom_data_layer(p_index); } } } void TileSetAtlasSource::reset_state() { // Reset all TileData. - for (Map<Vector2i, TileAlternativesData>::Element *E_tile = tiles.front(); E_tile; E_tile = E_tile->next()) { - for (Map<int, TileData *>::Element *E_alternative = E_tile->get().alternatives.front(); E_alternative; E_alternative = E_alternative->next()) { - E_alternative->get()->reset_state(); + for (KeyValue<Vector2i, TileAlternativesData> &E_tile : tiles) { + for (KeyValue<int, TileData *> &E_alternative : E_tile.value.alternatives) { + E_alternative.value->reset_state(); } } } @@ -2850,8 +3154,32 @@ bool TileSetAtlasSource::_set(const StringName &p_name, const Variant &p_value) // Properties. if (components[1] == "size_in_atlas") { move_tile_in_atlas(coords, coords, p_value); + return true; } else if (components[1] == "next_alternative_id") { tiles[coords].next_alternative_id = p_value; + return true; + } else if (components[1] == "animation_columns") { + set_tile_animation_columns(coords, p_value); + return true; + } else if (components[1] == "animation_separation") { + set_tile_animation_separation(coords, p_value); + return true; + } else if (components[1] == "animation_speed") { + set_tile_animation_speed(coords, p_value); + return true; + } else if (components[1] == "animation_frames_count") { + set_tile_animation_frames_count(coords, p_value); + return true; + } else if (components.size() >= 3 && components[1].begins_with("animation_frame_") && components[1].trim_prefix("animation_frame_").is_valid_int()) { + int frame = components[1].trim_prefix("animation_frame_").to_int(); + if (components[2] == "duration") { + if (frame >= get_tile_animation_frames_count(coords)) { + set_tile_animation_frames_count(coords, frame + 1); + } + set_tile_animation_frame_duration(coords, frame, p_value); + return true; + } + return false; } else if (components[1].is_valid_int()) { int alternative_id = components[1].to_int(); if (alternative_id != TileSetSource::INVALID_TILE_ALTERNATIVE) { @@ -2897,6 +3225,28 @@ bool TileSetAtlasSource::_get(const StringName &p_name, Variant &r_ret) const { } else if (components[1] == "next_alternative_id") { r_ret = tiles[coords].next_alternative_id; return true; + } else if (components[1] == "animation_columns") { + r_ret = get_tile_animation_columns(coords); + return true; + } else if (components[1] == "animation_separation") { + r_ret = get_tile_animation_separation(coords); + return true; + } else if (components[1] == "animation_speed") { + r_ret = get_tile_animation_speed(coords); + return true; + } else if (components[1] == "animation_frames_count") { + r_ret = get_tile_animation_frames_count(coords); + return true; + } else if (components.size() >= 3 && components[1].begins_with("animation_frame_") && components[1].trim_prefix("animation_frame_").is_valid_int()) { + int frame = components[1].trim_prefix("animation_frame_").to_int(); + if (frame < 0 || frame >= get_tile_animation_frames_count(coords)) { + return false; + } + if (components[2] == "duration") { + r_ret = get_tile_animation_frame_duration(coords, frame); + return true; + } + return false; } else if (components[1].is_valid_int()) { int alternative_id = components[1].to_int(); if (alternative_id != TileSetSource::INVALID_TILE_ALTERNATIVE && tiles[coords].alternatives.has(alternative_id)) { @@ -2921,45 +3271,78 @@ bool TileSetAtlasSource::_get(const StringName &p_name, Variant &r_ret) const { void TileSetAtlasSource::_get_property_list(List<PropertyInfo> *p_list) const { // Atlases data. PropertyInfo property_info; - for (Map<Vector2i, TileAlternativesData>::Element *E_tile = tiles.front(); E_tile; E_tile = E_tile->next()) { + for (const KeyValue<Vector2i, TileAlternativesData> &E_tile : tiles) { List<PropertyInfo> tile_property_list; // size_in_atlas property_info = PropertyInfo(Variant::VECTOR2I, "size_in_atlas", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR); - if (E_tile->get().size_in_atlas == Vector2i(1, 1)) { + if (E_tile.value.size_in_atlas == Vector2i(1, 1)) { property_info.usage ^= PROPERTY_USAGE_STORAGE; } tile_property_list.push_back(property_info); // next_alternative_id property_info = PropertyInfo(Variant::INT, "next_alternative_id", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR); - if (E_tile->get().next_alternative_id == 1) { + if (E_tile.value.next_alternative_id == 1) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + tile_property_list.push_back(property_info); + + // animation_columns. + property_info = PropertyInfo(Variant::INT, "animation_columns", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR); + if (E_tile.value.animation_columns == 0) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + tile_property_list.push_back(property_info); + + // animation_separation. + property_info = PropertyInfo(Variant::INT, "animation_separation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR); + if (E_tile.value.animation_separation == Vector2i()) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + tile_property_list.push_back(property_info); + + // animation_speed. + property_info = PropertyInfo(Variant::FLOAT, "animation_speed", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR); + if (E_tile.value.animation_speed == 1.0) { property_info.usage ^= PROPERTY_USAGE_STORAGE; } tile_property_list.push_back(property_info); - for (Map<int, TileData *>::Element *E_alternative = E_tile->get().alternatives.front(); E_alternative; E_alternative = E_alternative->next()) { + // animation_frames_count. + tile_property_list.push_back(PropertyInfo(Variant::INT, "animation_frames_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NETWORK)); + + // animation_frame_*. + bool store_durations = tiles[E_tile.key].animation_frames_durations.size() >= 2; + for (int i = 0; i < (int)tiles[E_tile.key].animation_frames_durations.size(); i++) { + property_info = PropertyInfo(Variant::FLOAT, vformat("animation_frame_%d/duration", i), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR); + if (!store_durations) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + tile_property_list.push_back(property_info); + } + + for (const KeyValue<int, TileData *> &E_alternative : E_tile.value.alternatives) { // Add a dummy property to show the alternative exists. - tile_property_list.push_back(PropertyInfo(Variant::INT, vformat("%d", E_alternative->key()), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + tile_property_list.push_back(PropertyInfo(Variant::INT, vformat("%d", E_alternative.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); // Get the alternative tile's properties and append them to the list of properties. List<PropertyInfo> alternative_property_list; - E_alternative->get()->get_property_list(&alternative_property_list); + E_alternative.value->get_property_list(&alternative_property_list); for (PropertyInfo &alternative_property_info : alternative_property_list) { - bool valid; - Variant default_value = ClassDB::class_get_default_property_value("TileData", alternative_property_info.name, &valid); - Variant value = E_alternative->get()->get(alternative_property_info.name); - if (valid && value == default_value) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; + Variant default_value = ClassDB::class_get_default_property_value("TileData", alternative_property_info.name); + Variant value = E_alternative.value->get(alternative_property_info.name); + if (default_value.get_type() != Variant::NIL && bool(Variant::evaluate(Variant::OP_EQUAL, value, default_value))) { + alternative_property_info.usage ^= PROPERTY_USAGE_STORAGE; } - alternative_property_info.name = vformat("%s/%s", vformat("%d", E_alternative->key()), alternative_property_info.name); + alternative_property_info.name = vformat("%s/%s", vformat("%d", E_alternative.key), alternative_property_info.name); tile_property_list.push_back(alternative_property_info); } } // Add all alternative. for (PropertyInfo &tile_property_info : tile_property_list) { - tile_property_info.name = vformat("%s/%s", vformat("%d:%d", E_tile->key().x, E_tile->key().y), tile_property_info.name); + tile_property_info.name = vformat("%s/%s", vformat("%d:%d", E_tile.key.x, E_tile.key.y), tile_property_info.name); p_list->push_back(tile_property_info); } } @@ -2969,33 +3352,27 @@ void TileSetAtlasSource::create_tile(const Vector2i p_atlas_coords, const Vector // Create a tile if it does not exists. ERR_FAIL_COND(p_atlas_coords.x < 0 || p_atlas_coords.y < 0); ERR_FAIL_COND(p_size.x <= 0 || p_size.y <= 0); - for (int x = 0; x < p_size.x; x++) { - for (int y = 0; y < p_size.y; y++) { - Vector2i coords = p_atlas_coords + Vector2i(x, y); - ERR_FAIL_COND_MSG(tiles.has(coords), vformat("Cannot create tile at position %s with size %s. Already a tile present at %s.", p_atlas_coords, p_size, coords)); - } - } + + bool room_for_tile = has_room_for_tile(p_atlas_coords, p_size, 1, Vector2i(), 1); + ERR_FAIL_COND_MSG(!room_for_tile, "Cannot create tile, tiles are already present in the space the tile would cover."); + + // Initialize the tile data. + TileAlternativesData tad; + tad.size_in_atlas = p_size; + tad.animation_frames_durations.push_back(1.0); + tad.alternatives[0] = memnew(TileData); + tad.alternatives[0]->set_tile_set(tile_set); + tad.alternatives[0]->set_allow_transform(false); + tad.alternatives[0]->connect("changed", callable_mp((Resource *)this, &TileSetAtlasSource::emit_changed)); + tad.alternatives[0]->notify_property_list_changed(); + tad.alternatives_ids.append(0); // Create and resize the tile. - tiles.insert(p_atlas_coords, TileSetAtlasSource::TileAlternativesData()); + tiles.insert(p_atlas_coords, tad); tiles_ids.append(p_atlas_coords); tiles_ids.sort(); - tiles[p_atlas_coords].size_in_atlas = p_size; - tiles[p_atlas_coords].alternatives[0] = memnew(TileData); - tiles[p_atlas_coords].alternatives[0]->set_tile_set(tile_set); - tiles[p_atlas_coords].alternatives[0]->set_allow_transform(false); - tiles[p_atlas_coords].alternatives[0]->connect("changed", callable_mp((Resource *)this, &TileSetAtlasSource::emit_changed)); - tiles[p_atlas_coords].alternatives[0]->notify_property_list_changed(); - tiles[p_atlas_coords].alternatives_ids.append(0); - - // Add all covered positions to the mapping cache - for (int x = 0; x < p_size.x; x++) { - for (int y = 0; y < p_size.y; y++) { - Vector2i coords = p_atlas_coords + Vector2i(x, y); - _coords_mapping_cache[coords] = p_atlas_coords; - } - } + _create_coords_mapping_cache(p_atlas_coords); emit_signal(SNAME("changed")); } @@ -3004,18 +3381,11 @@ void TileSetAtlasSource::remove_tile(Vector2i p_atlas_coords) { ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); // Remove all covered positions from the mapping cache - Size2i size = tiles[p_atlas_coords].size_in_atlas; - - for (int x = 0; x < size.x; x++) { - for (int y = 0; y < size.y; y++) { - Vector2i coords = p_atlas_coords + Vector2i(x, y); - _coords_mapping_cache.erase(coords); - } - } + _clear_coords_mapping_cache(p_atlas_coords); // Free tile data. - for (Map<int, TileData *>::Element *E_tile_data = tiles[p_atlas_coords].alternatives.front(); E_tile_data; E_tile_data = E_tile_data->next()) { - memdelete(E_tile_data->get()); + for (const KeyValue<int, TileData *> &E_tile_data : tiles[p_atlas_coords].alternatives) { + memdelete(E_tile_data.value); } // Delete the tile @@ -3038,6 +3408,118 @@ Vector2i TileSetAtlasSource::get_tile_at_coords(Vector2i p_atlas_coords) const { return _coords_mapping_cache[p_atlas_coords]; } +void TileSetAtlasSource::set_tile_animation_columns(const Vector2i p_atlas_coords, int p_frame_columns) { + ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + ERR_FAIL_COND(p_frame_columns < 0); + + TileAlternativesData &tad = tiles[p_atlas_coords]; + bool room_for_tile = has_room_for_tile(p_atlas_coords, tad.size_in_atlas, p_frame_columns, tad.animation_separation, tad.animation_frames_durations.size(), p_atlas_coords); + ERR_FAIL_COND_MSG(!room_for_tile, "Cannot set animation columns count, tiles are already present in the space the tile would cover."); + + _clear_coords_mapping_cache(p_atlas_coords); + + tiles[p_atlas_coords].animation_columns = p_frame_columns; + + _create_coords_mapping_cache(p_atlas_coords); + + emit_signal(SNAME("changed")); +} + +int TileSetAtlasSource::get_tile_animation_columns(const Vector2i p_atlas_coords) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), 1, vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + return tiles[p_atlas_coords].animation_columns; +} + +void TileSetAtlasSource::set_tile_animation_separation(const Vector2i p_atlas_coords, const Vector2i p_separation) { + ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + ERR_FAIL_COND(p_separation.x < 0 || p_separation.y < 0); + + TileAlternativesData &tad = tiles[p_atlas_coords]; + bool room_for_tile = has_room_for_tile(p_atlas_coords, tad.size_in_atlas, tad.animation_columns, p_separation, tad.animation_frames_durations.size(), p_atlas_coords); + ERR_FAIL_COND_MSG(!room_for_tile, "Cannot set animation columns count, tiles are already present in the space the tile would cover."); + + _clear_coords_mapping_cache(p_atlas_coords); + + tiles[p_atlas_coords].animation_separation = p_separation; + + _create_coords_mapping_cache(p_atlas_coords); + + emit_signal(SNAME("changed")); +} + +Vector2i TileSetAtlasSource::get_tile_animation_separation(const Vector2i p_atlas_coords) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), Vector2i(), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + return tiles[p_atlas_coords].animation_separation; +} + +void TileSetAtlasSource::set_tile_animation_speed(const Vector2i p_atlas_coords, real_t p_speed) { + ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + ERR_FAIL_COND(p_speed <= 0); + + tiles[p_atlas_coords].animation_speed = p_speed; + + emit_signal(SNAME("changed")); +} + +real_t TileSetAtlasSource::get_tile_animation_speed(const Vector2i p_atlas_coords) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), 1.0, vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + return tiles[p_atlas_coords].animation_speed; +} + +void TileSetAtlasSource::set_tile_animation_frames_count(const Vector2i p_atlas_coords, int p_frames_count) { + ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + ERR_FAIL_COND(p_frames_count < 1); + + TileAlternativesData &tad = tiles[p_atlas_coords]; + bool room_for_tile = has_room_for_tile(p_atlas_coords, tad.size_in_atlas, tad.animation_columns, tad.animation_separation, p_frames_count, p_atlas_coords); + ERR_FAIL_COND_MSG(!room_for_tile, "Cannot set animation columns count, tiles are already present in the space the tile would cover."); + + _clear_coords_mapping_cache(p_atlas_coords); + + int old_size = tiles[p_atlas_coords].animation_frames_durations.size(); + tiles[p_atlas_coords].animation_frames_durations.resize(p_frames_count); + for (int i = old_size; i < p_frames_count; i++) { + tiles[p_atlas_coords].animation_frames_durations[i] = 1.0; + } + + _create_coords_mapping_cache(p_atlas_coords); + + notify_property_list_changed(); + + emit_signal(SNAME("changed")); +} + +int TileSetAtlasSource::get_tile_animation_frames_count(const Vector2i p_atlas_coords) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), 1, vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + return tiles[p_atlas_coords].animation_frames_durations.size(); +} + +void TileSetAtlasSource::set_tile_animation_frame_duration(const Vector2i p_atlas_coords, int p_frame_index, real_t p_duration) { + ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + ERR_FAIL_INDEX(p_frame_index, (int)tiles[p_atlas_coords].animation_frames_durations.size()); + ERR_FAIL_COND(p_duration <= 0.0); + + tiles[p_atlas_coords].animation_frames_durations[p_frame_index] = p_duration; + + emit_signal(SNAME("changed")); +} + +real_t TileSetAtlasSource::get_tile_animation_frame_duration(const Vector2i p_atlas_coords, int p_frame_index) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), 1, vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + ERR_FAIL_INDEX_V(p_frame_index, (int)tiles[p_atlas_coords].animation_frames_durations.size(), 0.0); + return tiles[p_atlas_coords].animation_frames_durations[p_frame_index]; +} + +real_t TileSetAtlasSource::get_tile_animation_total_duration(const Vector2i p_atlas_coords) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), 1, vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + + real_t sum = 0.0; + for (int frame = 0; frame < (int)tiles[p_atlas_coords].animation_frames_durations.size(); frame++) { + sum += tiles[p_atlas_coords].animation_frames_durations[frame]; + } + return sum; +} + Vector2i TileSetAtlasSource::get_tile_size_in_atlas(Vector2i p_atlas_coords) const { ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), Vector2i(-1, -1), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); @@ -3053,16 +3535,46 @@ Vector2i TileSetAtlasSource::get_tile_id(int p_index) const { return tiles_ids[p_index]; } -Rect2i TileSetAtlasSource::get_tile_texture_region(Vector2i p_atlas_coords) const { +bool TileSetAtlasSource::has_room_for_tile(Vector2i p_atlas_coords, Vector2i p_size, int p_animation_columns, Vector2i p_animation_separation, int p_frames_count, Vector2i p_ignored_tile) const { + if (p_atlas_coords.x < 0 || p_atlas_coords.y < 0) { + return false; + } + if (p_size.x <= 0 || p_size.y <= 0) { + return false; + } + Size2i atlas_grid_size = get_atlas_grid_size(); + for (int frame = 0; frame < p_frames_count; frame++) { + Vector2i frame_coords = p_atlas_coords + (p_size + p_animation_separation) * ((p_animation_columns > 0) ? Vector2i(frame % p_animation_columns, frame / p_animation_columns) : Vector2i(frame, 0)); + for (int x = 0; x < p_size.x; x++) { + for (int y = 0; y < p_size.y; y++) { + Vector2i coords = frame_coords + Vector2i(x, y); + if (_coords_mapping_cache.has(coords) && _coords_mapping_cache[coords] != p_ignored_tile) { + return false; + } + if (coords.x >= atlas_grid_size.x || coords.y >= atlas_grid_size.y) { + if (!(_coords_mapping_cache.has(coords) && _coords_mapping_cache[coords] == p_ignored_tile)) { + return false; // Only accept tiles outside the atlas if they are part of the ignored tile. + } + } + } + } + } + return true; +} + +Rect2i TileSetAtlasSource::get_tile_texture_region(Vector2i p_atlas_coords, int p_frame) const { ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), Rect2i(), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); + ERR_FAIL_INDEX_V(p_frame, (int)tiles[p_atlas_coords].animation_frames_durations.size(), Rect2i()); - Vector2i size_in_atlas = tiles[p_atlas_coords].size_in_atlas; + const TileAlternativesData &tad = tiles[p_atlas_coords]; + + Vector2i size_in_atlas = tad.size_in_atlas; Vector2 region_size = texture_region_size * size_in_atlas + separation * (size_in_atlas - Vector2i(1, 1)); - Vector2 origin = margins + (p_atlas_coords * (texture_region_size + separation)); + Vector2i frame_coords = p_atlas_coords + (size_in_atlas + tad.animation_separation) * ((tad.animation_columns > 0) ? Vector2i(p_frame % tad.animation_columns, p_frame / tad.animation_columns) : Vector2i(p_frame, 0)); + Vector2 origin = margins + (frame_coords * (texture_region_size + separation)); return Rect2(origin, region_size); - ; } Vector2i TileSetAtlasSource::get_tile_effective_texture_offset(Vector2i p_atlas_coords, int p_alternative_tile) const { @@ -3074,63 +3586,29 @@ Vector2i TileSetAtlasSource::get_tile_effective_texture_offset(Vector2i p_atlas_ margin = Vector2i(MAX(0, margin.x), MAX(0, margin.y)); Vector2i effective_texture_offset = Object::cast_to<TileData>(get_tile_data(p_atlas_coords, p_alternative_tile))->get_texture_offset(); if (ABS(effective_texture_offset.x) > margin.x || ABS(effective_texture_offset.y) > margin.y) { - effective_texture_offset.x = CLAMP(effective_texture_offset.x, -margin.x, margin.x); - effective_texture_offset.y = CLAMP(effective_texture_offset.y, -margin.y, margin.y); + effective_texture_offset = effective_texture_offset.clamp(-margin, margin); } return effective_texture_offset; } -bool TileSetAtlasSource::can_move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords, Vector2i p_new_size) const { - ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), false, vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); - - Vector2i new_atlas_coords = (p_new_atlas_coords != INVALID_ATLAS_COORDS) ? p_new_atlas_coords : p_atlas_coords; - if (new_atlas_coords.x < 0 || new_atlas_coords.y < 0) { - return false; - } - - Vector2i size = (p_new_size != Vector2i(-1, -1)) ? p_new_size : tiles[p_atlas_coords].size_in_atlas; - ERR_FAIL_COND_V(size.x <= 0 || size.y <= 0, false); - - Size2i grid_size = get_atlas_grid_size(); - if (new_atlas_coords.x + size.x > grid_size.x || new_atlas_coords.y + size.y > grid_size.y) { - return false; - } - - Rect2i new_rect = Rect2i(new_atlas_coords, size); - // Check if the new tile can fit in the new rect. - for (int x = new_rect.position.x; x < new_rect.get_end().x; x++) { - for (int y = new_rect.position.y; y < new_rect.get_end().y; y++) { - Vector2i coords = get_tile_at_coords(Vector2i(x, y)); - if (coords != p_atlas_coords && coords != TileSetSource::INVALID_ATLAS_COORDS) { - return false; - } - } - } - - return true; -} - void TileSetAtlasSource::move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords, Vector2i p_new_size) { - bool can_move = can_move_tile_in_atlas(p_atlas_coords, p_new_atlas_coords, p_new_size); - ERR_FAIL_COND_MSG(!can_move, vformat("Cannot move tile at position %s with size %s. Tile already present.", p_new_atlas_coords, p_new_size)); + ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); + + TileAlternativesData &tad = tiles[p_atlas_coords]; // Compute the actual new rect from arguments. Vector2i new_atlas_coords = (p_new_atlas_coords != INVALID_ATLAS_COORDS) ? p_new_atlas_coords : p_atlas_coords; - Vector2i size = (p_new_size != Vector2i(-1, -1)) ? p_new_size : tiles[p_atlas_coords].size_in_atlas; + Vector2i new_size = (p_new_size != Vector2i(-1, -1)) ? p_new_size : tad.size_in_atlas; - if (new_atlas_coords == p_atlas_coords && size == tiles[p_atlas_coords].size_in_atlas) { + if (new_atlas_coords == p_atlas_coords && new_size == tad.size_in_atlas) { return; } - // Remove all covered positions from the mapping cache. - Size2i old_size = tiles[p_atlas_coords].size_in_atlas; - for (int x = 0; x < old_size.x; x++) { - for (int y = 0; y < old_size.y; y++) { - Vector2i coords = p_atlas_coords + Vector2i(x, y); - _coords_mapping_cache.erase(coords); - } - } + bool room_for_tile = has_room_for_tile(new_atlas_coords, new_size, tad.animation_columns, tad.animation_separation, tad.animation_frames_durations.size(), p_atlas_coords); + ERR_FAIL_COND_MSG(!room_for_tile, vformat("Cannot move tile at position %s with size %s. Tile already present.", new_atlas_coords, new_size)); + + _clear_coords_mapping_cache(p_atlas_coords); // Move the tile and update its size. if (new_atlas_coords != p_atlas_coords) { @@ -3141,15 +3619,9 @@ void TileSetAtlasSource::move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_ tiles_ids.append(new_atlas_coords); tiles_ids.sort(); } - tiles[new_atlas_coords].size_in_atlas = size; + tiles[new_atlas_coords].size_in_atlas = new_size; - // Add all covered positions to the mapping cache again. - for (int x = 0; x < size.x; x++) { - for (int y = 0; y < size.y; y++) { - Vector2i coords = new_atlas_coords + Vector2i(x, y); - _coords_mapping_cache[coords] = new_atlas_coords; - } - } + _create_coords_mapping_cache(new_atlas_coords); emit_signal(SNAME("changed")); } @@ -3158,8 +3630,8 @@ bool TileSetAtlasSource::has_tiles_outside_texture() { Vector2i grid_size = get_atlas_grid_size(); Vector<Vector2i> to_remove; - for (Map<Vector2i, TileSetAtlasSource::TileAlternativesData>::Element *E = tiles.front(); E; E = E->next()) { - if (E->key().x >= grid_size.x || E->key().y >= grid_size.y) { + for (const KeyValue<Vector2i, TileSetAtlasSource::TileAlternativesData> &E : tiles) { + if (E.key.x >= grid_size.x || E.key.y >= grid_size.y) { return true; } } @@ -3171,9 +3643,9 @@ void TileSetAtlasSource::clear_tiles_outside_texture() { Vector2i grid_size = get_atlas_grid_size(); Vector<Vector2i> to_remove; - for (Map<Vector2i, TileSetAtlasSource::TileAlternativesData>::Element *E = tiles.front(); E; E = E->next()) { - if (E->key().x >= grid_size.x || E->key().y >= grid_size.y) { - to_remove.append(E->key()); + for (const KeyValue<Vector2i, TileSetAtlasSource::TileAlternativesData> &E : tiles) { + if (E.key.x >= grid_size.x || E.key.y >= grid_size.y) { + to_remove.append(E.key); } } @@ -3273,45 +3745,50 @@ void TileSetAtlasSource::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_NOEDITOR), "set_texture", "get_texture"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "margins", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_margins", "get_margins"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "separation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_separation", "get_separation"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "tile_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_texture_region_size", "get_texture_region_size"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "texture_region_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_texture_region_size", "get_texture_region_size"); // Base tiles ClassDB::bind_method(D_METHOD("create_tile", "atlas_coords", "size"), &TileSetAtlasSource::create_tile, DEFVAL(Vector2i(1, 1))); ClassDB::bind_method(D_METHOD("remove_tile", "atlas_coords"), &TileSetAtlasSource::remove_tile); // Remove a tile. If p_tile_key.alternative_tile if different from 0, remove the alternative - ClassDB::bind_method(D_METHOD("has_tile", "atlas_coords"), &TileSetAtlasSource::has_tile); - ClassDB::bind_method(D_METHOD("can_move_tile_in_atlas", "atlas_coords", "new_atlas_coords", "new_size"), &TileSetAtlasSource::can_move_tile_in_atlas, DEFVAL(INVALID_ATLAS_COORDS), DEFVAL(Vector2i(-1, -1))); ClassDB::bind_method(D_METHOD("move_tile_in_atlas", "atlas_coords", "new_atlas_coords", "new_size"), &TileSetAtlasSource::move_tile_in_atlas, DEFVAL(INVALID_ATLAS_COORDS), DEFVAL(Vector2i(-1, -1))); ClassDB::bind_method(D_METHOD("get_tile_size_in_atlas", "atlas_coords"), &TileSetAtlasSource::get_tile_size_in_atlas); - ClassDB::bind_method(D_METHOD("get_tiles_count"), &TileSetAtlasSource::get_tiles_count); - ClassDB::bind_method(D_METHOD("get_tile_id", "index"), &TileSetAtlasSource::get_tile_id); + ClassDB::bind_method(D_METHOD("has_room_for_tile", "atlas_coords", "size", "animation_columns", "animation_separation", "frames_count", "ignored_tile"), &TileSetAtlasSource::has_room_for_tile, DEFVAL(INVALID_ATLAS_COORDS)); ClassDB::bind_method(D_METHOD("get_tile_at_coords", "atlas_coords"), &TileSetAtlasSource::get_tile_at_coords); + ClassDB::bind_method(D_METHOD("set_tile_animation_columns", "atlas_coords", "frame_columns"), &TileSetAtlasSource::set_tile_animation_columns); + ClassDB::bind_method(D_METHOD("get_tile_animation_columns", "atlas_coords"), &TileSetAtlasSource::get_tile_animation_columns); + ClassDB::bind_method(D_METHOD("set_tile_animation_separation", "atlas_coords", "separation"), &TileSetAtlasSource::set_tile_animation_separation); + ClassDB::bind_method(D_METHOD("get_tile_animation_separation", "atlas_coords"), &TileSetAtlasSource::get_tile_animation_separation); + ClassDB::bind_method(D_METHOD("set_tile_animation_speed", "atlas_coords", "speed"), &TileSetAtlasSource::set_tile_animation_speed); + ClassDB::bind_method(D_METHOD("get_tile_animation_speed", "atlas_coords"), &TileSetAtlasSource::get_tile_animation_speed); + ClassDB::bind_method(D_METHOD("set_tile_animation_frames_count", "atlas_coords", "frames_count"), &TileSetAtlasSource::set_tile_animation_frames_count); + ClassDB::bind_method(D_METHOD("get_tile_animation_frames_count", "atlas_coords"), &TileSetAtlasSource::get_tile_animation_frames_count); + ClassDB::bind_method(D_METHOD("set_tile_animation_frame_duration", "atlas_coords", "frame_index", "duration"), &TileSetAtlasSource::set_tile_animation_frame_duration); + ClassDB::bind_method(D_METHOD("get_tile_animation_frame_duration", "atlas_coords", "frame_index"), &TileSetAtlasSource::get_tile_animation_frame_duration); + ClassDB::bind_method(D_METHOD("get_tile_animation_total_duration", "atlas_coords"), &TileSetAtlasSource::get_tile_animation_total_duration); + // Alternative tiles ClassDB::bind_method(D_METHOD("create_alternative_tile", "atlas_coords", "alternative_id_override"), &TileSetAtlasSource::create_alternative_tile, DEFVAL(INVALID_TILE_ALTERNATIVE)); ClassDB::bind_method(D_METHOD("remove_alternative_tile", "atlas_coords", "alternative_tile"), &TileSetAtlasSource::remove_alternative_tile); ClassDB::bind_method(D_METHOD("set_alternative_tile_id", "atlas_coords", "alternative_tile", "new_id"), &TileSetAtlasSource::set_alternative_tile_id); - ClassDB::bind_method(D_METHOD("has_alternative_tile", "atlas_coords", "alternative_tile"), &TileSetAtlasSource::has_alternative_tile); ClassDB::bind_method(D_METHOD("get_next_alternative_tile_id", "atlas_coords"), &TileSetAtlasSource::get_next_alternative_tile_id); - ClassDB::bind_method(D_METHOD("get_alternative_tiles_count", "atlas_coords"), &TileSetAtlasSource::get_alternative_tiles_count); - ClassDB::bind_method(D_METHOD("get_alternative_tile_id", "atlas_coords", "index"), &TileSetAtlasSource::get_alternative_tile_id); - - ClassDB::bind_method(D_METHOD("get_tile_data", "atlas_coords", "index"), &TileSetAtlasSource::get_tile_data); + ClassDB::bind_method(D_METHOD("get_tile_data", "atlas_coords", "alternative_tile"), &TileSetAtlasSource::get_tile_data); // Helpers. ClassDB::bind_method(D_METHOD("get_atlas_grid_size"), &TileSetAtlasSource::get_atlas_grid_size); ClassDB::bind_method(D_METHOD("has_tiles_outside_texture"), &TileSetAtlasSource::has_tiles_outside_texture); ClassDB::bind_method(D_METHOD("clear_tiles_outside_texture"), &TileSetAtlasSource::clear_tiles_outside_texture); - ClassDB::bind_method(D_METHOD("get_tile_texture_region", "atlas_coords"), &TileSetAtlasSource::get_tile_texture_region); + ClassDB::bind_method(D_METHOD("get_tile_texture_region", "atlas_coords", "frame"), &TileSetAtlasSource::get_tile_texture_region, DEFVAL(0)); } TileSetAtlasSource::~TileSetAtlasSource() { // Free everything needed. - for (Map<Vector2i, TileAlternativesData>::Element *E_alternatives = tiles.front(); E_alternatives; E_alternatives = E_alternatives->next()) { - for (Map<int, TileData *>::Element *E_tile_data = E_alternatives->get().alternatives.front(); E_tile_data; E_tile_data = E_tile_data->next()) { - memdelete(E_tile_data->get()); + for (KeyValue<Vector2i, TileAlternativesData> &E_alternatives : tiles) { + for (KeyValue<int, TileData *> &E_tile_data : E_alternatives.value.alternatives) { + memdelete(E_tile_data.value); } } } @@ -3338,6 +3815,45 @@ void TileSetAtlasSource::_compute_next_alternative_id(const Vector2i p_atlas_coo }; } +void TileSetAtlasSource::_clear_coords_mapping_cache(Vector2i p_atlas_coords) { + ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + TileAlternativesData &tad = tiles[p_atlas_coords]; + for (int frame = 0; frame < (int)tad.animation_frames_durations.size(); frame++) { + Vector2i frame_coords = p_atlas_coords + (tad.size_in_atlas + tad.animation_separation) * ((tad.animation_columns > 0) ? Vector2i(frame % tad.animation_columns, frame / tad.animation_columns) : Vector2i(frame, 0)); + for (int x = 0; x < tad.size_in_atlas.x; x++) { + for (int y = 0; y < tad.size_in_atlas.y; y++) { + Vector2i coords = frame_coords + Vector2i(x, y); + if (!_coords_mapping_cache.has(coords)) { + WARN_PRINT(vformat("TileSetAtlasSource has no cached tile at position %s, the position cache might be corrupted.", coords)); + } else { + if (_coords_mapping_cache[coords] != p_atlas_coords) { + WARN_PRINT(vformat("The position cache at position %s is pointing to a wrong tile, the position cache might be corrupted.", coords)); + } + _coords_mapping_cache.erase(coords); + } + } + } + } +} + +void TileSetAtlasSource::_create_coords_mapping_cache(Vector2i p_atlas_coords) { + ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + + TileAlternativesData &tad = tiles[p_atlas_coords]; + for (int frame = 0; frame < (int)tad.animation_frames_durations.size(); frame++) { + Vector2i frame_coords = p_atlas_coords + (tad.size_in_atlas + tad.animation_separation) * ((tad.animation_columns > 0) ? Vector2i(frame % tad.animation_columns, frame / tad.animation_columns) : Vector2i(frame, 0)); + for (int x = 0; x < tad.size_in_atlas.x; x++) { + for (int y = 0; y < tad.size_in_atlas.y; y++) { + Vector2i coords = frame_coords + Vector2i(x, y); + if (_coords_mapping_cache.has(coords)) { + WARN_PRINT(vformat("The cache already has a tile for position %s, the position cache might be corrupted.", coords)); + } + _coords_mapping_cache[coords] = p_atlas_coords; + } + } + } +} + /////////////////////////////// TileSetScenesCollectionSource ////////////////////////////////////// void TileSetScenesCollectionSource::_compute_next_alternative_id() { @@ -3511,16 +4027,6 @@ void TileSetScenesCollectionSource::_get_property_list(List<PropertyInfo> *p_lis } void TileSetScenesCollectionSource::_bind_methods() { - // Base tiles - ClassDB::bind_method(D_METHOD("get_tiles_count"), &TileSetScenesCollectionSource::get_tiles_count); - ClassDB::bind_method(D_METHOD("get_tile_id", "index"), &TileSetScenesCollectionSource::get_tile_id); - ClassDB::bind_method(D_METHOD("has_tile", "atlas_coords"), &TileSetScenesCollectionSource::has_tile); - - // Alternative tiles - ClassDB::bind_method(D_METHOD("get_alternative_tiles_count", "atlas_coords"), &TileSetScenesCollectionSource::get_alternative_tiles_count); - ClassDB::bind_method(D_METHOD("get_alternative_tile_id", "atlas_coords", "index"), &TileSetScenesCollectionSource::get_alternative_tile_id); - ClassDB::bind_method(D_METHOD("has_alternative_tile", "atlas_coords", "alternative_tile"), &TileSetScenesCollectionSource::has_alternative_tile); - ClassDB::bind_method(D_METHOD("get_scene_tiles_count"), &TileSetScenesCollectionSource::get_scene_tiles_count); ClassDB::bind_method(D_METHOD("get_scene_tile_id", "index"), &TileSetScenesCollectionSource::get_scene_tile_id); ClassDB::bind_method(D_METHOD("has_scene_tile_id", "id"), &TileSetScenesCollectionSource::has_scene_tile_id); @@ -3575,6 +4081,155 @@ void TileData::notify_tile_data_properties_should_change() { emit_signal(SNAME("changed")); } +void TileData::add_occlusion_layer(int p_to_pos) { + if (p_to_pos < 0) { + p_to_pos = occluders.size(); + } + ERR_FAIL_INDEX(p_to_pos, occluders.size() + 1); + occluders.insert(p_to_pos, Ref<OccluderPolygon2D>()); +} + +void TileData::move_occlusion_layer(int p_from_index, int p_to_pos) { + ERR_FAIL_INDEX(p_from_index, occluders.size()); + ERR_FAIL_INDEX(p_to_pos, occluders.size() + 1); + occluders.insert(p_to_pos, occluders[p_from_index]); + occluders.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index); +} + +void TileData::remove_occlusion_layer(int p_index) { + ERR_FAIL_INDEX(p_index, occluders.size()); + occluders.remove(p_index); +} + +void TileData::add_physics_layer(int p_to_pos) { + if (p_to_pos < 0) { + p_to_pos = physics.size(); + } + ERR_FAIL_INDEX(p_to_pos, physics.size() + 1); + physics.insert(p_to_pos, PhysicsLayerTileData()); +} + +void TileData::move_physics_layer(int p_from_index, int p_to_pos) { + ERR_FAIL_INDEX(p_from_index, physics.size()); + ERR_FAIL_INDEX(p_to_pos, physics.size() + 1); + physics.insert(p_to_pos, physics[p_from_index]); + physics.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index); +} + +void TileData::remove_physics_layer(int p_index) { + ERR_FAIL_INDEX(p_index, physics.size()); + physics.remove(p_index); +} + +void TileData::add_terrain_set(int p_to_pos) { + if (p_to_pos >= 0 && p_to_pos <= terrain_set) { + terrain_set += 1; + } +} + +void TileData::move_terrain_set(int p_from_index, int p_to_pos) { + if (p_from_index == terrain_set) { + terrain_set = (p_from_index < p_to_pos) ? p_to_pos - 1 : p_to_pos; + } else { + if (p_from_index < terrain_set) { + terrain_set -= 1; + } + if (p_to_pos <= terrain_set) { + terrain_set += 1; + } + } +} + +void TileData::remove_terrain_set(int p_index) { + if (p_index == terrain_set) { + terrain_set = -1; + for (int i = 0; i < 16; i++) { + terrain_peering_bits[i] = -1; + } + } else if (terrain_set > p_index) { + terrain_set -= 1; + } +} + +void TileData::add_terrain(int p_terrain_set, int p_to_pos) { + if (terrain_set == p_terrain_set) { + for (int i = 0; i < 16; i++) { + if (p_to_pos >= 0 && p_to_pos <= terrain_peering_bits[i]) { + terrain_peering_bits[i] += 1; + } + } + } +} + +void TileData::move_terrain(int p_terrain_set, int p_from_index, int p_to_pos) { + if (terrain_set == p_terrain_set) { + for (int i = 0; i < 16; i++) { + if (p_from_index == terrain_peering_bits[i]) { + terrain_peering_bits[i] = (p_from_index < p_to_pos) ? p_to_pos - 1 : p_to_pos; + } else { + if (p_from_index < terrain_peering_bits[i]) { + terrain_peering_bits[i] -= 1; + } + if (p_to_pos <= terrain_peering_bits[i]) { + terrain_peering_bits[i] += 1; + } + } + } + } +} + +void TileData::remove_terrain(int p_terrain_set, int p_index) { + if (terrain_set == p_terrain_set) { + for (int i = 0; i < 16; i++) { + if (terrain_peering_bits[i] == p_index) { + terrain_peering_bits[i] = -1; + } else if (terrain_peering_bits[i] > p_index) { + terrain_peering_bits[i] -= 1; + } + } + } +} + +void TileData::add_navigation_layer(int p_to_pos) { + if (p_to_pos < 0) { + p_to_pos = navigation.size(); + } + ERR_FAIL_INDEX(p_to_pos, navigation.size() + 1); + navigation.insert(p_to_pos, Ref<NavigationPolygon>()); +} + +void TileData::move_navigation_layer(int p_from_index, int p_to_pos) { + ERR_FAIL_INDEX(p_from_index, navigation.size()); + ERR_FAIL_INDEX(p_to_pos, navigation.size() + 1); + navigation.insert(p_to_pos, navigation[p_from_index]); + navigation.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index); +} + +void TileData::remove_navigation_layer(int p_index) { + ERR_FAIL_INDEX(p_index, navigation.size()); + navigation.remove(p_index); +} + +void TileData::add_custom_data_layer(int p_to_pos) { + if (p_to_pos < 0) { + p_to_pos = custom_data.size(); + } + ERR_FAIL_INDEX(p_to_pos, custom_data.size() + 1); + custom_data.insert(p_to_pos, Variant()); +} + +void TileData::move_custom_data_layer(int p_from_index, int p_to_pos) { + ERR_FAIL_INDEX(p_from_index, custom_data.size()); + ERR_FAIL_INDEX(p_to_pos, custom_data.size() + 1); + custom_data.insert(p_to_pos, navigation[p_from_index]); + custom_data.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index); +} + +void TileData::remove_custom_data_layer(int p_index) { + ERR_FAIL_INDEX(p_index, custom_data.size()); + custom_data.remove(p_index); +} + void TileData::reset_state() { occluders.clear(); physics.clear(); @@ -3628,11 +4283,11 @@ Vector2i TileData::get_texture_offset() const { return tex_offset; } -void TileData::tile_set_material(Ref<ShaderMaterial> p_material) { +void TileData::set_material(Ref<ShaderMaterial> p_material) { material = p_material; emit_signal(SNAME("changed")); } -Ref<ShaderMaterial> TileData::tile_get_material() const { +Ref<ShaderMaterial> TileData::get_material() const { return material; } @@ -3672,9 +4327,26 @@ Ref<OccluderPolygon2D> TileData::get_occluder(int p_layer_id) const { } // Physics -int TileData::get_collision_polygons_count(int p_layer_id) const { - ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0); - return physics[p_layer_id].polygons.size(); +void TileData::set_constant_linear_velocity(int p_layer_id, const Vector2 &p_velocity) { + ERR_FAIL_INDEX(p_layer_id, physics.size()); + physics.write[p_layer_id].linear_velocity = p_velocity; + emit_signal(SNAME("changed")); +} + +Vector2 TileData::get_constant_linear_velocity(int p_layer_id) const { + ERR_FAIL_INDEX_V(p_layer_id, physics.size(), Vector2()); + return physics[p_layer_id].linear_velocity; +} + +void TileData::set_constant_angular_velocity(int p_layer_id, real_t p_velocity) { + ERR_FAIL_INDEX(p_layer_id, physics.size()); + physics.write[p_layer_id].angular_velocity = p_velocity; + emit_signal(SNAME("changed")); +} + +real_t TileData::get_constant_angular_velocity(int p_layer_id) const { + ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0.0); + return physics[p_layer_id].angular_velocity; } void TileData::set_collision_polygons_count(int p_layer_id, int p_polygons_count) { @@ -3685,6 +4357,11 @@ void TileData::set_collision_polygons_count(int p_layer_id, int p_polygons_count emit_signal(SNAME("changed")); } +int TileData::get_collision_polygons_count(int p_layer_id) const { + ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0); + return physics[p_layer_id].polygons.size(); +} + void TileData::add_collision_polygon(int p_layer_id) { ERR_FAIL_INDEX(p_layer_id, physics.size()); physics.write[p_layer_id].polygons.push_back(PhysicsLayerTileData::PolygonShapeTileData()); @@ -3886,11 +4563,7 @@ bool TileData::_set(const StringName &p_name, const Variant &p_value) { // Physics layers. int layer_index = components[0].trim_prefix("physics_layer_").to_int(); ERR_FAIL_COND_V(layer_index < 0, false); - if (components.size() == 2 && components[1] == "polygons_count") { - if (p_value.get_type() != Variant::INT) { - return false; - } - + if (components.size() == 2) { if (layer_index >= physics.size()) { if (tile_set) { return false; @@ -3898,8 +4571,19 @@ bool TileData::_set(const StringName &p_name, const Variant &p_value) { physics.resize(layer_index + 1); } } - set_collision_polygons_count(layer_index, p_value); - return true; + if (components[1] == "linear_velocity") { + set_constant_linear_velocity(layer_index, p_value); + return true; + } else if (components[1] == "angular_velocity") { + set_constant_angular_velocity(layer_index, p_value); + return true; + } else if (components[1] == "polygons_count") { + if (p_value.get_type() != Variant::INT) { + return false; + } + set_collision_polygons_count(layer_index, p_value); + return true; + } } else if (components.size() == 3 && components[1].begins_with("polygon_") && components[1].trim_prefix("polygon_").is_valid_int()) { int polygon_index = components[1].trim_prefix("polygon_").to_int(); ERR_FAIL_COND_V(polygon_index < 0, false); @@ -4001,9 +4685,18 @@ bool TileData::_get(const StringName &p_name, Variant &r_ret) const { if (layer_index >= physics.size()) { return false; } - if (components.size() == 2 && components[1] == "polygons_count") { - r_ret = get_collision_polygons_count(layer_index); - return true; + + if (components.size() == 2) { + if (components[1] == "linear_velocity") { + r_ret = get_constant_linear_velocity(layer_index); + return true; + } else if (components[1] == "angular_velocity") { + r_ret = get_constant_angular_velocity(layer_index); + return true; + } else if (components[1] == "polygons_count") { + r_ret = get_collision_polygons_count(layer_index); + return true; + } } else if (components.size() == 3 && components[1].begins_with("polygon_") && components[1].trim_prefix("polygon_").is_valid_int()) { int polygon_index = components[1].trim_prefix("polygon_").to_int(); ERR_FAIL_COND_V(polygon_index < 0, false); @@ -4074,6 +4767,8 @@ void TileData::_get_property_list(List<PropertyInfo> *p_list) const { // Physics layers. p_list->push_back(PropertyInfo(Variant::NIL, "Physics", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); for (int i = 0; i < physics.size(); i++) { + p_list->push_back(PropertyInfo(Variant::VECTOR2, vformat("physics_layer_%d/linear_velocity", i), PROPERTY_HINT_NONE)); + p_list->push_back(PropertyInfo(Variant::FLOAT, vformat("physics_layer_%d/angular_velocity", i), PROPERTY_HINT_NONE)); p_list->push_back(PropertyInfo(Variant::INT, vformat("physics_layer_%d/polygons_count", i), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); for (int j = 0; j < physics[i].polygons.size(); j++) { @@ -4148,8 +4843,8 @@ void TileData::_bind_methods() { ClassDB::bind_method(D_METHOD("get_flip_v"), &TileData::get_flip_v); ClassDB::bind_method(D_METHOD("set_transpose", "transpose"), &TileData::set_transpose); ClassDB::bind_method(D_METHOD("get_transpose"), &TileData::get_transpose); - ClassDB::bind_method(D_METHOD("tile_set_material", "material"), &TileData::tile_set_material); - ClassDB::bind_method(D_METHOD("tile_get_material"), &TileData::tile_get_material); + ClassDB::bind_method(D_METHOD("set_material", "material"), &TileData::set_material); + ClassDB::bind_method(D_METHOD("get_material"), &TileData::get_material); ClassDB::bind_method(D_METHOD("set_texture_offset", "texture_offset"), &TileData::set_texture_offset); ClassDB::bind_method(D_METHOD("get_texture_offset"), &TileData::get_texture_offset); ClassDB::bind_method(D_METHOD("set_modulate", "modulate"), &TileData::set_modulate); @@ -4163,8 +4858,12 @@ void TileData::_bind_methods() { ClassDB::bind_method(D_METHOD("get_occluder", "layer_id"), &TileData::get_occluder); // Physics. - ClassDB::bind_method(D_METHOD("get_collision_polygons_count", "layer_id"), &TileData::get_collision_polygons_count); + ClassDB::bind_method(D_METHOD("set_constant_linear_velocity", "layer_id", "velocity"), &TileData::set_constant_linear_velocity); + ClassDB::bind_method(D_METHOD("get_constant_linear_velocity", "layer_id"), &TileData::get_constant_linear_velocity); + ClassDB::bind_method(D_METHOD("set_constant_angular_velocity", "layer_id", "velocity"), &TileData::set_constant_angular_velocity); + ClassDB::bind_method(D_METHOD("get_constant_angular_velocity", "layer_id"), &TileData::get_constant_angular_velocity); ClassDB::bind_method(D_METHOD("set_collision_polygons_count", "layer_id", "polygons_count"), &TileData::set_collision_polygons_count); + ClassDB::bind_method(D_METHOD("get_collision_polygons_count", "layer_id"), &TileData::get_collision_polygons_count); ClassDB::bind_method(D_METHOD("add_collision_polygon", "layer_id"), &TileData::add_collision_polygon); ClassDB::bind_method(D_METHOD("remove_collision_polygon", "layer_id", "polygon_index"), &TileData::remove_collision_polygon); ClassDB::bind_method(D_METHOD("set_collision_polygon_points", "layer_id", "polygon_index", "polygon"), &TileData::set_collision_polygon_points); @@ -4200,6 +4899,7 @@ void TileData::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "transpose"), "set_transpose", "get_transpose"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "texture_offset"), "set_texture_offset", "get_texture_offset"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "modulate"), "set_modulate", "get_modulate"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial"), "set_material", "get_material"); ADD_PROPERTY(PropertyInfo(Variant::INT, "z_index"), "set_z_index", "get_z_index"); ADD_PROPERTY(PropertyInfo(Variant::INT, "y_sort_origin"), "set_y_sort_origin", "get_y_sort_origin"); diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h index 35e6999d13..716b66405f 100644 --- a/scene/resources/tile_set.h +++ b/scene/resources/tile_set.h @@ -180,6 +180,7 @@ protected: bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List<PropertyInfo> *p_list) const; + virtual void _validate_property(PropertyInfo &property) const override; private: // --- TileSet data --- @@ -225,10 +226,10 @@ private: bool terrain_bits_meshes_dirty = true; // Navigation - struct Navigationlayer { + struct NavigationLayer { uint32_t layers = 1; }; - Vector<Navigationlayer> navigation_layers; + Vector<NavigationLayer> navigation_layers; // CustomData struct CustomDataLayer { @@ -298,16 +299,20 @@ public: void set_uv_clipping(bool p_uv_clipping); bool is_uv_clipping() const; - void set_occlusion_layers_count(int p_occlusion_layers_count); int get_occlusion_layers_count() const; + void add_occlusion_layer(int p_index = -1); + void move_occlusion_layer(int p_from_index, int p_to_pos); + void remove_occlusion_layer(int p_index); void set_occlusion_layer_light_mask(int p_layer_index, int p_light_mask); int get_occlusion_layer_light_mask(int p_layer_index) const; - void set_occlusion_layer_sdf_collision(int p_layer_index, int p_sdf_collision); + void set_occlusion_layer_sdf_collision(int p_layer_index, bool p_sdf_collision); bool get_occlusion_layer_sdf_collision(int p_layer_index) const; // Physics - void set_physics_layers_count(int p_physics_layers_count); int get_physics_layers_count() const; + void add_physics_layer(int p_index = -1); + void move_physics_layer(int p_from_index, int p_to_pos); + void remove_physics_layer(int p_index); void set_physics_layer_collision_layer(int p_layer_index, uint32_t p_layer); uint32_t get_physics_layer_collision_layer(int p_layer_index) const; void set_physics_layer_collision_mask(int p_layer_index, uint32_t p_mask); @@ -315,13 +320,19 @@ public: void set_physics_layer_physics_material(int p_layer_index, Ref<PhysicsMaterial> p_physics_material); Ref<PhysicsMaterial> get_physics_layer_physics_material(int p_layer_index) const; - // Terrains - void set_terrain_sets_count(int p_terrains_sets_count); + // Terrain sets int get_terrain_sets_count() const; + void add_terrain_set(int p_index = -1); + void move_terrain_set(int p_from_index, int p_to_pos); + void remove_terrain_set(int p_index); void set_terrain_set_mode(int p_terrain_set, TerrainMode p_terrain_mode); TerrainMode get_terrain_set_mode(int p_terrain_set) const; - void set_terrains_count(int p_terrain_set, int p_terrains_count); + + // Terrains int get_terrains_count(int p_terrain_set) const; + void add_terrain(int p_terrain_set, int p_index = -1); + void move_terrain(int p_terrain_set, int p_from_index, int p_to_pos); + void remove_terrain(int p_terrain_set, int p_index); void set_terrain_name(int p_terrain_set, int p_terrain_index, String p_name); String get_terrain_name(int p_terrain_set, int p_terrain_index) const; void set_terrain_color(int p_terrain_set, int p_terrain_index, Color p_color); @@ -330,14 +341,18 @@ public: bool is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeighbor p_peering_bit) const; // Navigation - void set_navigation_layers_count(int p_navigation_layers_count); int get_navigation_layers_count() const; + void add_navigation_layer(int p_index = -1); + void move_navigation_layer(int p_from_index, int p_to_pos); + void remove_navigation_layer(int p_index); void set_navigation_layer_layers(int p_layer_index, uint32_t p_layers); uint32_t get_navigation_layer_layers(int p_layer_index) const; // Custom data - void set_custom_data_layers_count(int p_custom_data_layers_count); int get_custom_data_layers_count() const; + void add_custom_data_layer(int p_index = -1); + void move_custom_data_layer(int p_from_index, int p_to_pos); + void remove_custom_data_layer(int p_index); int get_custom_data_layer_by_name(String p_value) const; void set_custom_data_name(int p_layer_id, String p_value); String get_custom_data_name(int p_layer_id) const; @@ -371,7 +386,7 @@ public: // Helpers Vector<Vector2> get_tile_shape_polygon(); - void draw_tile_shape(CanvasItem *p_canvas_item, Rect2 p_region, Color p_color, bool p_filled = false, Ref<Texture2D> p_texture = Ref<Texture2D>()); + void draw_tile_shape(CanvasItem *p_canvas_item, Transform2D p_transform, Color p_color, bool p_filled = false, Ref<Texture2D> p_texture = Ref<Texture2D>()); Vector<Point2> get_terrain_bit_polygon(int p_terrain_set, TileSet::CellNeighbor p_bit); void draw_terrains(CanvasItem *p_canvas_item, Transform2D p_transform, const TileData *p_tile_data); @@ -390,6 +405,8 @@ class TileSetSource : public Resource { protected: const TileSet *tile_set = nullptr; + static void _bind_methods(); + public: static const Vector2i INVALID_ATLAS_COORDS; // Vector2i(-1, -1); static const int INVALID_TILE_ALTERNATIVE; // -1; @@ -397,6 +414,24 @@ public: // Not exposed. virtual void set_tile_set(const TileSet *p_tile_set); virtual void notify_tile_data_properties_should_change(){}; + virtual void add_occlusion_layer(int p_index){}; + virtual void move_occlusion_layer(int p_from_index, int p_to_pos){}; + virtual void remove_occlusion_layer(int p_index){}; + virtual void add_physics_layer(int p_index){}; + virtual void move_physics_layer(int p_from_index, int p_to_pos){}; + virtual void remove_physics_layer(int p_index){}; + virtual void add_terrain_set(int p_index){}; + virtual void move_terrain_set(int p_from_index, int p_to_pos){}; + virtual void remove_terrain_set(int p_index){}; + virtual void add_terrain(int p_terrain_set, int p_index){}; + virtual void move_terrain(int p_terrain_set, int p_from_index, int p_to_pos){}; + virtual void remove_terrain(int p_terrain_set, int p_index){}; + virtual void add_navigation_layer(int p_index){}; + virtual void move_navigation_layer(int p_from_index, int p_to_pos){}; + virtual void remove_navigation_layer(int p_index){}; + virtual void add_custom_data_layer(int p_index){}; + virtual void move_custom_data_layer(int p_from_index, int p_to_pos){}; + virtual void remove_custom_data_layer(int p_index){}; virtual void reset_state() override{}; // Tiles. @@ -413,16 +448,23 @@ public: class TileSetAtlasSource : public TileSetSource { GDCLASS(TileSetAtlasSource, TileSetSource); -public: +private: struct TileAlternativesData { Vector2i size_in_atlas = Vector2i(1, 1); Vector2i texture_offset; + + // Animation + int animation_columns = 0; + Vector2i animation_separation; + real_t animation_speed = 1.0; + LocalVector<real_t> animation_frames_durations; + + // Alternatives Map<int, TileData *> alternatives; Vector<int> alternatives_ids; int next_alternative_id = 1; }; -private: Ref<Texture2D> texture; Vector2i margins; Vector2i separation; @@ -437,6 +479,9 @@ private: void _compute_next_alternative_id(const Vector2i p_atlas_coords); + void _create_coords_mapping_cache(Vector2i p_atlas_coords); + void _clear_coords_mapping_cache(Vector2i p_atlas_coords); + protected: bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; @@ -448,6 +493,24 @@ public: // Not exposed. virtual void set_tile_set(const TileSet *p_tile_set) override; virtual void notify_tile_data_properties_should_change() override; + virtual void add_occlusion_layer(int p_index) override; + virtual void move_occlusion_layer(int p_from_index, int p_to_pos) override; + virtual void remove_occlusion_layer(int p_index) override; + virtual void add_physics_layer(int p_index) override; + virtual void move_physics_layer(int p_from_index, int p_to_pos) override; + virtual void remove_physics_layer(int p_index) override; + virtual void add_terrain_set(int p_index) override; + virtual void move_terrain_set(int p_from_index, int p_to_pos) override; + virtual void remove_terrain_set(int p_index) override; + virtual void add_terrain(int p_terrain_set, int p_index) override; + virtual void move_terrain(int p_terrain_set, int p_from_index, int p_to_pos) override; + virtual void remove_terrain(int p_terrain_set, int p_index) override; + virtual void add_navigation_layer(int p_index) override; + virtual void move_navigation_layer(int p_from_index, int p_to_pos) override; + virtual void remove_navigation_layer(int p_index) override; + virtual void add_custom_data_layer(int p_index) override; + virtual void move_custom_data_layer(int p_from_index, int p_to_pos) override; + virtual void remove_custom_data_layer(int p_index) override; virtual void reset_state() override; // Base properties. @@ -461,18 +524,32 @@ public: Vector2i get_texture_region_size() const; // Base tiles. - void create_tile(const Vector2i p_atlas_coords, const Vector2i p_size = Vector2i(1, 1)); // Create a tile if it does not exists, or add alternative tile if it does. - void remove_tile(Vector2i p_atlas_coords); // Remove a tile. If p_tile_key.alternative_tile if different from 0, remove the alternative + void create_tile(const Vector2i p_atlas_coords, const Vector2i p_size = Vector2i(1, 1)); + void remove_tile(Vector2i p_atlas_coords); virtual bool has_tile(Vector2i p_atlas_coords) const override; - bool can_move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords = INVALID_ATLAS_COORDS, Vector2i p_new_size = Vector2i(-1, -1)) const; void move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords = INVALID_ATLAS_COORDS, Vector2i p_new_size = Vector2i(-1, -1)); Vector2i get_tile_size_in_atlas(Vector2i p_atlas_coords) const; virtual int get_tiles_count() const override; virtual Vector2i get_tile_id(int p_index) const override; + bool has_room_for_tile(Vector2i p_atlas_coords, Vector2i p_size, int p_animation_columns, Vector2i p_animation_separation, int p_frames_count, Vector2i p_ignored_tile = INVALID_ATLAS_COORDS) const; + Vector2i get_tile_at_coords(Vector2i p_atlas_coords) const; + // Animation. + void set_tile_animation_columns(const Vector2i p_atlas_coords, int p_frame_columns); + int get_tile_animation_columns(const Vector2i p_atlas_coords) const; + void set_tile_animation_separation(const Vector2i p_atlas_coords, const Vector2i p_separation); + Vector2i get_tile_animation_separation(const Vector2i p_atlas_coords) const; + void set_tile_animation_speed(const Vector2i p_atlas_coords, real_t p_speed); + real_t get_tile_animation_speed(const Vector2i p_atlas_coords) const; + void set_tile_animation_frames_count(const Vector2i p_atlas_coords, int p_frames_count); + int get_tile_animation_frames_count(const Vector2i p_atlas_coords) const; + void set_tile_animation_frame_duration(const Vector2i p_atlas_coords, int p_frame_index, real_t p_duration); + real_t get_tile_animation_frame_duration(const Vector2i p_atlas_coords, int p_frame_index) const; + real_t get_tile_animation_total_duration(const Vector2i p_atlas_coords) const; + // Alternative tiles. int create_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_id_override = -1); void remove_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile); @@ -490,7 +567,7 @@ public: Vector2i get_atlas_grid_size() const; bool has_tiles_outside_texture(); void clear_tiles_outside_texture(); - Rect2i get_tile_texture_region(Vector2i p_atlas_coords) const; + Rect2i get_tile_texture_region(Vector2i p_atlas_coords, int p_frame = 0) const; Vector2i get_tile_effective_texture_offset(Vector2i p_atlas_coords, int p_alternative_tile) const; ~TileSetAtlasSource(); @@ -528,7 +605,7 @@ public: int get_alternative_tile_id(const Vector2i p_atlas_coords, int p_index) const override; bool has_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile) const override; - // Scenes sccessors. Lot are similar to "Alternative tiles". + // Scenes accessors. Lot are similar to "Alternative tiles". int get_scene_tiles_count() { return get_alternative_tiles_count(Vector2i()); } int get_scene_tile_id(int p_index) { return get_alternative_tile_id(Vector2i(), p_index); }; bool has_scene_tile_id(int p_id) { return has_alternative_tile(Vector2i(), p_id); }; @@ -569,6 +646,8 @@ private: float one_way_margin = 1.0; }; + Vector2 linear_velocity; + float angular_velocity = 0.0; Vector<PolygonShapeTileData> polygons; }; Vector<PhysicsLayerTileData> physics; @@ -597,6 +676,24 @@ public: // Not exposed. void set_tile_set(const TileSet *p_tile_set); void notify_tile_data_properties_should_change(); + void add_occlusion_layer(int p_index); + void move_occlusion_layer(int p_from_index, int p_to_pos); + void remove_occlusion_layer(int p_index); + void add_physics_layer(int p_index); + void move_physics_layer(int p_from_index, int p_to_pos); + void remove_physics_layer(int p_index); + void add_terrain_set(int p_index); + void move_terrain_set(int p_from_index, int p_to_pos); + void remove_terrain_set(int p_index); + void add_terrain(int p_terrain_set, int p_index); + void move_terrain(int p_terrain_set, int p_from_index, int p_to_pos); + void remove_terrain(int p_terrain_set, int p_index); + void add_navigation_layer(int p_index); + void move_navigation_layer(int p_from_index, int p_to_pos); + void remove_navigation_layer(int p_index); + void add_custom_data_layer(int p_index); + void move_custom_data_layer(int p_from_index, int p_to_pos); + void remove_custom_data_layer(int p_index); void reset_state(); void set_allow_transform(bool p_allow_transform); bool is_allowing_transform() const; @@ -611,8 +708,8 @@ public: void set_texture_offset(Vector2i p_texture_offset); Vector2i get_texture_offset() const; - void tile_set_material(Ref<ShaderMaterial> p_material); - Ref<ShaderMaterial> tile_get_material() const; + void set_material(Ref<ShaderMaterial> p_material); + Ref<ShaderMaterial> get_material() const; void set_modulate(Color p_modulate); Color get_modulate() const; void set_z_index(int p_z_index); @@ -624,8 +721,12 @@ public: Ref<OccluderPolygon2D> get_occluder(int p_layer_id) const; // Physics - int get_collision_polygons_count(int p_layer_id) const; + void set_constant_linear_velocity(int p_layer_id, const Vector2 &p_velocity); + Vector2 get_constant_linear_velocity(int p_layer_id) const; + void set_constant_angular_velocity(int p_layer_id, real_t p_velocity); + real_t get_constant_angular_velocity(int p_layer_id) const; void set_collision_polygons_count(int p_layer_id, int p_shapes_count); + int get_collision_polygons_count(int p_layer_id) const; void add_collision_polygon(int p_layer_id); void remove_collision_polygon(int p_layer_id, int p_polygon_index); void set_collision_polygon_points(int p_layer_id, int p_polygon_index, Vector<Vector2> p_polygon); diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index b00dcca004..934d16bd7e 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -197,9 +197,9 @@ Vector<StringName> VisualShaderNode::get_editable_properties() const { Array VisualShaderNode::get_default_input_values() const { Array ret; - for (Map<int, Variant>::Element *E = default_input_values.front(); E; E = E->next()) { - ret.push_back(E->key()); - ret.push_back(E->get()); + for (const KeyValue<int, Variant> &E : default_input_values) { + ret.push_back(E.key); + ret.push_back(E.value); } return ret; } @@ -261,54 +261,47 @@ VisualShaderNode::VisualShaderNode() { ///////////////////////////////////////////////////////// void VisualShaderNodeCustom::update_ports() { - ERR_FAIL_COND(!get_script_instance()); + { + input_ports.clear(); + int input_port_count; + if (GDVIRTUAL_CALL(_get_input_port_count, input_port_count)) { + for (int i = 0; i < input_port_count; i++) { + Port port; + if (!GDVIRTUAL_CALL(_get_input_port_name, i, port.name)) { + port.name = "in" + itos(i); + } + if (!GDVIRTUAL_CALL(_get_input_port_type, i, port.type)) { + port.type = (int)PortType::PORT_TYPE_SCALAR; + } - input_ports.clear(); - if (get_script_instance()->has_method("_get_input_port_count")) { - int input_port_count = (int)get_script_instance()->call("_get_input_port_count"); - bool has_name = get_script_instance()->has_method("_get_input_port_name"); - bool has_type = get_script_instance()->has_method("_get_input_port_type"); - for (int i = 0; i < input_port_count; i++) { - Port port; - if (has_name) { - port.name = (String)get_script_instance()->call("_get_input_port_name", i); - } else { - port.name = "in" + itos(i); + input_ports.push_back(port); } - if (has_type) { - port.type = (int)get_script_instance()->call("_get_input_port_type", i); - } else { - port.type = (int)PortType::PORT_TYPE_SCALAR; - } - input_ports.push_back(port); } } - output_ports.clear(); - if (get_script_instance()->has_method("_get_output_port_count")) { - int output_port_count = (int)get_script_instance()->call("_get_output_port_count"); - bool has_name = get_script_instance()->has_method("_get_output_port_name"); - bool has_type = get_script_instance()->has_method("_get_output_port_type"); - for (int i = 0; i < output_port_count; i++) { - Port port; - if (has_name) { - port.name = (String)get_script_instance()->call("_get_output_port_name", i); - } else { - port.name = "out" + itos(i); - } - if (has_type) { - port.type = (int)get_script_instance()->call("_get_output_port_type", i); - } else { - port.type = (int)PortType::PORT_TYPE_SCALAR; + + { + output_ports.clear(); + int output_port_count; + if (GDVIRTUAL_CALL(_get_output_port_count, output_port_count)) { + for (int i = 0; i < output_port_count; i++) { + Port port; + if (!GDVIRTUAL_CALL(_get_output_port_name, i, port.name)) { + port.name = "out" + itos(i); + } + if (!GDVIRTUAL_CALL(_get_output_port_type, i, port.type)) { + port.type = (int)PortType::PORT_TYPE_SCALAR; + } + + output_ports.push_back(port); } - output_ports.push_back(port); } } } String VisualShaderNodeCustom::get_caption() const { - ERR_FAIL_COND_V(!get_script_instance(), ""); - if (get_script_instance()->has_method("_get_name")) { - return (String)get_script_instance()->call("_get_name"); + String ret; + if (GDVIRTUAL_CALL(_get_name, ret)) { + return ret; } return "Unnamed"; } @@ -342,9 +335,8 @@ String VisualShaderNodeCustom::get_output_port_name(int p_port) const { } String VisualShaderNodeCustom::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { - ERR_FAIL_COND_V(!get_script_instance(), ""); - ERR_FAIL_COND_V(!get_script_instance()->has_method("_get_code"), ""); - Array input_vars; + ERR_FAIL_COND_V(!GDVIRTUAL_IS_OVERRIDDEN(_get_code), ""); + Vector<String> input_vars; for (int i = 0; i < get_input_port_count(); i++) { input_vars.push_back(p_input_vars[i]); } @@ -353,7 +345,8 @@ String VisualShaderNodeCustom::generate_code(Shader::Mode p_mode, VisualShader:: output_vars.push_back(p_output_vars[i]); } String code = " {\n"; - String _code = (String)get_script_instance()->call("_get_code", input_vars, output_vars, (int)p_mode, (int)p_type); + String _code; + GDVIRTUAL_CALL(_get_code, input_vars, output_vars, (int)p_mode, (int)p_type, _code); bool nend = _code.ends_with("\n"); _code = _code.insert(0, " "); _code = _code.replace("\n", "\n "); @@ -369,10 +362,10 @@ String VisualShaderNodeCustom::generate_code(Shader::Mode p_mode, VisualShader:: } String VisualShaderNodeCustom::generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { - ERR_FAIL_COND_V(!get_script_instance(), ""); - if (get_script_instance()->has_method("_get_global_code")) { + String ret; + if (GDVIRTUAL_CALL(_get_global_code, (int)p_mode, ret)) { String code = "// " + get_caption() + "\n"; - code += (String)get_script_instance()->call("_get_global_code", (int)p_mode); + code += ret; code += "\n"; return code; } @@ -416,19 +409,19 @@ void VisualShaderNodeCustom::_set_initialized(bool p_enabled) { } void VisualShaderNodeCustom::_bind_methods() { - BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_name")); - BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_description")); - BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_category")); - BIND_VMETHOD(MethodInfo(Variant::INT, "_get_return_icon_type")); - BIND_VMETHOD(MethodInfo(Variant::INT, "_get_input_port_count")); - BIND_VMETHOD(MethodInfo(Variant::INT, "_get_input_port_type", PropertyInfo(Variant::INT, "port"))); - BIND_VMETHOD(MethodInfo(Variant::STRING_NAME, "_get_input_port_name", PropertyInfo(Variant::INT, "port"))); - BIND_VMETHOD(MethodInfo(Variant::INT, "_get_output_port_count")); - BIND_VMETHOD(MethodInfo(Variant::INT, "_get_output_port_type", PropertyInfo(Variant::INT, "port"))); - BIND_VMETHOD(MethodInfo(Variant::STRING_NAME, "_get_output_port_name", PropertyInfo(Variant::INT, "port"))); - BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_code", PropertyInfo(Variant::ARRAY, "input_vars"), PropertyInfo(Variant::ARRAY, "output_vars"), PropertyInfo(Variant::INT, "mode"), PropertyInfo(Variant::INT, "type"))); - BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_global_code", PropertyInfo(Variant::INT, "mode"))); - BIND_VMETHOD(MethodInfo(Variant::BOOL, "_is_highend")); + GDVIRTUAL_BIND(_get_name); + GDVIRTUAL_BIND(_get_description); + GDVIRTUAL_BIND(_get_category); + GDVIRTUAL_BIND(_get_return_icon_type); + GDVIRTUAL_BIND(_get_input_port_count); + GDVIRTUAL_BIND(_get_input_port_type, "port"); + GDVIRTUAL_BIND(_get_input_port_name, "port"); + GDVIRTUAL_BIND(_get_output_port_count); + GDVIRTUAL_BIND(_get_output_port_type, "port"); + GDVIRTUAL_BIND(_get_output_port_name, "port"); + GDVIRTUAL_BIND(_get_code, "input_vars", "output_vars", "mode", "type"); + GDVIRTUAL_BIND(_get_global_code, "mode"); + GDVIRTUAL_BIND(_is_highend); ClassDB::bind_method(D_METHOD("_set_initialized", "enabled"), &VisualShaderNodeCustom::_set_initialized); ClassDB::bind_method(D_METHOD("_is_initialized"), &VisualShaderNodeCustom::_is_initialized); @@ -467,14 +460,14 @@ Dictionary VisualShader::get_engine_version() const { void VisualShader::update_engine_version(const Dictionary &p_new_version) { if (engine_version.is_empty()) { // before 4.0 for (int i = 0; i < TYPE_MAX; i++) { - for (Map<int, Node>::Element *E = graph[i].nodes.front(); E; E = E->next()) { - Ref<VisualShaderNodeInput> input = Object::cast_to<VisualShaderNodeInput>(E->get().node.ptr()); + for (KeyValue<int, Node> &E : graph[i].nodes) { + Ref<VisualShaderNodeInput> input = Object::cast_to<VisualShaderNodeInput>(E.value.node.ptr()); if (input.is_valid()) { if (input->get_input_name() == "side") { input->set_input_name("front_facing"); } } - Ref<VisualShaderNodeExpression> expression = Object::cast_to<VisualShaderNodeExpression>(E->get().node.ptr()); + Ref<VisualShaderNodeExpression> expression = Object::cast_to<VisualShaderNodeExpression>(E.value.node.ptr()); if (expression.is_valid()) { for (int j = 0; j < expression->get_input_port_count(); j++) { int type = expression->get_input_port_type(j); @@ -491,7 +484,7 @@ void VisualShader::update_engine_version(const Dictionary &p_new_version) { expression->set_output_port_type(j, type); } } - Ref<VisualShaderNodeCompare> compare = Object::cast_to<VisualShaderNodeCompare>(E->get().node.ptr()); + Ref<VisualShaderNodeCompare> compare = Object::cast_to<VisualShaderNodeCompare>(E.value.node.ptr()); if (compare.is_valid()) { int ctype = int(compare->get_comparison_type()); if (int(ctype) > 0) { // + PORT_TYPE_SCALAR_INT @@ -568,8 +561,8 @@ Vector<int> VisualShader::get_node_list(Type p_type) const { const Graph *g = &graph[p_type]; Vector<int> ret; - for (Map<int, Node>::Element *E = g->nodes.front(); E; E = E->next()) { - ret.push_back(E->key()); + for (const KeyValue<int, Node> &E : g->nodes) { + ret.push_back(E.key); } return ret; @@ -582,9 +575,9 @@ int VisualShader::get_valid_node_id(Type p_type) const { } int VisualShader::find_node_id(Type p_type, const Ref<VisualShaderNode> &p_node) const { - for (const Map<int, Node>::Element *E = graph[p_type].nodes.front(); E; E = E->next()) { - if (E->get().node == p_node) { - return E->key(); + for (const KeyValue<int, Node> &E : graph[p_type].nodes) { + if (E.value.node == p_node) { + return E.key; } } @@ -826,8 +819,8 @@ void VisualShader::set_mode(Mode p_mode) { flags.clear(); shader_mode = p_mode; for (int i = 0; i < TYPE_MAX; i++) { - for (Map<int, Node>::Element *E = graph[i].nodes.front(); E; E = E->next()) { - Ref<VisualShaderNodeInput> input = E->get().node; + for (KeyValue<int, Node> &E : graph[i].nodes) { + Ref<VisualShaderNodeInput> input = E.value.node; if (input.is_valid()) { input->shader_mode = shader_mode; //input->input_index = 0; @@ -1048,8 +1041,8 @@ String VisualShader::validate_uniform_name(const String &p_name, const Ref<Visua while (true) { bool exists = false; for (int i = 0; i < TYPE_MAX; i++) { - for (const Map<int, Node>::Element *E = graph[i].nodes.front(); E; E = E->next()) { - Ref<VisualShaderNodeUniform> node = E->get().node; + for (const KeyValue<int, Node> &E : graph[i].nodes) { + Ref<VisualShaderNodeUniform> node = E.value.node; if (node == p_uniform) { //do not test on self continue; } @@ -1278,8 +1271,8 @@ void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const { } } - for (Map<String, String>::Element *E = blend_mode_enums.front(); E; E = E->next()) { - p_list->push_back(PropertyInfo(Variant::INT, "modes/" + E->key(), PROPERTY_HINT_ENUM, E->get())); + for (const KeyValue<String, String> &E : blend_mode_enums) { + p_list->push_back(PropertyInfo(Variant::INT, "modes/" + E.key, PROPERTY_HINT_ENUM, E.value)); } for (Set<String>::Element *E = toggles.front(); E; E = E->next()) { @@ -1287,22 +1280,22 @@ void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const { } for (int i = 0; i < TYPE_MAX; i++) { - for (Map<int, Node>::Element *E = graph[i].nodes.front(); E; E = E->next()) { + for (const KeyValue<int, Node> &E : graph[i].nodes) { String prop_name = "nodes/"; prop_name += type_string[i]; - prop_name += "/" + itos(E->key()); + prop_name += "/" + itos(E.key); - if (E->key() != NODE_ID_OUTPUT) { + if (E.key != NODE_ID_OUTPUT) { p_list->push_back(PropertyInfo(Variant::OBJECT, prop_name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "VisualShaderNode", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); } p_list->push_back(PropertyInfo(Variant::VECTOR2, prop_name + "/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); - if (Object::cast_to<VisualShaderNodeGroupBase>(E->get().node.ptr()) != nullptr) { + if (Object::cast_to<VisualShaderNodeGroupBase>(E.value.node.ptr()) != nullptr) { p_list->push_back(PropertyInfo(Variant::VECTOR2, prop_name + "/size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); p_list->push_back(PropertyInfo(Variant::STRING, prop_name + "/input_ports", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); p_list->push_back(PropertyInfo(Variant::STRING, prop_name + "/output_ports", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); } - if (Object::cast_to<VisualShaderNodeExpression>(E->get().node.ptr()) != nullptr) { + if (Object::cast_to<VisualShaderNodeExpression>(E.value.node.ptr()) != nullptr) { p_list->push_back(PropertyInfo(Variant::STRING, prop_name + "/expression", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); } } @@ -1723,9 +1716,9 @@ void VisualShader::_update_shader() const { emitters.insert(i, List<int>()); } - for (Map<int, Node>::Element *M = graph[i].nodes.front(); M; M = M->next()) { - if (M->get().node == emit_particle.ptr()) { - emitters[i].push_back(M->key()); + for (const KeyValue<int, Node> &M : graph[i].nodes) { + if (M.value.node == emit_particle.ptr()) { + emitters[i].push_back(M.key); break; } } diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h index 31651318ae..b3efac02aa 100644 --- a/scene/resources/visual_shader.h +++ b/scene/resources/visual_shader.h @@ -314,6 +314,20 @@ protected: virtual void remove_input_port_default_value(int p_port) override; virtual void clear_default_input_values() override; + GDVIRTUAL0RC(String, _get_name) + GDVIRTUAL0RC(String, _get_description) + GDVIRTUAL0RC(String, _get_category) + GDVIRTUAL0RC(int, _get_return_icon_type) + GDVIRTUAL0RC(int, _get_input_port_count) + GDVIRTUAL1RC(int, _get_input_port_type, int) + GDVIRTUAL1RC(String, _get_input_port_name, int) + GDVIRTUAL0RC(int, _get_output_port_count) + GDVIRTUAL1RC(int, _get_output_port_type, int) + GDVIRTUAL1RC(String, _get_output_port_name, int) + GDVIRTUAL4RC(String, _get_code, Vector<String>, TypedArray<String>, int, int) + GDVIRTUAL1RC(String, _get_global_code, int) + GDVIRTUAL0RC(bool, _is_highend) + protected: void _set_input_port_default_value(int p_port, const Variant &p_value); diff --git a/scene/resources/world_margin_shape_2d.cpp b/scene/resources/world_boundary_shape_2d.cpp index 3b43681528..39af92793f 100644 --- a/scene/resources/world_margin_shape_2d.cpp +++ b/scene/resources/world_boundary_shape_2d.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* world_margin_shape_2d.cpp */ +/* world_boundary_shape_2d.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,13 +28,13 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "world_margin_shape_2d.h" +#include "world_boundary_shape_2d.h" #include "core/math/geometry_2d.h" #include "servers/physics_server_2d.h" #include "servers/rendering_server.h" -bool WorldMarginShape2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { +bool WorldBoundaryShape2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { Vector2 point = get_distance() * get_normal(); Vector2 l[2][2] = { { point - get_normal().orthogonal() * 100, point + get_normal().orthogonal() * 100 }, { point, point + get_normal() * 30 } }; @@ -48,7 +48,7 @@ bool WorldMarginShape2D::_edit_is_selected_on_click(const Point2 &p_point, doubl return false; } -void WorldMarginShape2D::_update_shape() { +void WorldBoundaryShape2D::_update_shape() { Array arr; arr.push_back(normal); arr.push_back(distance); @@ -56,25 +56,25 @@ void WorldMarginShape2D::_update_shape() { emit_changed(); } -void WorldMarginShape2D::set_normal(const Vector2 &p_normal) { +void WorldBoundaryShape2D::set_normal(const Vector2 &p_normal) { normal = p_normal; _update_shape(); } -void WorldMarginShape2D::set_distance(real_t p_distance) { +void WorldBoundaryShape2D::set_distance(real_t p_distance) { distance = p_distance; _update_shape(); } -Vector2 WorldMarginShape2D::get_normal() const { +Vector2 WorldBoundaryShape2D::get_normal() const { return normal; } -real_t WorldMarginShape2D::get_distance() const { +real_t WorldBoundaryShape2D::get_distance() const { return distance; } -void WorldMarginShape2D::draw(const RID &p_to_rid, const Color &p_color) { +void WorldBoundaryShape2D::draw(const RID &p_to_rid, const Color &p_color) { Vector2 point = get_distance() * get_normal(); Vector2 l1[2] = { point - get_normal().orthogonal() * 100, point + get_normal().orthogonal() * 100 }; @@ -83,7 +83,7 @@ void WorldMarginShape2D::draw(const RID &p_to_rid, const Color &p_color) { RS::get_singleton()->canvas_item_add_line(p_to_rid, l2[0], l2[1], p_color, 3); } -Rect2 WorldMarginShape2D::get_rect() const { +Rect2 WorldBoundaryShape2D::get_rect() const { Vector2 point = get_distance() * get_normal(); Vector2 l1[2] = { point - get_normal().orthogonal() * 100, point + get_normal().orthogonal() * 100 }; @@ -96,22 +96,22 @@ Rect2 WorldMarginShape2D::get_rect() const { return rect; } -real_t WorldMarginShape2D::get_enclosing_radius() const { +real_t WorldBoundaryShape2D::get_enclosing_radius() const { return distance; } -void WorldMarginShape2D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_normal", "normal"), &WorldMarginShape2D::set_normal); - ClassDB::bind_method(D_METHOD("get_normal"), &WorldMarginShape2D::get_normal); +void WorldBoundaryShape2D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_normal", "normal"), &WorldBoundaryShape2D::set_normal); + ClassDB::bind_method(D_METHOD("get_normal"), &WorldBoundaryShape2D::get_normal); - ClassDB::bind_method(D_METHOD("set_distance", "distance"), &WorldMarginShape2D::set_distance); - ClassDB::bind_method(D_METHOD("get_distance"), &WorldMarginShape2D::get_distance); + ClassDB::bind_method(D_METHOD("set_distance", "distance"), &WorldBoundaryShape2D::set_distance); + ClassDB::bind_method(D_METHOD("get_distance"), &WorldBoundaryShape2D::get_distance); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "normal"), "set_normal", "get_normal"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance"), "set_distance", "get_distance"); } -WorldMarginShape2D::WorldMarginShape2D() : - Shape2D(PhysicsServer2D::get_singleton()->world_margin_shape_create()) { +WorldBoundaryShape2D::WorldBoundaryShape2D() : + Shape2D(PhysicsServer2D::get_singleton()->world_boundary_shape_create()) { _update_shape(); } diff --git a/scene/resources/world_margin_shape_2d.h b/scene/resources/world_boundary_shape_2d.h index 3c1d593ffe..4cc60f5985 100644 --- a/scene/resources/world_margin_shape_2d.h +++ b/scene/resources/world_boundary_shape_2d.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* world_margin_shape_2d.h */ +/* world_boundary_shape_2d.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,15 +28,15 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef WORLD_MARGIN_SHAPE_2D_H -#define WORLD_MARGIN_SHAPE_2D_H +#ifndef WORLD_BOUNDARY_SHAPE_2D_H +#define WORLD_BOUNDARY_SHAPE_2D_H #include "scene/resources/shape_2d.h" -class WorldMarginShape2D : public Shape2D { - GDCLASS(WorldMarginShape2D, Shape2D); +class WorldBoundaryShape2D : public Shape2D { + GDCLASS(WorldBoundaryShape2D, Shape2D); - // WorldMarginShape2D is often used for one-way platforms, where the normal pointing up makes sense. + // WorldBoundaryShape2D is often used for one-way platforms, where the normal pointing up makes sense. Vector2 normal = Vector2(0, -1); real_t distance = 0.0; @@ -58,7 +58,7 @@ public: virtual Rect2 get_rect() const override; virtual real_t get_enclosing_radius() const override; - WorldMarginShape2D(); + WorldBoundaryShape2D(); }; -#endif // WORLD_MARGIN_SHAPE_2D_H +#endif // WORLD_BOUNDARY_SHAPE_2D_H diff --git a/scene/resources/world_margin_shape_3d.cpp b/scene/resources/world_boundary_shape_3d.cpp index 28d50e1921..8cde537164 100644 --- a/scene/resources/world_margin_shape_3d.cpp +++ b/scene/resources/world_boundary_shape_3d.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* world_margin_shape_3d.cpp */ +/* world_boundary_shape_3d.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,11 +28,11 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "world_margin_shape_3d.h" +#include "world_boundary_shape_3d.h" #include "servers/physics_server_3d.h" -Vector<Vector3> WorldMarginShape3D::get_debug_mesh_lines() const { +Vector<Vector3> WorldBoundaryShape3D::get_debug_mesh_lines() const { Plane p = get_plane(); Vector<Vector3> points; @@ -60,29 +60,29 @@ Vector<Vector3> WorldMarginShape3D::get_debug_mesh_lines() const { return points; } -void WorldMarginShape3D::_update_shape() { +void WorldBoundaryShape3D::_update_shape() { PhysicsServer3D::get_singleton()->shape_set_data(get_shape(), plane); Shape3D::_update_shape(); } -void WorldMarginShape3D::set_plane(Plane p_plane) { +void WorldBoundaryShape3D::set_plane(const Plane &p_plane) { plane = p_plane; _update_shape(); notify_change_to_owners(); } -Plane WorldMarginShape3D::get_plane() const { +const Plane &WorldBoundaryShape3D::get_plane() const { return plane; } -void WorldMarginShape3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_plane", "plane"), &WorldMarginShape3D::set_plane); - ClassDB::bind_method(D_METHOD("get_plane"), &WorldMarginShape3D::get_plane); +void WorldBoundaryShape3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_plane", "plane"), &WorldBoundaryShape3D::set_plane); + ClassDB::bind_method(D_METHOD("get_plane"), &WorldBoundaryShape3D::get_plane); ADD_PROPERTY(PropertyInfo(Variant::PLANE, "plane"), "set_plane", "get_plane"); } -WorldMarginShape3D::WorldMarginShape3D() : - Shape3D(PhysicsServer3D::get_singleton()->shape_create(PhysicsServer3D::SHAPE_PLANE)) { +WorldBoundaryShape3D::WorldBoundaryShape3D() : + Shape3D(PhysicsServer3D::get_singleton()->shape_create(PhysicsServer3D::SHAPE_WORLD_BOUNDARY)) { set_plane(Plane(0, 1, 0, 0)); } diff --git a/scene/resources/world_margin_shape_3d.h b/scene/resources/world_boundary_shape_3d.h index 00417c4408..853f555ebc 100644 --- a/scene/resources/world_margin_shape_3d.h +++ b/scene/resources/world_boundary_shape_3d.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* world_margin_shape_3d.h */ +/* world_boundary_shape_3d.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,13 +28,13 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef WORLD_MARGIN_SHAPE_3D_H -#define WORLD_MARGIN_SHAPE_3D_H +#ifndef WORLD_BOUNDARY_SHAPE_3D_H +#define WORLD_BOUNDARY_SHAPE_3D_H #include "scene/resources/shape_3d.h" -class WorldMarginShape3D : public Shape3D { - GDCLASS(WorldMarginShape3D, Shape3D); +class WorldBoundaryShape3D : public Shape3D { + GDCLASS(WorldBoundaryShape3D, Shape3D); Plane plane; protected: @@ -42,8 +42,8 @@ protected: virtual void _update_shape() override; public: - void set_plane(Plane p_plane); - Plane get_plane() const; + void set_plane(const Plane &p_plane); + const Plane &get_plane() const; virtual Vector<Vector3> get_debug_mesh_lines() const override; virtual real_t get_enclosing_radius() const override { @@ -51,6 +51,6 @@ public: return 0; } - WorldMarginShape3D(); + WorldBoundaryShape3D(); }; -#endif // WORLD_MARGIN_SHAPE_H +#endif // WORLD_BOUNDARY_SHAPE_H |