summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/classes/TextServer.xml5
-rw-r--r--editor/debugger/script_editor_debugger.cpp2
-rw-r--r--editor/editor_settings.cpp2
-rw-r--r--modules/basis_universal/image_compress_basisu.cpp6
-rw-r--r--modules/multiplayer/multiplayer_synchronizer.cpp2
-rw-r--r--modules/navigation/2d/nav_mesh_generator_2d.cpp12
-rw-r--r--modules/text_server_adv/text_server_adv.cpp154
-rw-r--r--modules/text_server_fb/text_server_fb.cpp132
-rw-r--r--platform/linuxbsd/tts_linux.cpp8
-rw-r--r--scene/property_utils.cpp18
-rw-r--r--scene/resources/2d/navigation_polygon.cpp45
-rw-r--r--scene/resources/2d/navigation_polygon.h4
-rw-r--r--scene/resources/material.cpp18
-rw-r--r--tests/scene/test_node.h59
14 files changed, 331 insertions, 136 deletions
diff --git a/doc/classes/TextServer.xml b/doc/classes/TextServer.xml
index 34137a18ef..4fa9700f9c 100644
--- a/doc/classes/TextServer.xml
+++ b/doc/classes/TextServer.xml
@@ -1739,8 +1739,9 @@
When [param chars_per_line] is greater than zero, line break boundaries are returned instead.
[codeblock]
var ts = TextServerManager.get_primary_interface()
- print(ts.string_get_word_breaks("Godot Engine")) # Prints [0, 5, 6, 12]
- print(ts.string_get_word_breaks("Godot Engine", "en", 5)) # Prints [0, 5, 6, 11, 11, 12]
+ print(ts.string_get_word_breaks("The Godot Engine, 4")) # Prints [0, 3, 4, 9, 10, 16, 18, 19], which corresponds to the following substrings: "The", "Godot", "Engine", "4"
+ print(ts.string_get_word_breaks("The Godot Engine, 4", "en", 5)) # Prints [0, 3, 4, 9, 10, 15, 15, 19], which corresponds to the following substrings: "The", "Godot", "Engin", "e, 4"
+ print(ts.string_get_word_breaks("The Godot Engine, 4", "en", 10)) # Prints [0, 9, 10, 19], which corresponds to the following substrings: "The Godot", "Engine, 4"
[/codeblock]
</description>
</method>
diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp
index b062b20000..5e96daf69c 100644
--- a/editor/debugger/script_editor_debugger.cpp
+++ b/editor/debugger/script_editor_debugger.cpp
@@ -840,7 +840,7 @@ void ScriptEditorDebugger::_set_reason_text(const String &p_reason, MessageType
for (int i = 0; i < boundaries.size(); i += 2) {
const int start = boundaries[i];
const int end = boundaries[i + 1];
- lines.append(p_reason.substr(start, end - start + 1));
+ lines.append(p_reason.substr(start, end - start));
}
reason->set_tooltip_text(String("\n").join(lines));
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index 024aec22b6..702d15bf7b 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -662,7 +662,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
_initial_set("text_editor/script_list/sort_members_outline_alphabetically", false);
// Completion
- EDITOR_SETTING(Variant::FLOAT, PROPERTY_HINT_RANGE, "text_editor/completion/idle_parse_delay", 2.0, "0.1,10,0.01")
+ EDITOR_SETTING(Variant::FLOAT, PROPERTY_HINT_RANGE, "text_editor/completion/idle_parse_delay", 1.5, "0.1,10,0.01")
_initial_set("text_editor/completion/auto_brace_complete", true);
_initial_set("text_editor/completion/code_complete_enabled", true);
EDITOR_SETTING(Variant::FLOAT, PROPERTY_HINT_RANGE, "text_editor/completion/code_complete_delay", 0.3, "0.01,5,0.01,or_greater")
diff --git a/modules/basis_universal/image_compress_basisu.cpp b/modules/basis_universal/image_compress_basisu.cpp
index d8ef1c0414..216fa6c9a2 100644
--- a/modules/basis_universal/image_compress_basisu.cpp
+++ b/modules/basis_universal/image_compress_basisu.cpp
@@ -65,6 +65,12 @@ Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedCha
params.m_multithreading = true;
params.m_check_for_alpha = false;
+ if (!OS::get_singleton()->is_stdout_verbose()) {
+ params.m_print_stats = false;
+ params.m_compute_stats = false;
+ params.m_status_output = false;
+ }
+
basisu::job_pool job_pool(OS::get_singleton()->get_processor_count());
params.m_pJob_pool = &job_pool;
diff --git a/modules/multiplayer/multiplayer_synchronizer.cpp b/modules/multiplayer/multiplayer_synchronizer.cpp
index ca3fa43207..852975b8eb 100644
--- a/modules/multiplayer/multiplayer_synchronizer.cpp
+++ b/modules/multiplayer/multiplayer_synchronizer.cpp
@@ -270,7 +270,7 @@ void MultiplayerSynchronizer::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_path"), "set_root_path", "get_root_path");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "replication_interval", PROPERTY_HINT_RANGE, "0,5,0.001,suffix:s"), "set_replication_interval", "get_replication_interval");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "delta_interval", PROPERTY_HINT_RANGE, "0,5,0.001,suffix:s"), "set_delta_interval", "get_delta_interval");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "replication_config", PROPERTY_HINT_RESOURCE_TYPE, "SceneReplicationConfig", PROPERTY_USAGE_NO_EDITOR), "set_replication_config", "get_replication_config");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "replication_config", PROPERTY_HINT_RESOURCE_TYPE, "SceneReplicationConfig", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT), "set_replication_config", "get_replication_config");
ADD_PROPERTY(PropertyInfo(Variant::INT, "visibility_update_mode", PROPERTY_HINT_ENUM, "Idle,Physics,None"), "set_visibility_update_mode", "get_visibility_update_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "public_visibility"), "set_visibility_public", "is_visibility_public");
diff --git a/modules/navigation/2d/nav_mesh_generator_2d.cpp b/modules/navigation/2d/nav_mesh_generator_2d.cpp
index aa4c797723..8c2fb42463 100644
--- a/modules/navigation/2d/nav_mesh_generator_2d.cpp
+++ b/modules/navigation/2d/nav_mesh_generator_2d.cpp
@@ -1010,8 +1010,7 @@ void NavMeshGenerator2D::generator_bake_from_source_geometry_data(Ref<Navigation
}
if (new_baked_outlines.size() == 0) {
- p_navigation_mesh->set_vertices(Vector<Vector2>());
- p_navigation_mesh->clear_polygons();
+ p_navigation_mesh->clear();
return;
}
@@ -1045,8 +1044,7 @@ void NavMeshGenerator2D::generator_bake_from_source_geometry_data(Ref<Navigation
TPPLPartition tpart;
if (tpart.ConvexPartition_HM(&tppl_in_polygon, &tppl_out_polygon) == 0) { //failed!
ERR_PRINT("NavigationPolygon Convex partition failed. Unable to create a valid NavigationMesh from defined polygon outline paths.");
- p_navigation_mesh->set_vertices(Vector<Vector2>());
- p_navigation_mesh->clear_polygons();
+ p_navigation_mesh->clear();
return;
}
@@ -1071,11 +1069,7 @@ void NavMeshGenerator2D::generator_bake_from_source_geometry_data(Ref<Navigation
new_polygons.push_back(new_polygon);
}
- p_navigation_mesh->set_vertices(new_vertices);
- p_navigation_mesh->clear_polygons();
- for (int i = 0; i < new_polygons.size(); i++) {
- p_navigation_mesh->add_polygon(new_polygons[i]);
- }
+ p_navigation_mesh->set_data(new_vertices, new_polygons);
}
#endif // CLIPPER2_ENABLED
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index 33ba2da761..0c87199635 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -7048,10 +7048,10 @@ PackedInt32Array TextServerAdvanced::_string_get_word_breaks(const String &p_str
HashSet<int> breaks;
UErrorCode err = U_ZERO_ERROR;
- UBreakIterator *bi = ubrk_open(UBRK_LINE, lang.ascii().get_data(), (const UChar *)utf16.get_data(), utf16.length(), &err);
+ UBreakIterator *bi = ubrk_open(UBRK_WORD, lang.ascii().get_data(), (const UChar *)utf16.get_data(), utf16.length(), &err);
if (U_SUCCESS(err)) {
while (ubrk_next(bi) != UBRK_DONE) {
- int pos = _convert_pos(p_string, utf16, ubrk_current(bi)) - 1;
+ int pos = _convert_pos(p_string, utf16, ubrk_current(bi));
if (pos != p_string.length() - 1) {
breaks.insert(pos);
}
@@ -7061,79 +7061,111 @@ PackedInt32Array TextServerAdvanced::_string_get_word_breaks(const String &p_str
PackedInt32Array ret;
- int line_start = 0;
- int line_end = 0; // End of last word on current line.
- int word_start = 0; // -1 if no word encountered. Leading spaces are part of a word.
- int word_length = 0;
+ if (p_chars_per_line > 0) {
+ int line_start = 0;
+ int last_break = -1;
+ int line_length = 0;
- for (int i = 0; i < p_string.length(); i++) {
- const char32_t c = p_string[i];
+ for (int i = 0; i < p_string.length(); i++) {
+ const char32_t c = p_string[i];
- if (is_linebreak(c)) {
- // Force newline.
- ret.push_back(line_start);
- ret.push_back(i);
- line_start = i + 1;
- line_end = line_start;
- word_start = line_start;
- word_length = 0;
- } else if (c == 0xfffc) {
- continue;
- } else if ((u_ispunct(c) && c != 0x005F) || is_underscore(c) || c == '\t' || is_whitespace(c)) {
- // A whitespace ends current word.
- if (word_length > 0) {
- line_end = i - 1;
- word_start = -1;
- word_length = 0;
- }
- } else if (breaks.has(i)) {
- // End current word, no space.
- if (word_length > 0) {
- line_end = i;
- word_start = i + 1;
- word_length = 0;
- }
- if (p_chars_per_line <= 0) {
- ret.push_back(line_start);
- ret.push_back(line_end + 1);
- line_start = word_start;
- line_end = line_start;
- }
- } else {
- if (word_start == -1) {
- word_start = i;
- if (p_chars_per_line <= 0) {
+ bool is_lb = is_linebreak(c);
+ bool is_ws = is_whitespace(c);
+ bool is_p = (u_ispunct(c) && c != 0x005F) || is_underscore(c) || c == '\t' || c == 0xfffc;
+
+ if (is_lb) {
+ if (line_length > 0) {
ret.push_back(line_start);
- ret.push_back(line_end + 1);
- line_start = word_start;
- line_end = line_start;
+ ret.push_back(i);
}
+ line_start = i;
+ line_length = 0;
+ last_break = -1;
+ continue;
+ } else if (breaks.has(i) || is_ws || is_p) {
+ last_break = i;
}
- word_length += 1;
- if (p_chars_per_line > 0) {
- if (word_length > p_chars_per_line) {
- // Word too long: wrap before current character.
+ if (line_length == p_chars_per_line) {
+ if (last_break != -1) {
+ int last_break_w_spaces = last_break;
+ while (last_break > line_start && is_whitespace(p_string[last_break - 1])) {
+ last_break--;
+ }
+ if (line_start != last_break) {
+ ret.push_back(line_start);
+ ret.push_back(last_break);
+ }
+ while (last_break_w_spaces < p_string.length() && is_whitespace(p_string[last_break_w_spaces])) {
+ last_break_w_spaces++;
+ }
+ line_start = last_break_w_spaces;
+ if (last_break_w_spaces < i) {
+ line_length = i - last_break_w_spaces;
+ } else {
+ i = last_break_w_spaces;
+ line_length = 0;
+ }
+ } else {
ret.push_back(line_start);
ret.push_back(i);
line_start = i;
- line_end = i;
+ line_length = 0;
+ }
+ last_break = -1;
+ }
+ line_length++;
+ }
+ if (line_length > 0) {
+ ret.push_back(line_start);
+ ret.push_back(p_string.length());
+ }
+ } else {
+ int word_start = 0; // -1 if no word encountered. Leading spaces are part of a word.
+ int word_length = 0;
+
+ for (int i = 0; i < p_string.length(); i++) {
+ const char32_t c = p_string[i];
+
+ bool is_lb = is_linebreak(c);
+ bool is_ws = is_whitespace(c);
+ bool is_p = (u_ispunct(c) && c != 0x005F) || is_underscore(c) || c == '\t' || c == 0xfffc;
+
+ if (word_start == -1) {
+ if (!is_lb && !is_ws && !is_p) {
word_start = i;
- word_length = 1;
- } else if (i - line_start + 1 > p_chars_per_line) {
- // Line too long: wrap after the last word.
- ret.push_back(line_start);
- ret.push_back(line_end + 1);
- line_start = word_start;
- line_end = line_start;
}
+ continue;
+ }
+
+ if (is_lb) {
+ if (word_start != -1 && word_length > 0) {
+ ret.push_back(word_start);
+ ret.push_back(i);
+ }
+ word_start = -1;
+ word_length = 0;
+ } else if (breaks.has(i) || is_ws || is_p) {
+ if (word_start != -1 && word_length > 0) {
+ ret.push_back(word_start);
+ ret.push_back(i);
+ }
+ if (is_ws || is_p) {
+ word_start = -1;
+ } else {
+ word_start = i;
+ }
+ word_length = 0;
}
+
+ word_length++;
+ }
+ if (word_start != -1 && word_length > 0) {
+ ret.push_back(word_start);
+ ret.push_back(p_string.length());
}
}
- if (line_start < p_string.length()) {
- ret.push_back(line_start);
- ret.push_back(p_string.length());
- }
+
return ret;
}
diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp
index 697c3366c5..6cf6b236ed 100644
--- a/modules/text_server_fb/text_server_fb.cpp
+++ b/modules/text_server_fb/text_server_fb.cpp
@@ -4492,65 +4492,105 @@ String TextServerFallback::_string_to_title(const String &p_string, const String
PackedInt32Array TextServerFallback::_string_get_word_breaks(const String &p_string, const String &p_language, int64_t p_chars_per_line) const {
PackedInt32Array ret;
- int line_start = 0;
- int line_end = 0; // End of last word on current line.
- int word_start = 0; // -1 if no word encountered. Leading spaces are part of a word.
- int word_length = 0;
+ if (p_chars_per_line > 0) {
+ int line_start = 0;
+ int last_break = -1;
+ int line_length = 0;
- for (int i = 0; i < p_string.length(); i++) {
- const char32_t c = p_string[i];
+ for (int i = 0; i < p_string.length(); i++) {
+ const char32_t c = p_string[i];
- if (is_linebreak(c)) {
- // Force newline.
- ret.push_back(line_start);
- ret.push_back(i);
- line_start = i + 1;
- line_end = line_start;
- word_start = line_start;
- word_length = 0;
- } else if (c == 0xfffc) {
- continue;
- } else if ((is_punct(c) && c != 0x005F) || is_underscore(c) || c == '\t' || is_whitespace(c)) {
- // A whitespace ends current word.
- if (word_length > 0) {
- line_end = i - 1;
- word_start = -1;
- word_length = 0;
- }
- } else {
- if (word_start == -1) {
- word_start = i;
- if (p_chars_per_line <= 0) {
+ bool is_lb = is_linebreak(c);
+ bool is_ws = is_whitespace(c);
+ bool is_p = (is_punct(c) && c != 0x005F) || is_underscore(c) || c == '\t' || c == 0xfffc;
+
+ if (is_lb) {
+ if (line_length > 0) {
ret.push_back(line_start);
- ret.push_back(line_end + 1);
- line_start = word_start;
- line_end = line_start;
+ ret.push_back(i);
}
+ line_start = i;
+ line_length = 0;
+ last_break = -1;
+ continue;
+ } else if (is_ws || is_p) {
+ last_break = i;
}
- word_length += 1;
- if (p_chars_per_line > 0) {
- if (word_length > p_chars_per_line) {
- // Word too long: wrap before current character.
+ if (line_length == p_chars_per_line) {
+ if (last_break != -1) {
+ int last_break_w_spaces = last_break;
+ while (last_break > line_start && is_whitespace(p_string[last_break - 1])) {
+ last_break--;
+ }
+ if (line_start != last_break) {
+ ret.push_back(line_start);
+ ret.push_back(last_break);
+ }
+ while (last_break_w_spaces < p_string.length() && is_whitespace(p_string[last_break_w_spaces])) {
+ last_break_w_spaces++;
+ }
+ line_start = last_break_w_spaces;
+ if (last_break_w_spaces < i) {
+ line_length = i - last_break_w_spaces;
+ } else {
+ i = last_break_w_spaces;
+ line_length = 0;
+ }
+ } else {
ret.push_back(line_start);
ret.push_back(i);
line_start = i;
- line_end = i;
+ line_length = 0;
+ }
+ last_break = -1;
+ }
+ line_length++;
+ }
+ if (line_length > 0) {
+ ret.push_back(line_start);
+ ret.push_back(p_string.length());
+ }
+ } else {
+ int word_start = 0; // -1 if no word encountered. Leading spaces are part of a word.
+ int word_length = 0;
+
+ for (int i = 0; i < p_string.length(); i++) {
+ const char32_t c = p_string[i];
+
+ bool is_lb = is_linebreak(c);
+ bool is_ws = is_whitespace(c);
+ bool is_p = (is_punct(c) && c != 0x005F) || is_underscore(c) || c == '\t' || c == 0xfffc;
+
+ if (word_start == -1) {
+ if (!is_lb && !is_ws && !is_p) {
word_start = i;
- word_length = 1;
- } else if (i - line_start + 1 > p_chars_per_line) {
- // Line too long: wrap after the last word.
- ret.push_back(line_start);
- ret.push_back(line_end + 1);
- line_start = word_start;
- line_end = line_start;
}
+ continue;
}
+
+ if (is_lb) {
+ if (word_start != -1 && word_length > 0) {
+ ret.push_back(word_start);
+ ret.push_back(i);
+ }
+ word_start = -1;
+ word_length = 0;
+ } else if (is_ws || is_p) {
+ if (word_start != -1 && word_length > 0) {
+ ret.push_back(word_start);
+ ret.push_back(i);
+ }
+ word_start = -1;
+ word_length = 0;
+ }
+
+ word_length++;
+ }
+ if (word_start != -1 && word_length > 0) {
+ ret.push_back(word_start);
+ ret.push_back(p_string.length());
}
- }
- if (line_start < p_string.length()) {
- ret.push_back(line_start);
- ret.push_back(p_string.length());
}
return ret;
}
diff --git a/platform/linuxbsd/tts_linux.cpp b/platform/linuxbsd/tts_linux.cpp
index 46291bb4da..6c1f49f046 100644
--- a/platform/linuxbsd/tts_linux.cpp
+++ b/platform/linuxbsd/tts_linux.cpp
@@ -149,12 +149,18 @@ void TTS_Linux::_speech_event(int p_msg_id, int p_type) {
}
PackedInt32Array breaks = TS->string_get_word_breaks(message.text, language);
+ int prev_end = -1;
for (int i = 0; i < breaks.size(); i += 2) {
const int start = breaks[i];
const int end = breaks[i + 1];
- text += message.text.substr(start, end - start + 1);
+ if (prev_end != -1 && prev_end != start) {
+ text += message.text.substr(prev_end, start - prev_end);
+ }
+ text += message.text.substr(start, end - start);
text += "<mark name=\"" + String::num_int64(end, 10) + "\"/>";
+ prev_end = end;
}
+
spd_set_synthesis_voice(synth, message.voice.utf8().get_data());
spd_set_volume(synth, message.volume * 2 - 100);
spd_set_voice_pitch(synth, (message.pitch - 1) * 100);
diff --git a/scene/property_utils.cpp b/scene/property_utils.cpp
index db17f9d643..94a037bd9b 100644
--- a/scene/property_utils.cpp
+++ b/scene/property_utils.cpp
@@ -44,6 +44,7 @@ bool PropertyUtils::is_property_value_different(const Object *p_object, const Va
// This must be done because, as some scenes save as text, there might be a tiny difference in floats due to numerical error.
return !Math::is_equal_approx((float)p_a, (float)p_b);
} else if (p_a.get_type() == Variant::NODE_PATH && p_b.get_type() == Variant::OBJECT) {
+ // With properties of type Node, left side is NodePath, while right side is Node.
const Node *base_node = Object::cast_to<Node>(p_object);
const Node *target_node = Object::cast_to<Node>(p_b);
if (base_node && target_node) {
@@ -51,6 +52,23 @@ bool PropertyUtils::is_property_value_different(const Object *p_object, const Va
}
}
+ if (p_a.get_type() == Variant::ARRAY && p_b.get_type() == Variant::ARRAY) {
+ const Node *base_node = Object::cast_to<Node>(p_object);
+ Array array1 = p_a;
+ Array array2 = p_b;
+
+ if (base_node && !array1.is_empty() && array2.size() == array1.size() && array1[0].get_type() == Variant::NODE_PATH && array2[0].get_type() == Variant::OBJECT) {
+ // Like above, but NodePaths/Nodes are inside arrays.
+ for (int i = 0; i < array1.size(); i++) {
+ const Node *target_node = Object::cast_to<Node>(array2[i]);
+ if (!target_node || array1[i] != base_node->get_path_to(target_node)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
// For our purposes, treating null object as NIL is the right thing to do
const Variant &a = p_a.get_type() == Variant::OBJECT && (Object *)p_a == nullptr ? Variant() : p_a;
const Variant &b = p_b.get_type() == Variant::OBJECT && (Object *)p_b == nullptr ? Variant() : p_b;
diff --git a/scene/resources/2d/navigation_polygon.cpp b/scene/resources/2d/navigation_polygon.cpp
index 274b13a487..a845809bf2 100644
--- a/scene/resources/2d/navigation_polygon.cpp
+++ b/scene/resources/2d/navigation_polygon.cpp
@@ -38,6 +38,7 @@
#ifdef TOOLS_ENABLED
Rect2 NavigationPolygon::_edit_get_rect() const {
+ RWLockRead read_lock(rwlock);
if (rect_cache_dirty) {
item_rect = Rect2();
bool first = true;
@@ -65,6 +66,7 @@ Rect2 NavigationPolygon::_edit_get_rect() const {
}
bool NavigationPolygon::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const {
+ RWLockRead read_lock(rwlock);
for (int i = 0; i < outlines.size(); i++) {
const Vector<Vector2> &outline = outlines[i];
const int outline_size = outline.size();
@@ -80,6 +82,7 @@ bool NavigationPolygon::_edit_is_selected_on_click(const Point2 &p_point, double
#endif
void NavigationPolygon::set_vertices(const Vector<Vector2> &p_vertices) {
+ RWLockWrite write_lock(rwlock);
{
MutexLock lock(navigation_mesh_generation);
navigation_mesh.unref();
@@ -89,10 +92,12 @@ void NavigationPolygon::set_vertices(const Vector<Vector2> &p_vertices) {
}
Vector<Vector2> NavigationPolygon::get_vertices() const {
+ RWLockRead read_lock(rwlock);
return vertices;
}
void NavigationPolygon::_set_polygons(const TypedArray<Vector<int32_t>> &p_array) {
+ RWLockWrite write_lock(rwlock);
{
MutexLock lock(navigation_mesh_generation);
navigation_mesh.unref();
@@ -104,6 +109,7 @@ void NavigationPolygon::_set_polygons(const TypedArray<Vector<int32_t>> &p_array
}
TypedArray<Vector<int32_t>> NavigationPolygon::_get_polygons() const {
+ RWLockRead read_lock(rwlock);
TypedArray<Vector<int32_t>> ret;
ret.resize(polygons.size());
for (int i = 0; i < ret.size(); i++) {
@@ -114,6 +120,7 @@ TypedArray<Vector<int32_t>> NavigationPolygon::_get_polygons() const {
}
void NavigationPolygon::_set_outlines(const TypedArray<Vector<Vector2>> &p_array) {
+ RWLockWrite write_lock(rwlock);
outlines.resize(p_array.size());
for (int i = 0; i < p_array.size(); i++) {
outlines.write[i] = p_array[i];
@@ -122,6 +129,7 @@ void NavigationPolygon::_set_outlines(const TypedArray<Vector<Vector2>> &p_array
}
TypedArray<Vector<Vector2>> NavigationPolygon::_get_outlines() const {
+ RWLockRead read_lock(rwlock);
TypedArray<Vector<Vector2>> ret;
ret.resize(outlines.size());
for (int i = 0; i < ret.size(); i++) {
@@ -132,6 +140,7 @@ TypedArray<Vector<Vector2>> NavigationPolygon::_get_outlines() const {
}
void NavigationPolygon::add_polygon(const Vector<int> &p_polygon) {
+ RWLockWrite write_lock(rwlock);
Polygon polygon;
polygon.indices = p_polygon;
polygons.push_back(polygon);
@@ -142,20 +151,24 @@ void NavigationPolygon::add_polygon(const Vector<int> &p_polygon) {
}
void NavigationPolygon::add_outline_at_index(const Vector<Vector2> &p_outline, int p_index) {
+ RWLockWrite write_lock(rwlock);
outlines.insert(p_index, p_outline);
rect_cache_dirty = true;
}
int NavigationPolygon::get_polygon_count() const {
+ RWLockRead read_lock(rwlock);
return polygons.size();
}
Vector<int> NavigationPolygon::get_polygon(int p_idx) {
+ RWLockRead read_lock(rwlock);
ERR_FAIL_INDEX_V(p_idx, polygons.size(), Vector<int>());
return polygons[p_idx].indices;
}
void NavigationPolygon::clear_polygons() {
+ RWLockWrite write_lock(rwlock);
polygons.clear();
{
MutexLock lock(navigation_mesh_generation);
@@ -164,6 +177,7 @@ void NavigationPolygon::clear_polygons() {
}
void NavigationPolygon::clear() {
+ RWLockWrite write_lock(rwlock);
polygons.clear();
vertices.clear();
{
@@ -172,12 +186,31 @@ void NavigationPolygon::clear() {
}
}
+void NavigationPolygon::set_data(const Vector<Vector2> &p_vertices, const Vector<Vector<int>> &p_polygons) {
+ RWLockWrite write_lock(rwlock);
+ vertices = p_vertices;
+ polygons.resize(p_polygons.size());
+ for (int i = 0; i < p_polygons.size(); i++) {
+ polygons.write[i].indices = p_polygons[i];
+ }
+}
+
+void NavigationPolygon::get_data(Vector<Vector2> &r_vertices, Vector<Vector<int>> &r_polygons) {
+ RWLockRead read_lock(rwlock);
+ r_vertices = vertices;
+ r_polygons.resize(polygons.size());
+ for (int i = 0; i < polygons.size(); i++) {
+ r_polygons.write[i] = polygons[i].indices;
+ }
+}
+
Ref<NavigationMesh> NavigationPolygon::get_navigation_mesh() {
MutexLock lock(navigation_mesh_generation);
if (navigation_mesh.is_null()) {
navigation_mesh.instantiate();
Vector<Vector3> verts;
+ Vector<Vector<int>> polys;
{
verts.resize(get_vertices().size());
Vector3 *w = verts.ptrw();
@@ -188,11 +221,12 @@ Ref<NavigationMesh> NavigationPolygon::get_navigation_mesh() {
w[i] = Vector3(r[i].x, 0.0, r[i].y);
}
}
- navigation_mesh->set_vertices(verts);
for (int i(0); i < get_polygon_count(); i++) {
- navigation_mesh->add_polygon(get_polygon(i));
+ polys.push_back(get_polygon(i));
}
+
+ navigation_mesh->set_data(verts, polys);
navigation_mesh->set_cell_size(cell_size); // Needed to not fail the cell size check on the server
}
@@ -200,38 +234,45 @@ Ref<NavigationMesh> NavigationPolygon::get_navigation_mesh() {
}
void NavigationPolygon::add_outline(const Vector<Vector2> &p_outline) {
+ RWLockWrite write_lock(rwlock);
outlines.push_back(p_outline);
rect_cache_dirty = true;
}
int NavigationPolygon::get_outline_count() const {
+ RWLockRead read_lock(rwlock);
return outlines.size();
}
void NavigationPolygon::set_outline(int p_idx, const Vector<Vector2> &p_outline) {
+ RWLockWrite write_lock(rwlock);
ERR_FAIL_INDEX(p_idx, outlines.size());
outlines.write[p_idx] = p_outline;
rect_cache_dirty = true;
}
void NavigationPolygon::remove_outline(int p_idx) {
+ RWLockWrite write_lock(rwlock);
ERR_FAIL_INDEX(p_idx, outlines.size());
outlines.remove_at(p_idx);
rect_cache_dirty = true;
}
Vector<Vector2> NavigationPolygon::get_outline(int p_idx) const {
+ RWLockRead read_lock(rwlock);
ERR_FAIL_INDEX_V(p_idx, outlines.size(), Vector<Vector2>());
return outlines[p_idx];
}
void NavigationPolygon::clear_outlines() {
+ RWLockWrite write_lock(rwlock);
outlines.clear();
rect_cache_dirty = true;
}
#ifndef DISABLE_DEPRECATED
void NavigationPolygon::make_polygons_from_outlines() {
+ RWLockWrite write_lock(rwlock);
WARN_PRINT("Function make_polygons_from_outlines() is deprecated."
"\nUse NavigationServer2D.parse_source_geometry_data() and NavigationServer2D.bake_from_source_geometry_data() instead.");
diff --git a/scene/resources/2d/navigation_polygon.h b/scene/resources/2d/navigation_polygon.h
index b9816f900c..4e99660b0e 100644
--- a/scene/resources/2d/navigation_polygon.h
+++ b/scene/resources/2d/navigation_polygon.h
@@ -36,6 +36,7 @@
class NavigationPolygon : public Resource {
GDCLASS(NavigationPolygon, Resource);
+ RWLock rwlock;
Vector<Vector2> vertices;
struct Polygon {
@@ -153,6 +154,9 @@ public:
void clear();
+ void set_data(const Vector<Vector2> &p_vertices, const Vector<Vector<int>> &p_polygons);
+ void get_data(Vector<Vector2> &r_vertices, Vector<Vector<int>> &r_polygons);
+
NavigationPolygon() {}
~NavigationPolygon() {}
};
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index 65b97acabf..b2567e431b 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -1337,7 +1337,7 @@ void fragment() {)";
}
// Heightmapping isn't supported at the same time as triplanar mapping.
- if (!RenderingServer::get_singleton()->is_low_end() && features[FEATURE_HEIGHT_MAPPING] && !flags[FLAG_UV1_USE_TRIPLANAR]) {
+ if (features[FEATURE_HEIGHT_MAPPING] && !flags[FLAG_UV1_USE_TRIPLANAR]) {
// Binormal is negative due to mikktspace. Flipping it "unflips" it.
code += R"(
{
@@ -1637,21 +1637,20 @@ void fragment() {)";
// Use the slightly more expensive circular fade (distance to the object) instead of linear
// (Z distance), so that the fade is always the same regardless of the camera angle.
if ((distance_fade == DISTANCE_FADE_OBJECT_DITHER || distance_fade == DISTANCE_FADE_PIXEL_DITHER)) {
- if (!RenderingServer::get_singleton()->is_low_end()) {
- code += "\n {";
+ code += "\n {";
- if (distance_fade == DISTANCE_FADE_OBJECT_DITHER) {
- code += R"(
+ if (distance_fade == DISTANCE_FADE_OBJECT_DITHER) {
+ code += R"(
// Distance Fade: Object Dither
float fade_distance = length((VIEW_MATRIX * MODEL_MATRIX[3]));
)";
- } else {
- code += R"(
+ } else {
+ code += R"(
// Distance Fade: Pixel Dither
float fade_distance = length(VERTEX);
)";
- }
- code += R"(
+ }
+ code += R"(
// Use interleaved gradient noise, which is fast but still looks good.
const vec3 magic = vec3(0.06711056, 0.00583715, 52.9829189);
float fade = clamp(smoothstep(distance_fade_min, distance_fade_max, fade_distance), 0.0, 1.0);
@@ -1661,7 +1660,6 @@ void fragment() {)";
}
}
)";
- }
} else {
code += R"(
// Distance Fade: Pixel Alpha
diff --git a/tests/scene/test_node.h b/tests/scene/test_node.h
index 93ef3fe728..05764d8f29 100644
--- a/tests/scene/test_node.h
+++ b/tests/scene/test_node.h
@@ -68,6 +68,10 @@ protected:
ClassDB::bind_method(D_METHOD("set_exported_node", "node"), &TestNode::set_exported_node);
ClassDB::bind_method(D_METHOD("get_exported_node"), &TestNode::get_exported_node);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "exported_node", PROPERTY_HINT_NODE_TYPE, "Node"), "set_exported_node", "get_exported_node");
+
+ ClassDB::bind_method(D_METHOD("set_exported_nodes", "node"), &TestNode::set_exported_nodes);
+ ClassDB::bind_method(D_METHOD("get_exported_nodes"), &TestNode::get_exported_nodes);
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "exported_nodes", PROPERTY_HINT_TYPE_STRING, "24/34:Node"), "set_exported_nodes", "get_exported_nodes");
}
private:
@@ -84,11 +88,15 @@ public:
int physics_process_counter = 0;
Node *exported_node = nullptr;
+ Array exported_nodes;
List<Node *> *callback_list = nullptr;
void set_exported_node(Node *p_node) { exported_node = p_node; }
Node *get_exported_node() const { return exported_node; }
+
+ void set_exported_nodes(const Array &p_nodes) { exported_nodes = p_nodes; }
+ Array get_exported_nodes() const { return exported_nodes; }
};
TEST_CASE("[SceneTree][Node] Testing node operations with a very simple scene tree") {
@@ -500,7 +508,16 @@ TEST_CASE("[SceneTree][Node]Exported node checks") {
node->add_child(child);
child->set_owner(node);
+ Node *child2 = memnew(Node);
+ child2->set_name("Child2");
+ node->add_child(child2);
+ child2->set_owner(node);
+
+ Array children;
+ children.append(child);
+
node->set("exported_node", child);
+ node->set("exported_nodes", children);
SUBCASE("Property of duplicated node should point to duplicated child") {
GDREGISTER_CLASS(TestNode);
@@ -512,8 +529,7 @@ TEST_CASE("[SceneTree][Node]Exported node checks") {
memdelete(dup);
}
- SUBCASE("Saving instance with exported node should not store the unchanged property") {
- node->set_process_mode(Node::PROCESS_MODE_ALWAYS);
+ SUBCASE("Saving instance with exported nodes should not store the unchanged property") {
Ref<PackedScene> ps;
ps.instantiate();
ps->pack(node);
@@ -548,6 +564,45 @@ TEST_CASE("[SceneTree][Node]Exported node checks") {
CHECK_FALSE(is_wrong);
}
+ SUBCASE("Saving instance with exported nodes should store property if changed") {
+ Ref<PackedScene> ps;
+ ps.instantiate();
+ ps->pack(node);
+
+ String scene_path = TestUtils::get_temp_path("test_scene.tscn");
+ ps->set_path(scene_path);
+
+ Node *root = memnew(Node);
+
+ Node *sub_child = ps->instantiate(PackedScene::GEN_EDIT_STATE_MAIN);
+ root->add_child(sub_child);
+ sub_child->set_owner(root);
+
+ sub_child->set("exported_node", sub_child->get_child(1));
+
+ children = Array();
+ children.append(sub_child->get_child(1));
+ sub_child->set("exported_nodes", children);
+
+ Ref<PackedScene> ps2;
+ ps2.instantiate();
+ ps2->pack(root);
+
+ scene_path = TestUtils::get_temp_path("new_test_scene2.tscn");
+ ResourceSaver::save(ps2, scene_path);
+ memdelete(root);
+
+ int stored_properties = 0;
+ Ref<FileAccess> fa = FileAccess::open(scene_path, FileAccess::READ);
+ while (!fa->eof_reached()) {
+ const String line = fa->get_line();
+ if (line.begins_with("exported_node")) {
+ stored_properties++;
+ }
+ }
+ CHECK_EQ(stored_properties, 2);
+ }
+
memdelete(node);
}