Compare commits
52 Commits
03bd8ba9c2
...
4261cc07a5
Author | SHA1 | Date | |
---|---|---|---|
|
4261cc07a5 | ||
|
94d7a54fea | ||
|
ce3ebacb3e | ||
|
ae33da972b | ||
|
4adef85151 | ||
|
7842a042ae | ||
|
6eb6e3e6e0 | ||
|
a4b99785dc | ||
|
6c52478491 | ||
|
5507b3aa80 | ||
|
f7a3ea72ae | ||
|
34e4abd220 | ||
|
e90fd0b3af | ||
|
ea908f1ab2 | ||
|
ae484828bb | ||
|
9eda3c682d | ||
|
6845898461 | ||
|
53da681e89 | ||
|
1a64b6b5b6 | ||
|
e0072e9169 | ||
|
88c47c663d | ||
|
2d8e408e7e | ||
|
d46512a89d | ||
|
c1eaf51db5 | ||
|
f9475a892e | ||
|
786bf741f4 | ||
|
db9b8ff003 | ||
|
0f0229e5b7 | ||
|
d8376e6256 | ||
|
d160d71796 | ||
|
805ad87340 | ||
|
246253661d | ||
|
d1083c9722 | ||
|
1725231e14 | ||
|
bf8edd2c2a | ||
|
beb8398857 | ||
|
66d68707d3 | ||
|
a1e12dca3f | ||
|
2d0ff9774d | ||
|
41009718d2 | ||
|
e2d4469dc2 | ||
|
b13a0e1834 | ||
|
e2931a5c19 | ||
|
2a1d2afaca | ||
|
2458370de4 | ||
|
2674ffe1b7 | ||
|
547450befd | ||
|
12ad9ed4e0 | ||
|
b0e472c8e4 | ||
|
e9edff8d25 | ||
|
49d4168f30 | ||
|
472cdb6da2 |
@ -824,7 +824,7 @@ Error ProjectSettings::_load_settings_binary(const String &p_path) {
|
|||||||
for (uint32_t i = 0; i < count; i++) {
|
for (uint32_t i = 0; i < count; i++) {
|
||||||
uint32_t slen = f->get_32();
|
uint32_t slen = f->get_32();
|
||||||
CharString cs;
|
CharString cs;
|
||||||
cs.resize(slen + 1);
|
cs.resize_uninitialized(slen + 1);
|
||||||
cs[slen] = 0;
|
cs[slen] = 0;
|
||||||
f->get_buffer((uint8_t *)cs.ptr(), slen);
|
f->get_buffer((uint8_t *)cs.ptr(), slen);
|
||||||
String key = String::utf8(cs.ptr(), slen);
|
String key = String::utf8(cs.ptr(), slen);
|
||||||
|
@ -1045,7 +1045,7 @@ static void gdextension_string_operator_plus_eq_c32str(GDExtensionStringPtr p_se
|
|||||||
|
|
||||||
static GDExtensionInt gdextension_string_resize(GDExtensionStringPtr p_self, GDExtensionInt p_length) {
|
static GDExtensionInt gdextension_string_resize(GDExtensionStringPtr p_self, GDExtensionInt p_length) {
|
||||||
String *self = (String *)p_self;
|
String *self = (String *)p_self;
|
||||||
return (*self).resize(p_length);
|
return (*self).resize_uninitialized(p_length);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gdextension_string_name_new_with_latin1_chars(GDExtensionUninitializedStringNamePtr r_dest, const char *p_contents, GDExtensionBool p_is_static) {
|
static void gdextension_string_name_new_with_latin1_chars(GDExtensionUninitializedStringNamePtr r_dest, const char *p_contents, GDExtensionBool p_is_static) {
|
||||||
|
@ -258,7 +258,12 @@ String FileAccess::fix_path(const String &p_path) const {
|
|||||||
case ACCESS_RESOURCES: {
|
case ACCESS_RESOURCES: {
|
||||||
if (ProjectSettings::get_singleton()) {
|
if (ProjectSettings::get_singleton()) {
|
||||||
if (r_path.begins_with("uid://")) {
|
if (r_path.begins_with("uid://")) {
|
||||||
r_path = ResourceUID::uid_to_path(r_path);
|
ResourceUID::ID uid = ResourceUID::get_singleton()->text_to_id(r_path);
|
||||||
|
if (ResourceUID::get_singleton()->has_id(uid)) {
|
||||||
|
r_path = ResourceUID::get_singleton()->get_id_path(uid);
|
||||||
|
} else {
|
||||||
|
r_path.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (r_path.begins_with("res://")) {
|
if (r_path.begins_with("res://")) {
|
||||||
@ -769,7 +774,7 @@ bool FileAccess::store_pascal_string(const String &p_string) {
|
|||||||
String FileAccess::get_pascal_string() {
|
String FileAccess::get_pascal_string() {
|
||||||
uint32_t sl = get_32();
|
uint32_t sl = get_32();
|
||||||
CharString cs;
|
CharString cs;
|
||||||
cs.resize(sl + 1);
|
cs.resize_uninitialized(sl + 1);
|
||||||
get_buffer((uint8_t *)cs.ptr(), sl);
|
get_buffer((uint8_t *)cs.ptr(), sl);
|
||||||
cs[sl] = 0;
|
cs[sl] = 0;
|
||||||
|
|
||||||
|
@ -306,7 +306,7 @@ bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files,
|
|||||||
for (int i = 0; i < file_count; i++) {
|
for (int i = 0; i < file_count; i++) {
|
||||||
uint32_t sl = f->get_32();
|
uint32_t sl = f->get_32();
|
||||||
CharString cs;
|
CharString cs;
|
||||||
cs.resize(sl + 1);
|
cs.resize_uninitialized(sl + 1);
|
||||||
f->get_buffer((uint8_t *)cs.ptr(), sl);
|
f->get_buffer((uint8_t *)cs.ptr(), sl);
|
||||||
cs[sl] = 0;
|
cs[sl] = 0;
|
||||||
|
|
||||||
|
@ -553,7 +553,7 @@ Ref<PListNode> PList::read_bplist_obj(Ref<FileAccess> p_file, uint64_t p_offset_
|
|||||||
marker_size = read_bplist_var_size_int(p_file, std::pow(2, ext));
|
marker_size = read_bplist_var_size_int(p_file, std::pow(2, ext));
|
||||||
}
|
}
|
||||||
node->data_type = PL_NODE_TYPE_STRING;
|
node->data_type = PL_NODE_TYPE_STRING;
|
||||||
node->data_string.resize(marker_size + 1);
|
node->data_string.resize_uninitialized(marker_size + 1);
|
||||||
p_file->get_buffer(reinterpret_cast<uint8_t *>(node->data_string.ptrw()), marker_size);
|
p_file->get_buffer(reinterpret_cast<uint8_t *>(node->data_string.ptrw()), marker_size);
|
||||||
} break;
|
} break;
|
||||||
case 0x60: {
|
case 0x60: {
|
||||||
@ -562,7 +562,7 @@ Ref<PListNode> PList::read_bplist_obj(Ref<FileAccess> p_file, uint64_t p_offset_
|
|||||||
marker_size = read_bplist_var_size_int(p_file, std::pow(2, ext));
|
marker_size = read_bplist_var_size_int(p_file, std::pow(2, ext));
|
||||||
}
|
}
|
||||||
Char16String cs16;
|
Char16String cs16;
|
||||||
cs16.resize(marker_size + 1);
|
cs16.resize_uninitialized(marker_size + 1);
|
||||||
for (uint64_t i = 0; i < marker_size; i++) {
|
for (uint64_t i = 0; i < marker_size; i++) {
|
||||||
cs16[i] = BSWAP16(p_file->get_16());
|
cs16[i] = BSWAP16(p_file->get_16());
|
||||||
}
|
}
|
||||||
|
@ -143,7 +143,7 @@ String Resource::generate_scene_unique_id() {
|
|||||||
static constexpr uint32_t char_count = ('z' - 'a');
|
static constexpr uint32_t char_count = ('z' - 'a');
|
||||||
static constexpr uint32_t base = char_count + ('9' - '0');
|
static constexpr uint32_t base = char_count + ('9' - '0');
|
||||||
String id;
|
String id;
|
||||||
id.resize(characters + 1);
|
id.resize_uninitialized(characters + 1);
|
||||||
char32_t *ptr = id.ptrw();
|
char32_t *ptr = id.ptrw();
|
||||||
for (uint32_t i = 0; i < characters; i++) {
|
for (uint32_t i = 0; i < characters; i++) {
|
||||||
uint32_t c = random_num % base;
|
uint32_t c = random_num % base;
|
||||||
|
@ -66,7 +66,7 @@ String ResourceUID::id_to_text(ID p_id) const {
|
|||||||
|
|
||||||
// tmp_size + uid:// (6) + 1 for null.
|
// tmp_size + uid:// (6) + 1 for null.
|
||||||
String txt;
|
String txt;
|
||||||
txt.resize(tmp_size + 7);
|
txt.resize_uninitialized(tmp_size + 7);
|
||||||
|
|
||||||
char32_t *p = txt.ptrw();
|
char32_t *p = txt.ptrw();
|
||||||
p[0] = 'u';
|
p[0] = 'u';
|
||||||
@ -273,7 +273,7 @@ Error ResourceUID::load_from_cache(bool p_reset) {
|
|||||||
int64_t id = f->get_64();
|
int64_t id = f->get_64();
|
||||||
int32_t len = f->get_32();
|
int32_t len = f->get_32();
|
||||||
Cache c;
|
Cache c;
|
||||||
c.cs.resize(len + 1);
|
c.cs.resize_uninitialized(len + 1);
|
||||||
ERR_FAIL_COND_V(c.cs.size() != len + 1, ERR_FILE_CORRUPT); // Out of memory.
|
ERR_FAIL_COND_V(c.cs.size() != len + 1, ERR_FILE_CORRUPT); // Out of memory.
|
||||||
c.cs[len] = 0;
|
c.cs[len] = 0;
|
||||||
int32_t rl = f->get_buffer((uint8_t *)c.cs.ptrw(), len);
|
int32_t rl = f->get_buffer((uint8_t *)c.cs.ptrw(), len);
|
||||||
@ -333,7 +333,7 @@ String ResourceUID::get_path_from_cache(Ref<FileAccess> &p_cache_file, const Str
|
|||||||
for (uint32_t i = 0; i < entry_count; i++) {
|
for (uint32_t i = 0; i < entry_count; i++) {
|
||||||
int64_t id = p_cache_file->get_64();
|
int64_t id = p_cache_file->get_64();
|
||||||
int32_t len = p_cache_file->get_32();
|
int32_t len = p_cache_file->get_32();
|
||||||
cs.resize(len + 1);
|
cs.resize_uninitialized(len + 1);
|
||||||
ERR_FAIL_COND_V(cs.size() != len + 1, String());
|
ERR_FAIL_COND_V(cs.size() != len + 1, String());
|
||||||
cs[len] = 0;
|
cs[len] = 0;
|
||||||
int32_t rl = p_cache_file->get_buffer((uint8_t *)cs.ptrw(), len);
|
int32_t rl = p_cache_file->get_buffer((uint8_t *)cs.ptrw(), len);
|
||||||
|
@ -119,7 +119,7 @@ void _append_hex(float p_val, char32_t *string) {
|
|||||||
|
|
||||||
String Color::to_html(bool p_alpha) const {
|
String Color::to_html(bool p_alpha) const {
|
||||||
String txt;
|
String txt;
|
||||||
txt.resize(p_alpha ? 9 : 7);
|
txt.resize_uninitialized(p_alpha ? 9 : 7);
|
||||||
char32_t *ptr = txt.ptrw();
|
char32_t *ptr = txt.ptrw();
|
||||||
|
|
||||||
_append_hex(r, ptr + 0);
|
_append_hex(r, ptr + 0);
|
||||||
|
@ -823,7 +823,7 @@ Expression::ENode *Expression::_parse_expression() {
|
|||||||
if (!Variant::is_utility_function_vararg(bifunc->func)) {
|
if (!Variant::is_utility_function_vararg(bifunc->func)) {
|
||||||
int expected_args = Variant::get_utility_function_argument_count(bifunc->func);
|
int expected_args = Variant::get_utility_function_argument_count(bifunc->func);
|
||||||
if (expected_args != bifunc->arguments.size()) {
|
if (expected_args != bifunc->arguments.size()) {
|
||||||
_set_error("Builtin func '" + String(bifunc->func) + "' expects " + itos(expected_args) + " arguments.");
|
_set_error("Builtin func '" + String(bifunc->func) + "' expects " + itos(expected_args) + " argument(s).");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ public:
|
|||||||
return data->hash_cache;
|
return data->hash_cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
operator String() const;
|
explicit operator String() const;
|
||||||
bool is_empty() const;
|
bool is_empty() const;
|
||||||
|
|
||||||
bool operator==(const NodePath &p_path) const;
|
bool operator==(const NodePath &p_path) const;
|
||||||
|
@ -82,20 +82,20 @@ void OptimizedTranslation::generate(const Ref<Translation> &p_from) {
|
|||||||
|
|
||||||
if (ps.orig_len != 0) {
|
if (ps.orig_len != 0) {
|
||||||
CharString dst_s;
|
CharString dst_s;
|
||||||
dst_s.resize(src_s.size());
|
dst_s.resize_uninitialized(src_s.size());
|
||||||
int ret = smaz_compress(src_s.get_data(), src_s.size(), dst_s.ptrw(), src_s.size());
|
int ret = smaz_compress(src_s.get_data(), src_s.size(), dst_s.ptrw(), src_s.size());
|
||||||
if (ret >= src_s.size()) {
|
if (ret >= src_s.size()) {
|
||||||
//if compressed is larger than original, just use original
|
//if compressed is larger than original, just use original
|
||||||
ps.orig_len = src_s.size();
|
ps.orig_len = src_s.size();
|
||||||
ps.compressed = src_s;
|
ps.compressed = src_s;
|
||||||
} else {
|
} else {
|
||||||
dst_s.resize(ret);
|
dst_s.resize_uninitialized(ret);
|
||||||
//ps.orig_len=;
|
//ps.orig_len=;
|
||||||
ps.compressed = dst_s;
|
ps.compressed = dst_s;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ps.orig_len = 1;
|
ps.orig_len = 1;
|
||||||
ps.compressed.resize(1);
|
ps.compressed.resize_uninitialized(1);
|
||||||
ps.compressed[0] = 0;
|
ps.compressed[0] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -256,7 +256,7 @@ StringName OptimizedTranslation::get_message(const StringName &p_src_text, const
|
|||||||
return String::utf8(&sptr[bucket.elem[idx].str_offset], bucket.elem[idx].uncomp_size);
|
return String::utf8(&sptr[bucket.elem[idx].str_offset], bucket.elem[idx].uncomp_size);
|
||||||
} else {
|
} else {
|
||||||
CharString uncomp;
|
CharString uncomp;
|
||||||
uncomp.resize(bucket.elem[idx].uncomp_size + 1);
|
uncomp.resize_uninitialized(bucket.elem[idx].uncomp_size + 1);
|
||||||
smaz_decompress(&sptr[bucket.elem[idx].str_offset], bucket.elem[idx].comp_size, uncomp.ptrw(), bucket.elem[idx].uncomp_size);
|
smaz_decompress(&sptr[bucket.elem[idx].str_offset], bucket.elem[idx].comp_size, uncomp.ptrw(), bucket.elem[idx].uncomp_size);
|
||||||
return String::utf8(uncomp.get_data());
|
return String::utf8(uncomp.get_data());
|
||||||
}
|
}
|
||||||
@ -282,7 +282,7 @@ Vector<String> OptimizedTranslation::get_translated_message_list() const {
|
|||||||
msgs.push_back(rstr);
|
msgs.push_back(rstr);
|
||||||
} else {
|
} else {
|
||||||
CharString uncomp;
|
CharString uncomp;
|
||||||
uncomp.resize(bucket.elem[j].uncomp_size + 1);
|
uncomp.resize_uninitialized(bucket.elem[j].uncomp_size + 1);
|
||||||
smaz_decompress(&sptr[bucket.elem[j].str_offset], bucket.elem[j].comp_size, uncomp.ptrw(), bucket.elem[j].uncomp_size);
|
smaz_decompress(&sptr[bucket.elem[j].str_offset], bucket.elem[j].comp_size, uncomp.ptrw(), bucket.elem[j].uncomp_size);
|
||||||
String rstr = String::utf8(uncomp.get_data());
|
String rstr = String::utf8(uncomp.get_data());
|
||||||
msgs.push_back(rstr);
|
msgs.push_back(rstr);
|
||||||
|
@ -123,7 +123,7 @@ StringBuffer<SHORT_BUFFER_SIZE> &StringBuffer<SHORT_BUFFER_SIZE>::reserve(int p_
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool need_copy = string_length > 0 && buffer.is_empty();
|
bool need_copy = string_length > 0 && buffer.is_empty();
|
||||||
buffer.resize(next_power_of_2((uint32_t)p_size));
|
buffer.resize_uninitialized(next_power_of_2((uint32_t)p_size));
|
||||||
if (need_copy) {
|
if (need_copy) {
|
||||||
memcpy(buffer.ptrw(), short_buffer, string_length * sizeof(char32_t));
|
memcpy(buffer.ptrw(), short_buffer, string_length * sizeof(char32_t));
|
||||||
}
|
}
|
||||||
@ -142,7 +142,7 @@ String StringBuffer<SHORT_BUFFER_SIZE>::as_string() {
|
|||||||
if (buffer.is_empty()) {
|
if (buffer.is_empty()) {
|
||||||
return String(short_buffer);
|
return String(short_buffer);
|
||||||
} else {
|
} else {
|
||||||
buffer.resize(string_length + 1);
|
buffer.resize_uninitialized(string_length + 1);
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,7 @@ String StringBuilder::as_string() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String string;
|
String string;
|
||||||
string.resize(string_length + 1);
|
string.resize_uninitialized(string_length + 1);
|
||||||
char32_t *buffer = string.ptrw();
|
char32_t *buffer = string.ptrw();
|
||||||
|
|
||||||
int current_position = 0;
|
int current_position = 0;
|
||||||
|
@ -175,7 +175,7 @@ void String::append_latin1(const Span<char> &p_cstr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const int prev_length = length();
|
const int prev_length = length();
|
||||||
resize(prev_length + p_cstr.size() + 1); // include 0
|
resize_uninitialized(prev_length + p_cstr.size() + 1); // include 0
|
||||||
|
|
||||||
const char *src = p_cstr.ptr();
|
const char *src = p_cstr.ptr();
|
||||||
const char *end = src + p_cstr.size();
|
const char *end = src + p_cstr.size();
|
||||||
@ -194,7 +194,7 @@ void String::append_utf32(const Span<char32_t> &p_cstr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const int prev_length = length();
|
const int prev_length = length();
|
||||||
resize(prev_length + p_cstr.size() + 1);
|
resize_uninitialized(prev_length + p_cstr.size() + 1);
|
||||||
const char32_t *src = p_cstr.ptr();
|
const char32_t *src = p_cstr.ptr();
|
||||||
const char32_t *end = p_cstr.ptr() + p_cstr.size();
|
const char32_t *end = p_cstr.ptr() + p_cstr.size();
|
||||||
char32_t *dst = ptrw() + prev_length;
|
char32_t *dst = ptrw() + prev_length;
|
||||||
@ -223,7 +223,7 @@ void String::append_utf32(const Span<char32_t> &p_cstr) {
|
|||||||
// p_length <= p_char strlen
|
// p_length <= p_char strlen
|
||||||
// p_char is a valid UTF32 string
|
// p_char is a valid UTF32 string
|
||||||
void String::copy_from_unchecked(const char32_t *p_char, const int p_length) {
|
void String::copy_from_unchecked(const char32_t *p_char, const int p_length) {
|
||||||
resize(p_length + 1); // + 1 for \0
|
resize_uninitialized(p_length + 1); // + 1 for \0
|
||||||
char32_t *dst = ptrw();
|
char32_t *dst = ptrw();
|
||||||
memcpy(dst, p_char, p_length * sizeof(char32_t));
|
memcpy(dst, p_char, p_length * sizeof(char32_t));
|
||||||
*(dst + p_length) = _null;
|
*(dst + p_length) = _null;
|
||||||
@ -1365,7 +1365,7 @@ String String::join(const Vector<String> &parts) const {
|
|||||||
new_size += 1;
|
new_size += 1;
|
||||||
|
|
||||||
String ret;
|
String ret;
|
||||||
ret.resize(new_size);
|
ret.resize_uninitialized(new_size);
|
||||||
char32_t *ret_ptrw = ret.ptrw();
|
char32_t *ret_ptrw = ret.ptrw();
|
||||||
const char32_t *this_ptr = ptr();
|
const char32_t *this_ptr = ptr();
|
||||||
|
|
||||||
@ -1404,7 +1404,7 @@ String String::to_upper() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String upper;
|
String upper;
|
||||||
upper.resize(size());
|
upper.resize_uninitialized(size());
|
||||||
const char32_t *old_ptr = ptr();
|
const char32_t *old_ptr = ptr();
|
||||||
char32_t *upper_ptrw = upper.ptrw();
|
char32_t *upper_ptrw = upper.ptrw();
|
||||||
|
|
||||||
@ -1423,7 +1423,7 @@ String String::to_lower() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String lower;
|
String lower;
|
||||||
lower.resize(size());
|
lower.resize_uninitialized(size());
|
||||||
const char32_t *old_ptr = ptr();
|
const char32_t *old_ptr = ptr();
|
||||||
char32_t *lower_ptrw = lower.ptrw();
|
char32_t *lower_ptrw = lower.ptrw();
|
||||||
|
|
||||||
@ -1549,7 +1549,7 @@ String String::num_int64(int64_t p_num, int base, bool capitalize_hex) {
|
|||||||
chars++;
|
chars++;
|
||||||
}
|
}
|
||||||
String s;
|
String s;
|
||||||
s.resize(chars + 1);
|
s.resize_uninitialized(chars + 1);
|
||||||
char32_t *c = s.ptrw();
|
char32_t *c = s.ptrw();
|
||||||
c[chars] = 0;
|
c[chars] = 0;
|
||||||
n = p_num;
|
n = p_num;
|
||||||
@ -1584,7 +1584,7 @@ String String::num_uint64(uint64_t p_num, int base, bool capitalize_hex) {
|
|||||||
} while (n);
|
} while (n);
|
||||||
|
|
||||||
String s;
|
String s;
|
||||||
s.resize(chars + 1);
|
s.resize_uninitialized(chars + 1);
|
||||||
char32_t *c = s.ptrw();
|
char32_t *c = s.ptrw();
|
||||||
c[chars] = 0;
|
c[chars] = 0;
|
||||||
n = p_num;
|
n = p_num;
|
||||||
@ -1675,7 +1675,7 @@ String String::hex_encode_buffer(const uint8_t *p_buffer, int p_len) {
|
|||||||
static const char hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
|
static const char hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
|
||||||
|
|
||||||
String ret;
|
String ret;
|
||||||
ret.resize(p_len * 2 + 1);
|
ret.resize_uninitialized(p_len * 2 + 1);
|
||||||
char32_t *ret_ptrw = ret.ptrw();
|
char32_t *ret_ptrw = ret.ptrw();
|
||||||
|
|
||||||
for (int i = 0; i < p_len; i++) {
|
for (int i = 0; i < p_len; i++) {
|
||||||
@ -1706,7 +1706,7 @@ Vector<uint8_t> String::hex_decode() const {
|
|||||||
|
|
||||||
Vector<uint8_t> out;
|
Vector<uint8_t> out;
|
||||||
int len = length() / 2;
|
int len = length() / 2;
|
||||||
out.resize(len);
|
out.resize_uninitialized(len);
|
||||||
uint8_t *out_ptrw = out.ptrw();
|
uint8_t *out_ptrw = out.ptrw();
|
||||||
for (int i = 0; i < len; i++) {
|
for (int i = 0; i < len; i++) {
|
||||||
char32_t c;
|
char32_t c;
|
||||||
@ -1732,7 +1732,7 @@ CharString String::ascii(bool p_allow_extended) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
CharString cs;
|
CharString cs;
|
||||||
cs.resize(size());
|
cs.resize_uninitialized(size());
|
||||||
char *cs_ptrw = cs.ptrw();
|
char *cs_ptrw = cs.ptrw();
|
||||||
const char32_t *this_ptr = ptr();
|
const char32_t *this_ptr = ptr();
|
||||||
|
|
||||||
@ -1755,7 +1755,7 @@ Error String::append_ascii(const Span<char> &p_range) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const int prev_length = length();
|
const int prev_length = length();
|
||||||
resize(prev_length + p_range.size() + 1); // Include \0
|
resize_uninitialized(prev_length + p_range.size() + 1); // Include \0
|
||||||
|
|
||||||
const char *src = p_range.ptr();
|
const char *src = p_range.ptr();
|
||||||
const char *end = src + p_range.size();
|
const char *end = src + p_range.size();
|
||||||
@ -1800,7 +1800,7 @@ Error String::append_utf8(const char *p_utf8, int p_len, bool p_skip_cr) {
|
|||||||
|
|
||||||
const int prev_length = length();
|
const int prev_length = length();
|
||||||
// If all utf8 characters maps to ASCII, then the max size will be p_len, and we add +1 for the null termination.
|
// If all utf8 characters maps to ASCII, then the max size will be p_len, and we add +1 for the null termination.
|
||||||
resize(prev_length + p_len + 1);
|
resize_uninitialized(prev_length + p_len + 1);
|
||||||
char32_t *dst = ptrw() + prev_length;
|
char32_t *dst = ptrw() + prev_length;
|
||||||
|
|
||||||
Error result = Error::OK;
|
Error result = Error::OK;
|
||||||
@ -1946,7 +1946,7 @@ Error String::append_utf8(const char *p_utf8, int p_len, bool p_skip_cr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
(*dst++) = 0;
|
(*dst++) = 0;
|
||||||
resize(prev_length + dst - ptr());
|
resize_uninitialized(prev_length + dst - ptr());
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -1959,7 +1959,7 @@ CharString String::utf8(Vector<uint8_t> *r_ch_length_map) const {
|
|||||||
|
|
||||||
uint8_t *map_ptr = nullptr;
|
uint8_t *map_ptr = nullptr;
|
||||||
if (r_ch_length_map) {
|
if (r_ch_length_map) {
|
||||||
r_ch_length_map->resize(l);
|
r_ch_length_map->resize_uninitialized(l);
|
||||||
map_ptr = r_ch_length_map->ptrw();
|
map_ptr = r_ch_length_map->ptrw();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1997,7 +1997,7 @@ CharString String::utf8(Vector<uint8_t> *r_ch_length_map) const {
|
|||||||
return utf8s;
|
return utf8s;
|
||||||
}
|
}
|
||||||
|
|
||||||
utf8s.resize(fl + 1);
|
utf8s.resize_uninitialized(fl + 1);
|
||||||
uint8_t *cdst = (uint8_t *)utf8s.get_data();
|
uint8_t *cdst = (uint8_t *)utf8s.get_data();
|
||||||
|
|
||||||
#define APPEND_CHAR(m_c) *(cdst++) = m_c
|
#define APPEND_CHAR(m_c) *(cdst++) = m_c
|
||||||
@ -2124,7 +2124,7 @@ Error String::append_utf16(const char16_t *p_utf16, int p_len, bool p_default_li
|
|||||||
}
|
}
|
||||||
|
|
||||||
const int prev_length = length();
|
const int prev_length = length();
|
||||||
resize(prev_length + str_size + 1);
|
resize_uninitialized(prev_length + str_size + 1);
|
||||||
char32_t *dst = ptrw() + prev_length;
|
char32_t *dst = ptrw() + prev_length;
|
||||||
dst[str_size] = 0;
|
dst[str_size] = 0;
|
||||||
|
|
||||||
@ -2194,7 +2194,7 @@ Char16String String::utf16() const {
|
|||||||
return utf16s;
|
return utf16s;
|
||||||
}
|
}
|
||||||
|
|
||||||
utf16s.resize(fl + 1);
|
utf16s.resize_uninitialized(fl + 1);
|
||||||
uint16_t *cdst = (uint16_t *)utf16s.get_data();
|
uint16_t *cdst = (uint16_t *)utf16s.get_data();
|
||||||
|
|
||||||
#define APPEND_CHAR(m_c) *(cdst++) = m_c
|
#define APPEND_CHAR(m_c) *(cdst++) = m_c
|
||||||
@ -2847,7 +2847,7 @@ Vector<uint8_t> String::md5_buffer() const {
|
|||||||
CryptoCore::md5((unsigned char *)cs.ptr(), cs.length(), hash);
|
CryptoCore::md5((unsigned char *)cs.ptr(), cs.length(), hash);
|
||||||
|
|
||||||
Vector<uint8_t> ret;
|
Vector<uint8_t> ret;
|
||||||
ret.resize(16);
|
ret.resize_uninitialized(16);
|
||||||
uint8_t *ret_ptrw = ret.ptrw();
|
uint8_t *ret_ptrw = ret.ptrw();
|
||||||
for (int i = 0; i < 16; i++) {
|
for (int i = 0; i < 16; i++) {
|
||||||
ret_ptrw[i] = hash[i];
|
ret_ptrw[i] = hash[i];
|
||||||
@ -2861,7 +2861,7 @@ Vector<uint8_t> String::sha1_buffer() const {
|
|||||||
CryptoCore::sha1((unsigned char *)cs.ptr(), cs.length(), hash);
|
CryptoCore::sha1((unsigned char *)cs.ptr(), cs.length(), hash);
|
||||||
|
|
||||||
Vector<uint8_t> ret;
|
Vector<uint8_t> ret;
|
||||||
ret.resize(20);
|
ret.resize_uninitialized(20);
|
||||||
uint8_t *ret_ptrw = ret.ptrw();
|
uint8_t *ret_ptrw = ret.ptrw();
|
||||||
for (int i = 0; i < 20; i++) {
|
for (int i = 0; i < 20; i++) {
|
||||||
ret_ptrw[i] = hash[i];
|
ret_ptrw[i] = hash[i];
|
||||||
@ -2876,7 +2876,7 @@ Vector<uint8_t> String::sha256_buffer() const {
|
|||||||
CryptoCore::sha256((unsigned char *)cs.ptr(), cs.length(), hash);
|
CryptoCore::sha256((unsigned char *)cs.ptr(), cs.length(), hash);
|
||||||
|
|
||||||
Vector<uint8_t> ret;
|
Vector<uint8_t> ret;
|
||||||
ret.resize(32);
|
ret.resize_uninitialized(32);
|
||||||
uint8_t *ret_ptrw = ret.ptrw();
|
uint8_t *ret_ptrw = ret.ptrw();
|
||||||
for (int i = 0; i < 32; i++) {
|
for (int i = 0; i < 32; i++) {
|
||||||
ret_ptrw[i] = hash[i];
|
ret_ptrw[i] = hash[i];
|
||||||
@ -2894,7 +2894,7 @@ String String::insert(int p_at_pos, const String &p_string) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String ret;
|
String ret;
|
||||||
ret.resize(length() + p_string.length() + 1);
|
ret.resize_uninitialized(length() + p_string.length() + 1);
|
||||||
char32_t *ret_ptrw = ret.ptrw();
|
char32_t *ret_ptrw = ret.ptrw();
|
||||||
const char32_t *this_ptr = ptr();
|
const char32_t *this_ptr = ptr();
|
||||||
|
|
||||||
@ -2958,7 +2958,7 @@ String String::remove_char(char32_t p_char) const {
|
|||||||
|
|
||||||
// If we found at least one occurrence of `char`, create new string, allocating enough space for the current length minus one.
|
// If we found at least one occurrence of `char`, create new string, allocating enough space for the current length minus one.
|
||||||
String new_string;
|
String new_string;
|
||||||
new_string.resize(len);
|
new_string.resize_uninitialized(len);
|
||||||
char32_t *new_ptr = new_string.ptrw();
|
char32_t *new_ptr = new_string.ptrw();
|
||||||
|
|
||||||
// Copy part of input before `char`.
|
// Copy part of input before `char`.
|
||||||
@ -2978,7 +2978,7 @@ String String::remove_char(char32_t p_char) const {
|
|||||||
new_ptr[new_size] = _null;
|
new_ptr[new_size] = _null;
|
||||||
|
|
||||||
// Shrink new string to fit.
|
// Shrink new string to fit.
|
||||||
new_string.resize(new_size + 1);
|
new_string.resize_uninitialized(new_size + 1);
|
||||||
|
|
||||||
return new_string;
|
return new_string;
|
||||||
}
|
}
|
||||||
@ -3013,7 +3013,7 @@ static String _remove_chars_common(const String &p_this, const T *p_chars, int p
|
|||||||
|
|
||||||
// If we found at least one occurrence of `chars`, create new string, allocating enough space for the current length minus one.
|
// If we found at least one occurrence of `chars`, create new string, allocating enough space for the current length minus one.
|
||||||
String new_string;
|
String new_string;
|
||||||
new_string.resize(len);
|
new_string.resize_uninitialized(len);
|
||||||
char32_t *new_ptr = new_string.ptrw();
|
char32_t *new_ptr = new_string.ptrw();
|
||||||
|
|
||||||
// Copy part of input before `char`.
|
// Copy part of input before `char`.
|
||||||
@ -3033,7 +3033,7 @@ static String _remove_chars_common(const String &p_this, const T *p_chars, int p
|
|||||||
new_ptr[new_size] = 0;
|
new_ptr[new_size] = 0;
|
||||||
|
|
||||||
// Shrink new string to fit.
|
// Shrink new string to fit.
|
||||||
new_string.resize(new_size + 1);
|
new_string.resize_uninitialized(new_size + 1);
|
||||||
|
|
||||||
return new_string;
|
return new_string;
|
||||||
}
|
}
|
||||||
@ -3747,7 +3747,7 @@ Vector<String> String::bigrams() const {
|
|||||||
if (n_pairs <= 0) {
|
if (n_pairs <= 0) {
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
b.resize(n_pairs);
|
b.resize_initialized(n_pairs);
|
||||||
String *b_ptrw = b.ptrw();
|
String *b_ptrw = b.ptrw();
|
||||||
for (int i = 0; i < n_pairs; i++) {
|
for (int i = 0; i < n_pairs; i++) {
|
||||||
b_ptrw[i] = substr(i, 2);
|
b_ptrw[i] = substr(i, 2);
|
||||||
@ -3893,7 +3893,7 @@ static String _replace_common(const String &p_this, const String &p_key, const S
|
|||||||
const int with_length = p_with.length();
|
const int with_length = p_with.length();
|
||||||
const int old_length = p_this.length();
|
const int old_length = p_this.length();
|
||||||
|
|
||||||
new_string.resize(old_length + int(found.size()) * (with_length - key_length) + 1);
|
new_string.resize_uninitialized(old_length + int(found.size()) * (with_length - key_length) + 1);
|
||||||
|
|
||||||
char32_t *new_ptrw = new_string.ptrw();
|
char32_t *new_ptrw = new_string.ptrw();
|
||||||
const char32_t *old_ptr = p_this.ptr();
|
const char32_t *old_ptr = p_this.ptr();
|
||||||
@ -3952,7 +3952,7 @@ static String _replace_common(const String &p_this, char const *p_key, char cons
|
|||||||
const int with_length = with_string.length();
|
const int with_length = with_string.length();
|
||||||
const int old_length = p_this.length();
|
const int old_length = p_this.length();
|
||||||
|
|
||||||
new_string.resize(old_length + int(found.size()) * (with_length - key_length) + 1);
|
new_string.resize_uninitialized(old_length + int(found.size()) * (with_length - key_length) + 1);
|
||||||
|
|
||||||
char32_t *new_ptrw = new_string.ptrw();
|
char32_t *new_ptrw = new_string.ptrw();
|
||||||
const char32_t *old_ptr = p_this.ptr();
|
const char32_t *old_ptr = p_this.ptr();
|
||||||
@ -3998,7 +3998,7 @@ String String::replace_first(const String &p_key, const String &p_with) const {
|
|||||||
const int with_length = p_with.length();
|
const int with_length = p_with.length();
|
||||||
|
|
||||||
String new_string;
|
String new_string;
|
||||||
new_string.resize(old_length + (with_length - key_length) + 1);
|
new_string.resize_uninitialized(old_length + (with_length - key_length) + 1);
|
||||||
|
|
||||||
char32_t *new_ptrw = new_string.ptrw();
|
char32_t *new_ptrw = new_string.ptrw();
|
||||||
const char32_t *old_ptr = ptr();
|
const char32_t *old_ptr = ptr();
|
||||||
@ -4036,7 +4036,7 @@ String String::replace_first(const char *p_key, const char *p_with) const {
|
|||||||
const int with_length = strlen(p_with);
|
const int with_length = strlen(p_with);
|
||||||
|
|
||||||
String new_string;
|
String new_string;
|
||||||
new_string.resize(old_length + (with_length - key_length) + 1);
|
new_string.resize_uninitialized(old_length + (with_length - key_length) + 1);
|
||||||
|
|
||||||
char32_t *new_ptrw = new_string.ptrw();
|
char32_t *new_ptrw = new_string.ptrw();
|
||||||
const char32_t *old_ptr = ptr();
|
const char32_t *old_ptr = ptr();
|
||||||
@ -4091,7 +4091,7 @@ String String::replace_char(char32_t p_key, char32_t p_with) const {
|
|||||||
|
|
||||||
// If we found at least one occurrence of `key`, create new string.
|
// If we found at least one occurrence of `key`, create new string.
|
||||||
String new_string;
|
String new_string;
|
||||||
new_string.resize(len + 1);
|
new_string.resize_uninitialized(len + 1);
|
||||||
char32_t *new_ptr = new_string.ptrw();
|
char32_t *new_ptr = new_string.ptrw();
|
||||||
|
|
||||||
// Copy part of input before `key`.
|
// Copy part of input before `key`.
|
||||||
@ -4144,7 +4144,7 @@ static String _replace_chars_common(const String &p_this, const T *p_keys, int p
|
|||||||
|
|
||||||
// If we found at least one occurrence of `keys`, create new string.
|
// If we found at least one occurrence of `keys`, create new string.
|
||||||
String new_string;
|
String new_string;
|
||||||
new_string.resize(len + 1);
|
new_string.resize_uninitialized(len + 1);
|
||||||
char32_t *new_ptr = new_string.ptrw();
|
char32_t *new_ptr = new_string.ptrw();
|
||||||
|
|
||||||
// Copy part of input before `key`.
|
// Copy part of input before `key`.
|
||||||
@ -4196,7 +4196,7 @@ String String::repeat(int p_count) const {
|
|||||||
|
|
||||||
int len = length();
|
int len = length();
|
||||||
String new_string = *this;
|
String new_string = *this;
|
||||||
new_string.resize(p_count * len + 1);
|
new_string.resize_uninitialized(p_count * len + 1);
|
||||||
|
|
||||||
char32_t *dst = new_string.ptrw();
|
char32_t *dst = new_string.ptrw();
|
||||||
int offset = 1;
|
int offset = 1;
|
||||||
@ -4216,7 +4216,7 @@ String String::reverse() const {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
String new_string;
|
String new_string;
|
||||||
new_string.resize(len + 1);
|
new_string.resize_uninitialized(len + 1);
|
||||||
|
|
||||||
const char32_t *src = ptr();
|
const char32_t *src = ptr();
|
||||||
char32_t *dst = new_string.ptrw();
|
char32_t *dst = new_string.ptrw();
|
||||||
@ -4909,7 +4909,7 @@ String String::xml_unescape() const {
|
|||||||
if (len == 0) {
|
if (len == 0) {
|
||||||
return String();
|
return String();
|
||||||
}
|
}
|
||||||
str.resize(len + 1);
|
str.resize_uninitialized(len + 1);
|
||||||
char32_t *str_ptrw = str.ptrw();
|
char32_t *str_ptrw = str.ptrw();
|
||||||
_xml_unescape(get_data(), l, str_ptrw);
|
_xml_unescape(get_data(), l, str_ptrw);
|
||||||
str_ptrw[len] = 0;
|
str_ptrw[len] = 0;
|
||||||
@ -5880,7 +5880,7 @@ Vector<uint8_t> String::to_ascii_buffer() const {
|
|||||||
|
|
||||||
Vector<uint8_t> retval;
|
Vector<uint8_t> retval;
|
||||||
size_t len = charstr.length();
|
size_t len = charstr.length();
|
||||||
retval.resize(len);
|
retval.resize_uninitialized(len);
|
||||||
uint8_t *w = retval.ptrw();
|
uint8_t *w = retval.ptrw();
|
||||||
memcpy(w, charstr.ptr(), len);
|
memcpy(w, charstr.ptr(), len);
|
||||||
|
|
||||||
@ -5896,7 +5896,7 @@ Vector<uint8_t> String::to_utf8_buffer() const {
|
|||||||
|
|
||||||
Vector<uint8_t> retval;
|
Vector<uint8_t> retval;
|
||||||
size_t len = charstr.length();
|
size_t len = charstr.length();
|
||||||
retval.resize(len);
|
retval.resize_uninitialized(len);
|
||||||
uint8_t *w = retval.ptrw();
|
uint8_t *w = retval.ptrw();
|
||||||
memcpy(w, charstr.ptr(), len);
|
memcpy(w, charstr.ptr(), len);
|
||||||
|
|
||||||
@ -5912,7 +5912,7 @@ Vector<uint8_t> String::to_utf16_buffer() const {
|
|||||||
|
|
||||||
Vector<uint8_t> retval;
|
Vector<uint8_t> retval;
|
||||||
size_t len = charstr.length() * sizeof(char16_t);
|
size_t len = charstr.length() * sizeof(char16_t);
|
||||||
retval.resize(len);
|
retval.resize_uninitialized(len);
|
||||||
uint8_t *w = retval.ptrw();
|
uint8_t *w = retval.ptrw();
|
||||||
memcpy(w, (const void *)charstr.ptr(), len);
|
memcpy(w, (const void *)charstr.ptr(), len);
|
||||||
|
|
||||||
@ -5927,7 +5927,7 @@ Vector<uint8_t> String::to_utf32_buffer() const {
|
|||||||
|
|
||||||
Vector<uint8_t> retval;
|
Vector<uint8_t> retval;
|
||||||
size_t len = s->length() * sizeof(char32_t);
|
size_t len = s->length() * sizeof(char32_t);
|
||||||
retval.resize(len);
|
retval.resize_uninitialized(len);
|
||||||
uint8_t *w = retval.ptrw();
|
uint8_t *w = retval.ptrw();
|
||||||
memcpy(w, (const void *)s->ptr(), len);
|
memcpy(w, (const void *)s->ptr(), len);
|
||||||
|
|
||||||
|
@ -186,7 +186,9 @@ public:
|
|||||||
_FORCE_INLINE_ operator Span<T>() const { return Span(ptr(), length()); }
|
_FORCE_INLINE_ operator Span<T>() const { return Span(ptr(), length()); }
|
||||||
_FORCE_INLINE_ Span<T> span() const { return Span(ptr(), length()); }
|
_FORCE_INLINE_ Span<T> span() const { return Span(ptr(), length()); }
|
||||||
|
|
||||||
_FORCE_INLINE_ Error resize(int p_size) { return _cowdata.template resize<false>(p_size); }
|
/// Resizes the string. The given size must include the null terminator.
|
||||||
|
/// New characters are not initialized, and should be set by the caller.
|
||||||
|
_FORCE_INLINE_ Error resize_uninitialized(int64_t p_size) { return _cowdata.template resize<false>(p_size); }
|
||||||
|
|
||||||
_FORCE_INLINE_ T get(int p_index) const { return _cowdata.get(p_index); }
|
_FORCE_INLINE_ T get(int p_index) const { return _cowdata.get(p_index); }
|
||||||
_FORCE_INLINE_ void set(int p_index, const T &p_elem) { _cowdata.set(p_index, p_elem); }
|
_FORCE_INLINE_ void set(int p_index, const T &p_elem) { _cowdata.set(p_index, p_elem); }
|
||||||
@ -221,7 +223,7 @@ public:
|
|||||||
}
|
}
|
||||||
_FORCE_INLINE_ CharStringT<T> &operator+=(T p_char) {
|
_FORCE_INLINE_ CharStringT<T> &operator+=(T p_char) {
|
||||||
const int lhs_len = length();
|
const int lhs_len = length();
|
||||||
resize(lhs_len + 2);
|
resize_uninitialized(lhs_len + 2);
|
||||||
|
|
||||||
T *dst = ptrw();
|
T *dst = ptrw();
|
||||||
dst[lhs_len] = p_char;
|
dst[lhs_len] = p_char;
|
||||||
@ -233,17 +235,17 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
void copy_from(const T *p_cstr) {
|
void copy_from(const T *p_cstr) {
|
||||||
if (!p_cstr) {
|
if (!p_cstr) {
|
||||||
resize(0);
|
resize_uninitialized(0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t len = strlen(p_cstr);
|
size_t len = strlen(p_cstr);
|
||||||
if (len == 0) {
|
if (len == 0) {
|
||||||
resize(0);
|
resize_uninitialized(0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Error err = resize(++len); // include terminating null char.
|
Error err = resize_uninitialized(++len); // include terminating null char.
|
||||||
|
|
||||||
ERR_FAIL_COND_MSG(err != OK, "Failed to copy C-string.");
|
ERR_FAIL_COND_MSG(err != OK, "Failed to copy C-string.");
|
||||||
|
|
||||||
@ -320,11 +322,14 @@ public:
|
|||||||
|
|
||||||
void remove_at(int p_index) { _cowdata.remove_at(p_index); }
|
void remove_at(int p_index) { _cowdata.remove_at(p_index); }
|
||||||
|
|
||||||
_FORCE_INLINE_ void clear() { resize(0); }
|
_FORCE_INLINE_ void clear() { resize_uninitialized(0); }
|
||||||
|
|
||||||
_FORCE_INLINE_ char32_t get(int p_index) const { return _cowdata.get(p_index); }
|
_FORCE_INLINE_ char32_t get(int p_index) const { return _cowdata.get(p_index); }
|
||||||
_FORCE_INLINE_ void set(int p_index, const char32_t &p_elem) { _cowdata.set(p_index, p_elem); }
|
_FORCE_INLINE_ void set(int p_index, const char32_t &p_elem) { _cowdata.set(p_index, p_elem); }
|
||||||
Error resize(int p_size) { return _cowdata.resize<false>(p_size); }
|
|
||||||
|
/// Resizes the string. The given size must include the null terminator.
|
||||||
|
/// New characters are not initialized, and should be set by the caller.
|
||||||
|
Error resize_uninitialized(int64_t p_size) { return _cowdata.resize<false>(p_size); }
|
||||||
|
|
||||||
_FORCE_INLINE_ const char32_t &operator[](int p_index) const {
|
_FORCE_INLINE_ const char32_t &operator[](int p_index) const {
|
||||||
if (unlikely(p_index == _cowdata.size())) {
|
if (unlikely(p_index == _cowdata.size())) {
|
||||||
|
@ -51,8 +51,17 @@ public:
|
|||||||
std::is_same<T, wchar_t>>;
|
std::is_same<T, wchar_t>>;
|
||||||
|
|
||||||
_FORCE_INLINE_ constexpr Span() = default;
|
_FORCE_INLINE_ constexpr Span() = default;
|
||||||
_FORCE_INLINE_ constexpr Span(const T *p_ptr, uint64_t p_len) :
|
|
||||||
_ptr(p_ptr), _len(p_len) {}
|
_FORCE_INLINE_ Span(const T *p_ptr, uint64_t p_len) :
|
||||||
|
_ptr(p_ptr), _len(p_len) {
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
// TODO In c++20, make this check run only in non-consteval, and make this constructor constexpr.
|
||||||
|
if (_ptr == nullptr && _len > 0) {
|
||||||
|
ERR_PRINT("Internal bug, please report: Span was created from nullptr with size > 0. Recovering by using size = 0.");
|
||||||
|
_len = 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
// Allows creating Span directly from C arrays and string literals.
|
// Allows creating Span directly from C arrays and string literals.
|
||||||
template <size_t N>
|
template <size_t N>
|
||||||
|
@ -1642,7 +1642,7 @@ String Variant::stringify(int recursion_count) const {
|
|||||||
case STRING_NAME:
|
case STRING_NAME:
|
||||||
return operator StringName();
|
return operator StringName();
|
||||||
case NODE_PATH:
|
case NODE_PATH:
|
||||||
return operator NodePath();
|
return String(operator NodePath());
|
||||||
case COLOR:
|
case COLOR:
|
||||||
return String(operator Color());
|
return String(operator Color());
|
||||||
case DICTIONARY: {
|
case DICTIONARY: {
|
||||||
@ -3501,9 +3501,9 @@ String Variant::get_call_error_text(Object *p_base, const StringName &p_method,
|
|||||||
err_text = "Cannot convert argument " + itos(errorarg + 1) + " from [missing argptr, type unknown] to " + Variant::get_type_name(Variant::Type(ce.expected));
|
err_text = "Cannot convert argument " + itos(errorarg + 1) + " from [missing argptr, type unknown] to " + Variant::get_type_name(Variant::Type(ce.expected));
|
||||||
}
|
}
|
||||||
} else if (ce.error == Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS) {
|
} else if (ce.error == Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS) {
|
||||||
err_text = "Method expected " + itos(ce.expected) + " arguments, but called with " + itos(p_argcount);
|
err_text = "Method expected " + itos(ce.expected) + " argument(s), but called with " + itos(p_argcount);
|
||||||
} else if (ce.error == Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS) {
|
} else if (ce.error == Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS) {
|
||||||
err_text = "Method expected " + itos(ce.expected) + " arguments, but called with " + itos(p_argcount);
|
err_text = "Method expected " + itos(ce.expected) + " argument(s), but called with " + itos(p_argcount);
|
||||||
} else if (ce.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) {
|
} else if (ce.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) {
|
||||||
err_text = "Method not found";
|
err_text = "Method not found";
|
||||||
} else if (ce.error == Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL) {
|
} else if (ce.error == Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL) {
|
||||||
|
@ -685,7 +685,7 @@ struct _VariantCall {
|
|||||||
if (p_instance->size() > 0) {
|
if (p_instance->size() > 0) {
|
||||||
const uint8_t *r = p_instance->ptr();
|
const uint8_t *r = p_instance->ptr();
|
||||||
CharString cs;
|
CharString cs;
|
||||||
cs.resize(p_instance->size() + 1);
|
cs.resize_uninitialized(p_instance->size() + 1);
|
||||||
memcpy(cs.ptrw(), r, p_instance->size());
|
memcpy(cs.ptrw(), r, p_instance->size());
|
||||||
cs[(int)p_instance->size()] = 0;
|
cs[(int)p_instance->size()] = 0;
|
||||||
|
|
||||||
@ -1012,6 +1012,62 @@ struct _VariantCall {
|
|||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PackedVector2Array func_PackedByteArray_decode_vector2_array(PackedByteArray *p_instance) {
|
||||||
|
uint64_t size = p_instance->size();
|
||||||
|
PackedVector2Array dest;
|
||||||
|
if (size == 0) {
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
ERR_FAIL_COND_V_MSG(size % sizeof(Vector2), dest, "PackedByteArray size must be a multiple of " + itos(sizeof(Vector2)) + " (size of Vector2) to convert to PackedVector2Array.");
|
||||||
|
const uint8_t *r = p_instance->ptr();
|
||||||
|
dest.resize(size / sizeof(Vector2));
|
||||||
|
ERR_FAIL_COND_V(dest.is_empty(), dest); // Avoid UB in case resize failed.
|
||||||
|
memcpy(dest.ptrw(), r, dest.size() * sizeof(Vector2));
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PackedVector3Array func_PackedByteArray_decode_vector3_array(PackedByteArray *p_instance) {
|
||||||
|
uint64_t size = p_instance->size();
|
||||||
|
PackedVector3Array dest;
|
||||||
|
if (size == 0) {
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
ERR_FAIL_COND_V_MSG(size % sizeof(Vector3), dest, "PackedByteArray size must be a multiple of " + itos(sizeof(Vector3)) + " (size of Vector3) to convert to PackedVector3Array.");
|
||||||
|
const uint8_t *r = p_instance->ptr();
|
||||||
|
dest.resize(size / sizeof(Vector3));
|
||||||
|
ERR_FAIL_COND_V(dest.is_empty(), dest); // Avoid UB in case resize failed.
|
||||||
|
memcpy(dest.ptrw(), r, dest.size() * sizeof(Vector3));
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PackedVector4Array func_PackedByteArray_decode_vector4_array(PackedByteArray *p_instance) {
|
||||||
|
uint64_t size = p_instance->size();
|
||||||
|
PackedVector4Array dest;
|
||||||
|
if (size == 0) {
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
ERR_FAIL_COND_V_MSG(size % sizeof(Vector4), dest, "PackedByteArray size must be a multiple of " + itos(sizeof(Vector4)) + " (size of Vector4) to convert to PackedVector4Array.");
|
||||||
|
const uint8_t *r = p_instance->ptr();
|
||||||
|
dest.resize(size / sizeof(Vector4));
|
||||||
|
ERR_FAIL_COND_V(dest.is_empty(), dest); // Avoid UB in case resize failed.
|
||||||
|
memcpy(dest.ptrw(), r, dest.size() * sizeof(Vector4));
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PackedColorArray func_PackedByteArray_decode_color_array(PackedByteArray *p_instance) {
|
||||||
|
uint64_t size = p_instance->size();
|
||||||
|
PackedColorArray dest;
|
||||||
|
if (size == 0) {
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
ERR_FAIL_COND_V_MSG(size % sizeof(Color), dest, "PackedByteArray size must be a multiple of " + itos(sizeof(Color)) + " (size of Color variant) to convert to PackedColorArray.");
|
||||||
|
const uint8_t *r = p_instance->ptr();
|
||||||
|
dest.resize(size / sizeof(Color));
|
||||||
|
ERR_FAIL_COND_V(dest.is_empty(), dest); // Avoid UB in case resize failed.
|
||||||
|
memcpy(dest.ptrw(), r, dest.size() * sizeof(Color));
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
static void func_PackedByteArray_encode_u8(PackedByteArray *p_instance, int64_t p_offset, int64_t p_value) {
|
static void func_PackedByteArray_encode_u8(PackedByteArray *p_instance, int64_t p_offset, int64_t p_value) {
|
||||||
uint64_t size = p_instance->size();
|
uint64_t size = p_instance->size();
|
||||||
ERR_FAIL_COND(p_offset < 0 || p_offset > int64_t(size) - 1);
|
ERR_FAIL_COND(p_offset < 0 || p_offset > int64_t(size) - 1);
|
||||||
@ -2560,6 +2616,10 @@ static void _register_variant_builtin_methods_array() {
|
|||||||
bind_function(PackedByteArray, to_int64_array, _VariantCall::func_PackedByteArray_decode_s64_array, sarray(), varray());
|
bind_function(PackedByteArray, to_int64_array, _VariantCall::func_PackedByteArray_decode_s64_array, sarray(), varray());
|
||||||
bind_function(PackedByteArray, to_float32_array, _VariantCall::func_PackedByteArray_decode_float_array, sarray(), varray());
|
bind_function(PackedByteArray, to_float32_array, _VariantCall::func_PackedByteArray_decode_float_array, sarray(), varray());
|
||||||
bind_function(PackedByteArray, to_float64_array, _VariantCall::func_PackedByteArray_decode_double_array, sarray(), varray());
|
bind_function(PackedByteArray, to_float64_array, _VariantCall::func_PackedByteArray_decode_double_array, sarray(), varray());
|
||||||
|
bind_function(PackedByteArray, to_vector2_array, _VariantCall::func_PackedByteArray_decode_vector2_array, sarray(), varray());
|
||||||
|
bind_function(PackedByteArray, to_vector3_array, _VariantCall::func_PackedByteArray_decode_vector3_array, sarray(), varray());
|
||||||
|
bind_function(PackedByteArray, to_vector4_array, _VariantCall::func_PackedByteArray_decode_vector4_array, sarray(), varray());
|
||||||
|
bind_function(PackedByteArray, to_color_array, _VariantCall::func_PackedByteArray_decode_color_array, sarray(), varray());
|
||||||
|
|
||||||
bind_functionnc(PackedByteArray, bswap16, _VariantCall::func_PackedByteArray_bswap16, sarray("offset", "count"), varray(0, -1));
|
bind_functionnc(PackedByteArray, bswap16, _VariantCall::func_PackedByteArray_bswap16, sarray("offset", "count"), varray(0, -1));
|
||||||
bind_functionnc(PackedByteArray, bswap32, _VariantCall::func_PackedByteArray_bswap32, sarray("offset", "count"), varray(0, -1));
|
bind_functionnc(PackedByteArray, bswap32, _VariantCall::func_PackedByteArray_bswap32, sarray("offset", "count"), varray(0, -1));
|
||||||
|
@ -91,7 +91,7 @@
|
|||||||
<method name="get_length" qualifiers="const">
|
<method name="get_length" qualifiers="const">
|
||||||
<return type="float" />
|
<return type="float" />
|
||||||
<description>
|
<description>
|
||||||
Returns the length of the audio stream in seconds.
|
Returns the length of the audio stream in seconds. If this stream is an [AudioStreamRandomizer], returns the length of the last played stream. If this stream has an indefinite length (such as for [AudioStreamGenerator] and [AudioStreamMicrophone]), returns [code]0.0[/code].
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="instantiate_playback">
|
<method name="instantiate_playback">
|
||||||
|
@ -644,7 +644,8 @@
|
|||||||
The filtering mode used to render this [CanvasItem]'s texture(s).
|
The filtering mode used to render this [CanvasItem]'s texture(s).
|
||||||
</member>
|
</member>
|
||||||
<member name="texture_repeat" type="int" setter="set_texture_repeat" getter="get_texture_repeat" enum="CanvasItem.TextureRepeat" default="0">
|
<member name="texture_repeat" type="int" setter="set_texture_repeat" getter="get_texture_repeat" enum="CanvasItem.TextureRepeat" default="0">
|
||||||
The repeating mode used to render this [CanvasItem]'s texture(s).
|
The repeating mode used to render this [CanvasItem]'s texture(s). It affects what happens when the texture is sampled outside its extents, for example by setting a [member Sprite2D.region_rect] that is larger than the texture or assigning [Polygon2D] UV points outside the texture.
|
||||||
|
[b]Note:[/b] [TextureRect] is not affected by [member texture_repeat], as it uses its own texture repeating implementation.
|
||||||
</member>
|
</member>
|
||||||
<member name="top_level" type="bool" setter="set_as_top_level" getter="is_set_as_top_level" default="false">
|
<member name="top_level" type="bool" setter="set_as_top_level" getter="is_set_as_top_level" default="false">
|
||||||
If [code]true[/code], this [CanvasItem] will [i]not[/i] inherit its transform from parent [CanvasItem]s. Its draw order will also be changed to make it draw on top of other [CanvasItem]s that do not have [member top_level] set to [code]true[/code]. The [CanvasItem] will effectively act as if it was placed as a child of a bare [Node].
|
If [code]true[/code], this [CanvasItem] will [i]not[/i] inherit its transform from parent [CanvasItem]s. Its draw order will also be changed to make it draw on top of other [CanvasItem]s that do not have [member top_level] set to [code]true[/code]. The [CanvasItem] will effectively act as if it was placed as a child of a bare [Node].
|
||||||
@ -754,7 +755,7 @@
|
|||||||
The [CanvasItem] will inherit the filter from its parent.
|
The [CanvasItem] will inherit the filter from its parent.
|
||||||
</constant>
|
</constant>
|
||||||
<constant name="TEXTURE_REPEAT_DISABLED" value="1" enum="TextureRepeat">
|
<constant name="TEXTURE_REPEAT_DISABLED" value="1" enum="TextureRepeat">
|
||||||
The texture does not repeat.
|
The texture does not repeat. Sampling the texture outside its extents will result in "stretching" of the edge pixels. You can avoid this by ensuring a 1-pixel fully transparent border on each side of the texture.
|
||||||
</constant>
|
</constant>
|
||||||
<constant name="TEXTURE_REPEAT_ENABLED" value="2" enum="TextureRepeat">
|
<constant name="TEXTURE_REPEAT_ENABLED" value="2" enum="TextureRepeat">
|
||||||
The texture repeats when exceeding the texture's size.
|
The texture repeats when exceeding the texture's size.
|
||||||
|
@ -2543,7 +2543,7 @@
|
|||||||
Display server supports [url=https://en.wikipedia.org/wiki/Input_method]Input Method Editor[/url], which is commonly used for inputting Chinese/Japanese/Korean text. This is handled by the operating system, rather than by Godot. [b]Windows, macOS, Linux (X11)[/b]
|
Display server supports [url=https://en.wikipedia.org/wiki/Input_method]Input Method Editor[/url], which is commonly used for inputting Chinese/Japanese/Korean text. This is handled by the operating system, rather than by Godot. [b]Windows, macOS, Linux (X11)[/b]
|
||||||
</constant>
|
</constant>
|
||||||
<constant name="FEATURE_WINDOW_TRANSPARENCY" value="11" enum="Feature">
|
<constant name="FEATURE_WINDOW_TRANSPARENCY" value="11" enum="Feature">
|
||||||
Display server supports windows can use per-pixel transparency to make windows behind them partially or fully visible. [b]Windows, macOS, Linux (X11/Wayland)[/b]
|
Display server supports windows can use per-pixel transparency to make windows behind them partially or fully visible. [b]Windows, macOS, Linux (X11/Wayland), Android[/b]
|
||||||
</constant>
|
</constant>
|
||||||
<constant name="FEATURE_HIDPI" value="12" enum="Feature">
|
<constant name="FEATURE_HIDPI" value="12" enum="Feature">
|
||||||
Display server supports querying the operating system's display scale factor. This allows automatically detecting the hiDPI display [i]reliably[/i], instead of guessing based on the screen resolution and the display's reported DPI (which might be unreliable due to broken monitor EDID). [b]Windows, Linux (Wayland), macOS[/b]
|
Display server supports querying the operating system's display scale factor. This allows automatically detecting the hiDPI display [i]reliably[/i], instead of guessing based on the screen resolution and the display's reported DPI (which might be unreliable due to broken monitor EDID). [b]Windows, Linux (Wayland), macOS[/b]
|
||||||
@ -3073,7 +3073,7 @@
|
|||||||
<constant name="WINDOW_FLAG_TRANSPARENT" value="3" enum="WindowFlags">
|
<constant name="WINDOW_FLAG_TRANSPARENT" value="3" enum="WindowFlags">
|
||||||
The window background can be transparent.
|
The window background can be transparent.
|
||||||
[b]Note:[/b] This flag has no effect if [method is_window_transparency_available] returns [code]false[/code].
|
[b]Note:[/b] This flag has no effect if [method is_window_transparency_available] returns [code]false[/code].
|
||||||
[b]Note:[/b] Transparency support is implemented on Linux (X11/Wayland), macOS, and Windows, but availability might vary depending on GPU driver, display manager, and compositor capabilities.
|
[b]Note:[/b] Transparency support is implemented on Android, Linux (X11/Wayland), macOS, and Windows, but availability might vary depending on GPU driver, display manager, and compositor capabilities.
|
||||||
</constant>
|
</constant>
|
||||||
<constant name="WINDOW_FLAG_NO_FOCUS" value="4" enum="WindowFlags">
|
<constant name="WINDOW_FLAG_NO_FOCUS" value="4" enum="WindowFlags">
|
||||||
The window can't be focused. No-focus window will ignore all input, except mouse clicks.
|
The window can't be focused. No-focus window will ignore all input, except mouse clicks.
|
||||||
|
@ -905,6 +905,13 @@
|
|||||||
Returns the travel cost of this [param region].
|
Returns the travel cost of this [param region].
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
|
<method name="region_get_use_async_iterations" qualifiers="const">
|
||||||
|
<return type="bool" />
|
||||||
|
<param index="0" name="region" type="RID" />
|
||||||
|
<description>
|
||||||
|
Returns [code]true[/code] if the [param region] uses an async synchronization process that runs on a background thread.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
<method name="region_get_use_edge_connections" qualifiers="const">
|
<method name="region_get_use_edge_connections" qualifiers="const">
|
||||||
<return type="bool" />
|
<return type="bool" />
|
||||||
<param index="0" name="region" type="RID" />
|
<param index="0" name="region" type="RID" />
|
||||||
@ -986,6 +993,14 @@
|
|||||||
Sets the [param travel_cost] for this [param region].
|
Sets the [param travel_cost] for this [param region].
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
|
<method name="region_set_use_async_iterations">
|
||||||
|
<return type="void" />
|
||||||
|
<param index="0" name="region" type="RID" />
|
||||||
|
<param index="1" name="enabled" type="bool" />
|
||||||
|
<description>
|
||||||
|
If [param enabled] is [code]true[/code] the [param region] uses an async synchronization process that runs on a background thread.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
<method name="region_set_use_edge_connections">
|
<method name="region_set_use_edge_connections">
|
||||||
<return type="void" />
|
<return type="void" />
|
||||||
<param index="0" name="region" type="RID" />
|
<param index="0" name="region" type="RID" />
|
||||||
|
@ -497,6 +497,13 @@
|
|||||||
Sorts the elements of the array in ascending order.
|
Sorts the elements of the array in ascending order.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
|
<method name="to_color_array" qualifiers="const">
|
||||||
|
<return type="PackedColorArray" />
|
||||||
|
<description>
|
||||||
|
Returns a copy of the data converted to a [PackedColorArray], where each block of 16 bytes has been converted to a [Color] variant.
|
||||||
|
[b]Note:[/b] The size of the input array must be a multiple of 16 (size of four 32-bit float variables). The size of the new array will be [code]byte_array.size() / 16[/code]. If the original data can't be converted to [Color] variants, the resulting data is undefined.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
<method name="to_float32_array" qualifiers="const">
|
<method name="to_float32_array" qualifiers="const">
|
||||||
<return type="PackedFloat32Array" />
|
<return type="PackedFloat32Array" />
|
||||||
<description>
|
<description>
|
||||||
@ -529,6 +536,27 @@
|
|||||||
If the original data can't be converted to signed 64-bit integers, the resulting data is undefined.
|
If the original data can't be converted to signed 64-bit integers, the resulting data is undefined.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
|
<method name="to_vector2_array" qualifiers="const">
|
||||||
|
<return type="PackedVector2Array" />
|
||||||
|
<description>
|
||||||
|
Returns a copy of the data converted to a [PackedVector2Array], where each block of 8 bytes or 16 bytes (32-bit or 64-bit) has been converted to a [Vector2] variant.
|
||||||
|
[b]Note:[/b] The size of the input array must be a multiple of 8 or 16 (depending on the build settings, see [Vector2] for more details). The size of the new array will be [code]byte_array.size() / (8 or 16)[/code]. If the original data can't be converted to [Vector2] variants, the resulting data is undefined.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="to_vector3_array" qualifiers="const">
|
||||||
|
<return type="PackedVector3Array" />
|
||||||
|
<description>
|
||||||
|
Returns a copy of the data converted to a [PackedVector3Array], where each block of 12 or 24 bytes (32-bit or 64-bit) has been converted to a [Vector3] variant.
|
||||||
|
[b]Note:[/b] The size of the input array must be a multiple of 12 or 24 (depending on the build settings, see [Vector3] for more details). The size of the new array will be [code]byte_array.size() / (12 or 24)[/code]. If the original data can't be converted to [Vector3] variants, the resulting data is undefined.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="to_vector4_array" qualifiers="const">
|
||||||
|
<return type="PackedVector4Array" />
|
||||||
|
<description>
|
||||||
|
Returns a copy of the data converted to a [PackedVector4Array], where each block of 16 or 32 bytes (32-bit or 64-bit) has been converted to a [Vector4] variant.
|
||||||
|
[b]Note:[/b] The size of the input array must be a multiple of 16 or 32 (depending on the build settings, see [Vector4] for more details). The size of the new array will be [code]byte_array.size() / (16 or 32)[/code]. If the original data can't be converted to [Vector4] variants, the resulting data is undefined.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
</methods>
|
</methods>
|
||||||
<operators>
|
<operators>
|
||||||
<operator name="operator !=">
|
<operator name="operator !=">
|
||||||
|
@ -854,7 +854,6 @@
|
|||||||
Full screen mode with full multi-window support.
|
Full screen mode with full multi-window support.
|
||||||
Full screen window covers the entire display area of a screen and has no decorations. The display's video mode is not changed.
|
Full screen window covers the entire display area of a screen and has no decorations. The display's video mode is not changed.
|
||||||
[b]On Android:[/b] This enables immersive mode.
|
[b]On Android:[/b] This enables immersive mode.
|
||||||
[b]On Windows:[/b] Multi-window full-screen mode has a 1px border of the [member ProjectSettings.rendering/environment/defaults/default_clear_color] color.
|
|
||||||
[b]On macOS:[/b] A new desktop is used to display the running project.
|
[b]On macOS:[/b] A new desktop is used to display the running project.
|
||||||
[b]Note:[/b] Regardless of the platform, enabling full screen will change the window size to match the monitor's size. Therefore, make sure your project supports [url=$DOCS_URL/tutorials/rendering/multiple_resolutions.html]multiple resolutions[/url] when enabling full screen mode.
|
[b]Note:[/b] Regardless of the platform, enabling full screen will change the window size to match the monitor's size. Therefore, make sure your project supports [url=$DOCS_URL/tutorials/rendering/multiple_resolutions.html]multiple resolutions[/url] when enabling full screen mode.
|
||||||
</constant>
|
</constant>
|
||||||
|
@ -155,7 +155,7 @@ Error DirAccessWindows::change_dir(String p_dir) {
|
|||||||
|
|
||||||
Char16String real_current_dir_name;
|
Char16String real_current_dir_name;
|
||||||
size_t str_len = GetCurrentDirectoryW(0, nullptr);
|
size_t str_len = GetCurrentDirectoryW(0, nullptr);
|
||||||
real_current_dir_name.resize(str_len + 1);
|
real_current_dir_name.resize_uninitialized(str_len + 1);
|
||||||
GetCurrentDirectoryW(real_current_dir_name.size(), (LPWSTR)real_current_dir_name.ptrw());
|
GetCurrentDirectoryW(real_current_dir_name.size(), (LPWSTR)real_current_dir_name.ptrw());
|
||||||
String prev_dir = String::utf16((const char16_t *)real_current_dir_name.get_data());
|
String prev_dir = String::utf16((const char16_t *)real_current_dir_name.get_data());
|
||||||
|
|
||||||
@ -165,7 +165,7 @@ Error DirAccessWindows::change_dir(String p_dir) {
|
|||||||
String base = _get_root_path();
|
String base = _get_root_path();
|
||||||
if (!base.is_empty()) {
|
if (!base.is_empty()) {
|
||||||
str_len = GetCurrentDirectoryW(0, nullptr);
|
str_len = GetCurrentDirectoryW(0, nullptr);
|
||||||
real_current_dir_name.resize(str_len + 1);
|
real_current_dir_name.resize_uninitialized(str_len + 1);
|
||||||
GetCurrentDirectoryW(real_current_dir_name.size(), (LPWSTR)real_current_dir_name.ptrw());
|
GetCurrentDirectoryW(real_current_dir_name.size(), (LPWSTR)real_current_dir_name.ptrw());
|
||||||
String new_dir = String::utf16((const char16_t *)real_current_dir_name.get_data()).trim_prefix(R"(\\?\)").replace_char('\\', '/');
|
String new_dir = String::utf16((const char16_t *)real_current_dir_name.get_data()).trim_prefix(R"(\\?\)").replace_char('\\', '/');
|
||||||
if (!new_dir.begins_with(base)) {
|
if (!new_dir.begins_with(base)) {
|
||||||
@ -175,7 +175,7 @@ Error DirAccessWindows::change_dir(String p_dir) {
|
|||||||
|
|
||||||
if (worked) {
|
if (worked) {
|
||||||
str_len = GetCurrentDirectoryW(0, nullptr);
|
str_len = GetCurrentDirectoryW(0, nullptr);
|
||||||
real_current_dir_name.resize(str_len + 1);
|
real_current_dir_name.resize_uninitialized(str_len + 1);
|
||||||
GetCurrentDirectoryW(real_current_dir_name.size(), (LPWSTR)real_current_dir_name.ptrw());
|
GetCurrentDirectoryW(real_current_dir_name.size(), (LPWSTR)real_current_dir_name.ptrw());
|
||||||
current_dir = String::utf16((const char16_t *)real_current_dir_name.get_data());
|
current_dir = String::utf16((const char16_t *)real_current_dir_name.get_data());
|
||||||
}
|
}
|
||||||
@ -447,7 +447,7 @@ String DirAccessWindows::read_link(String p_file) {
|
|||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
Char16String cs;
|
Char16String cs;
|
||||||
cs.resize(ret + 1);
|
cs.resize_uninitialized(ret + 1);
|
||||||
GetFinalPathNameByHandleW(hfile, (LPWSTR)cs.ptrw(), ret, VOLUME_NAME_DOS | FILE_NAME_NORMALIZED);
|
GetFinalPathNameByHandleW(hfile, (LPWSTR)cs.ptrw(), ret, VOLUME_NAME_DOS | FILE_NAME_NORMALIZED);
|
||||||
CloseHandle(hfile);
|
CloseHandle(hfile);
|
||||||
|
|
||||||
@ -475,7 +475,7 @@ DirAccessWindows::DirAccessWindows() {
|
|||||||
|
|
||||||
Char16String real_current_dir_name;
|
Char16String real_current_dir_name;
|
||||||
size_t str_len = GetCurrentDirectoryW(0, nullptr);
|
size_t str_len = GetCurrentDirectoryW(0, nullptr);
|
||||||
real_current_dir_name.resize(str_len + 1);
|
real_current_dir_name.resize_uninitialized(str_len + 1);
|
||||||
GetCurrentDirectoryW(real_current_dir_name.size(), (LPWSTR)real_current_dir_name.ptrw());
|
GetCurrentDirectoryW(real_current_dir_name.size(), (LPWSTR)real_current_dir_name.ptrw());
|
||||||
current_dir = String::utf16((const char16_t *)real_current_dir_name.get_data());
|
current_dir = String::utf16((const char16_t *)real_current_dir_name.get_data());
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ String FileAccessWindows::fix_path(const String &p_path) const {
|
|||||||
if (r_path.is_relative_path()) {
|
if (r_path.is_relative_path()) {
|
||||||
Char16String current_dir_name;
|
Char16String current_dir_name;
|
||||||
size_t str_len = GetCurrentDirectoryW(0, nullptr);
|
size_t str_len = GetCurrentDirectoryW(0, nullptr);
|
||||||
current_dir_name.resize(str_len + 1);
|
current_dir_name.resize_uninitialized(str_len + 1);
|
||||||
GetCurrentDirectoryW(current_dir_name.size(), (LPWSTR)current_dir_name.ptrw());
|
GetCurrentDirectoryW(current_dir_name.size(), (LPWSTR)current_dir_name.ptrw());
|
||||||
r_path = String::utf16((const char16_t *)current_dir_name.get_data()).trim_prefix(R"(\\?\)").replace_char('\\', '/').path_join(r_path);
|
r_path = String::utf16((const char16_t *)current_dir_name.get_data()).trim_prefix(R"(\\?\)").replace_char('\\', '/').path_join(r_path);
|
||||||
}
|
}
|
||||||
|
@ -328,7 +328,7 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
String base_path = animation->track_get_path(i);
|
String base_path = String(animation->track_get_path(i));
|
||||||
int end = base_path.find_char(':');
|
int end = base_path.find_char(':');
|
||||||
if (end != -1) {
|
if (end != -1) {
|
||||||
base_path = base_path.substr(0, end + 1);
|
base_path = base_path.substr(0, end + 1);
|
||||||
@ -405,7 +405,7 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
|
|||||||
|
|
||||||
int current_track = tracks[i];
|
int current_track = tracks[i];
|
||||||
|
|
||||||
String path = animation->track_get_path(current_track);
|
String path = String(animation->track_get_path(current_track));
|
||||||
path = path.replace_first(base_path, "");
|
path = path.replace_first(base_path, "");
|
||||||
|
|
||||||
Color cc = color;
|
Color cc = color;
|
||||||
@ -763,7 +763,7 @@ bool AnimationBezierTrackEdit::_is_track_displayed(int p_track_index) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (is_filtered) {
|
if (is_filtered) {
|
||||||
String path = animation->track_get_path(p_track_index);
|
String path = String(animation->track_get_path(p_track_index));
|
||||||
if (root && root->has_node(path)) {
|
if (root && root->has_node(path)) {
|
||||||
Node *node = root->get_node(path);
|
Node *node = root->get_node(path);
|
||||||
if (!node) {
|
if (!node) {
|
||||||
@ -899,7 +899,7 @@ void AnimationBezierTrackEdit::set_filtered(bool p_filtered) {
|
|||||||
if (animation.is_null()) {
|
if (animation.is_null()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String base_path = animation->track_get_path(selected_track);
|
String base_path = String(animation->track_get_path(selected_track));
|
||||||
if (is_filtered) {
|
if (is_filtered) {
|
||||||
if (root && root->has_node(base_path)) {
|
if (root && root->has_node(base_path)) {
|
||||||
Node *node = root->get_node(base_path);
|
Node *node = root->get_node(base_path);
|
||||||
@ -909,7 +909,7 @@ void AnimationBezierTrackEdit::set_filtered(bool p_filtered) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
base_path = animation->track_get_path(i);
|
base_path = String(animation->track_get_path(i));
|
||||||
if (root && root->has_node(base_path)) {
|
if (root && root->has_node(base_path)) {
|
||||||
node = root->get_node(base_path);
|
node = root->get_node(base_path);
|
||||||
if (!node) {
|
if (!node) {
|
||||||
|
@ -2191,7 +2191,7 @@ void AnimationTrackEdit::_notification(int p_what) {
|
|||||||
} else {
|
} else {
|
||||||
icon_cache = key_type_icon;
|
icon_cache = key_type_icon;
|
||||||
|
|
||||||
text = anim_path;
|
text = String(anim_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
path_cache = text;
|
path_cache = text;
|
||||||
@ -2822,7 +2822,7 @@ String AnimationTrackEdit::get_tooltip(const Point2 &p_pos) const {
|
|||||||
|
|
||||||
// Don't overlap track keys if they start at 0.
|
// Don't overlap track keys if they start at 0.
|
||||||
if (path_rect.has_point(p_pos + Size2(type_icon->get_width(), 0))) {
|
if (path_rect.has_point(p_pos + Size2(type_icon->get_width(), 0))) {
|
||||||
return animation->track_get_path(track);
|
return String(animation->track_get_path(track));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (update_mode_rect.has_point(p_pos)) {
|
if (update_mode_rect.has_point(p_pos)) {
|
||||||
@ -3230,7 +3230,7 @@ void AnimationTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
|
|||||||
path->connect(SceneStringName(text_submitted), callable_mp(this, &AnimationTrackEdit::_path_submitted));
|
path->connect(SceneStringName(text_submitted), callable_mp(this, &AnimationTrackEdit::_path_submitted));
|
||||||
}
|
}
|
||||||
|
|
||||||
path->set_text(animation->track_get_path(track));
|
path->set_text(String(animation->track_get_path(track)));
|
||||||
const Vector2 theme_ofs = path->get_theme_stylebox(CoreStringName(normal), SNAME("LineEdit"))->get_offset();
|
const Vector2 theme_ofs = path->get_theme_stylebox(CoreStringName(normal), SNAME("LineEdit"))->get_offset();
|
||||||
|
|
||||||
moving_selection_attempt = false;
|
moving_selection_attempt = false;
|
||||||
@ -3462,7 +3462,7 @@ Variant AnimationTrackEdit::get_drag_data(const Point2 &p_point) {
|
|||||||
|
|
||||||
Dictionary drag_data;
|
Dictionary drag_data;
|
||||||
drag_data["type"] = "animation_track";
|
drag_data["type"] = "animation_track";
|
||||||
String base_path = animation->track_get_path(track);
|
String base_path = String(animation->track_get_path(track));
|
||||||
base_path = base_path.get_slicec(':', 0); // Remove sub-path.
|
base_path = base_path.get_slicec(':', 0); // Remove sub-path.
|
||||||
drag_data["group"] = base_path;
|
drag_data["group"] = base_path;
|
||||||
drag_data["index"] = track;
|
drag_data["index"] = track;
|
||||||
@ -3493,7 +3493,7 @@ bool AnimationTrackEdit::can_drop_data(const Point2 &p_point, const Variant &p_d
|
|||||||
|
|
||||||
// Don't allow moving tracks outside their groups.
|
// Don't allow moving tracks outside their groups.
|
||||||
if (get_editor()->is_grouping_tracks()) {
|
if (get_editor()->is_grouping_tracks()) {
|
||||||
String base_path = animation->track_get_path(track);
|
String base_path = String(animation->track_get_path(track));
|
||||||
base_path = base_path.get_slicec(':', 0); // Remove sub-path.
|
base_path = base_path.get_slicec(':', 0); // Remove sub-path.
|
||||||
if (d["group"] != base_path) {
|
if (d["group"] != base_path) {
|
||||||
return false;
|
return false;
|
||||||
@ -3524,7 +3524,7 @@ void AnimationTrackEdit::drop_data(const Point2 &p_point, const Variant &p_data)
|
|||||||
|
|
||||||
// Don't allow moving tracks outside their groups.
|
// Don't allow moving tracks outside their groups.
|
||||||
if (get_editor()->is_grouping_tracks()) {
|
if (get_editor()->is_grouping_tracks()) {
|
||||||
String base_path = animation->track_get_path(track);
|
String base_path = String(animation->track_get_path(track));
|
||||||
base_path = base_path.get_slicec(':', 0); // Remove sub-path.
|
base_path = base_path.get_slicec(':', 0); // Remove sub-path.
|
||||||
if (d["group"] != base_path) {
|
if (d["group"] != base_path) {
|
||||||
return;
|
return;
|
||||||
@ -4370,7 +4370,7 @@ void AnimationTrackEditor::insert_transform_key(Node3D *p_node, const String &p_
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Let's build a node path.
|
// Let's build a node path.
|
||||||
String path = root->get_path_to(p_node, true);
|
String path = String(root->get_path_to(p_node, true));
|
||||||
if (!p_sub.is_empty()) {
|
if (!p_sub.is_empty()) {
|
||||||
path += ":" + p_sub;
|
path += ":" + p_sub;
|
||||||
}
|
}
|
||||||
@ -4410,7 +4410,7 @@ bool AnimationTrackEditor::has_track(Node3D *p_node, const String &p_sub, const
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Let's build a node path.
|
// Let's build a node path.
|
||||||
String path = root->get_path_to(p_node, true);
|
String path = String(root->get_path_to(p_node, true));
|
||||||
if (!p_sub.is_empty()) {
|
if (!p_sub.is_empty()) {
|
||||||
path += ":" + p_sub;
|
path += ":" + p_sub;
|
||||||
}
|
}
|
||||||
@ -4423,11 +4423,11 @@ bool AnimationTrackEditor::has_track(Node3D *p_node, const String &p_sub, const
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AnimationTrackEditor::_insert_animation_key(NodePath p_path, const Variant &p_value) {
|
void AnimationTrackEditor::_insert_animation_key(NodePath p_path, const Variant &p_value) {
|
||||||
String path = p_path;
|
String path = String(p_path);
|
||||||
|
|
||||||
// Animation property is a special case, always creates an animation track.
|
// Animation property is a special case, always creates an animation track.
|
||||||
for (int i = 0; i < animation->get_track_count(); i++) {
|
for (int i = 0; i < animation->get_track_count(); i++) {
|
||||||
String np = animation->track_get_path(i);
|
String np = String(animation->track_get_path(i));
|
||||||
|
|
||||||
if (path == np && animation->track_get_type(i) == Animation::TYPE_ANIMATION) {
|
if (path == np && animation->track_get_type(i) == Animation::TYPE_ANIMATION) {
|
||||||
// Exists.
|
// Exists.
|
||||||
@ -4460,7 +4460,7 @@ void AnimationTrackEditor::insert_node_value_key(Node *p_node, const String &p_p
|
|||||||
ERR_FAIL_NULL(root);
|
ERR_FAIL_NULL(root);
|
||||||
|
|
||||||
// Let's build a node path.
|
// Let's build a node path.
|
||||||
String path = root->get_path_to(p_node, true);
|
String path = String(root->get_path_to(p_node, true));
|
||||||
|
|
||||||
// Get the value from the subpath.
|
// Get the value from the subpath.
|
||||||
Vector<StringName> subpath = NodePath(p_property).get_as_property_path().get_subnames();
|
Vector<StringName> subpath = NodePath(p_property).get_as_property_path().get_subnames();
|
||||||
@ -4509,14 +4509,14 @@ void AnimationTrackEditor::insert_node_value_key(Node *p_node, const String &p_p
|
|||||||
inserted = true;
|
inserted = true;
|
||||||
} else if (animation->track_get_type(i) == Animation::TYPE_BEZIER) {
|
} else if (animation->track_get_type(i) == Animation::TYPE_BEZIER) {
|
||||||
Variant actual_value;
|
Variant actual_value;
|
||||||
String track_path = animation->track_get_path(i);
|
String track_path = String(animation->track_get_path(i));
|
||||||
if (track_path == np) {
|
if (track_path == String(np)) {
|
||||||
actual_value = value; // All good.
|
actual_value = value; // All good.
|
||||||
} else {
|
} else {
|
||||||
int sep = track_path.rfind_char(':');
|
int sep = track_path.rfind_char(':');
|
||||||
if (sep != -1) {
|
if (sep != -1) {
|
||||||
String base_path = track_path.substr(0, sep);
|
String base_path = track_path.substr(0, sep);
|
||||||
if (base_path == np) {
|
if (base_path == String(np)) {
|
||||||
String value_name = track_path.substr(sep + 1);
|
String value_name = track_path.substr(sep + 1);
|
||||||
actual_value = value.get(value_name);
|
actual_value = value.get(value_name);
|
||||||
} else {
|
} else {
|
||||||
@ -5017,7 +5017,7 @@ void AnimationTrackEditor::_update_tracks() {
|
|||||||
String filter_text = timeline->filter_track->get_text();
|
String filter_text = timeline->filter_track->get_text();
|
||||||
|
|
||||||
if (!filter_text.is_empty()) {
|
if (!filter_text.is_empty()) {
|
||||||
String target = animation->track_get_path(i);
|
String target = String(animation->track_get_path(i));
|
||||||
if (!target.containsn(filter_text)) {
|
if (!target.containsn(filter_text)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -5087,7 +5087,7 @@ void AnimationTrackEditor::_update_tracks() {
|
|||||||
track_edits.push_back(track_edit);
|
track_edits.push_back(track_edit);
|
||||||
|
|
||||||
if (use_grouping) {
|
if (use_grouping) {
|
||||||
String base_path = animation->track_get_path(i);
|
String base_path = String(animation->track_get_path(i));
|
||||||
base_path = base_path.get_slicec(':', 0); // Remove sub-path.
|
base_path = base_path.get_slicec(':', 0); // Remove sub-path.
|
||||||
|
|
||||||
if (!group_sort.has(base_path)) {
|
if (!group_sort.has(base_path)) {
|
||||||
@ -5100,7 +5100,7 @@ void AnimationTrackEditor::_update_tracks() {
|
|||||||
if (n) {
|
if (n) {
|
||||||
icon = EditorNode::get_singleton()->get_object_icon(n, "Node");
|
icon = EditorNode::get_singleton()->get_object_icon(n, "Node");
|
||||||
name = n->get_name();
|
name = n->get_name();
|
||||||
tooltip = root->get_path_to(n);
|
tooltip = String(root->get_path_to(n));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6681,7 +6681,6 @@ void AnimationTrackEditor::goto_next_step(bool p_from_mouse_event, bool p_timeli
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
|
void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
|
||||||
last_menu_track_opt = p_option;
|
|
||||||
switch (p_option) {
|
switch (p_option) {
|
||||||
case EDIT_COPY_TRACKS: {
|
case EDIT_COPY_TRACKS: {
|
||||||
track_copy_select->clear();
|
track_copy_select->clear();
|
||||||
@ -6711,7 +6710,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
|
|||||||
|
|
||||||
path = NodePath(node->get_path().get_names(), path.get_subnames(), true); // Store full path instead for copying.
|
path = NodePath(node->get_path().get_names(), path.get_subnames(), true); // Store full path instead for copying.
|
||||||
} else {
|
} else {
|
||||||
text = path;
|
text = String(path);
|
||||||
int sep = text.find_char(':');
|
int sep = text.find_char(':');
|
||||||
if (sep != -1) {
|
if (sep != -1) {
|
||||||
text = text.substr(sep + 1);
|
text = text.substr(sep + 1);
|
||||||
@ -6841,11 +6840,15 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
|
|||||||
|
|
||||||
undo_redo->commit_action();
|
undo_redo->commit_action();
|
||||||
} break;
|
} break;
|
||||||
|
case EDIT_SCALE_SELECTION: {
|
||||||
case EDIT_SCALE_SELECTION:
|
scale_dialog->popup_centered(Size2(200, 100) * EDSCALE);
|
||||||
|
scale->get_line_edit()->grab_focus();
|
||||||
|
scale_from_cursor = false;
|
||||||
|
} break;
|
||||||
case EDIT_SCALE_FROM_CURSOR: {
|
case EDIT_SCALE_FROM_CURSOR: {
|
||||||
scale_dialog->popup_centered(Size2(200, 100) * EDSCALE);
|
scale_dialog->popup_centered(Size2(200, 100) * EDSCALE);
|
||||||
scale->get_line_edit()->grab_focus();
|
scale->get_line_edit()->grab_focus();
|
||||||
|
scale_from_cursor = true;
|
||||||
} break;
|
} break;
|
||||||
case EDIT_SCALE_CONFIRM: {
|
case EDIT_SCALE_CONFIRM: {
|
||||||
if (selection.is_empty()) {
|
if (selection.is_empty()) {
|
||||||
@ -6868,9 +6871,8 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
len = to_t - from_t;
|
len = to_t - from_t;
|
||||||
if (last_menu_track_opt == EDIT_SCALE_FROM_CURSOR) {
|
if (scale_from_cursor) {
|
||||||
pivot = timeline->get_play_position();
|
pivot = timeline->get_play_position();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
pivot = from_t;
|
pivot = from_t;
|
||||||
}
|
}
|
||||||
@ -6912,7 +6914,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
|
|||||||
to_restore.push_back(amr);
|
to_restore.push_back(amr);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define NEW_POS(m_ofs) (((s > 0) ? m_ofs : from_t + (len - (m_ofs - from_t))) - pivot) * Math::abs(s) + from_t
|
#define NEW_POS(m_ofs) (((s > 0) ? m_ofs : from_t + (len - (m_ofs - from_t))) - pivot) * Math::abs(s) + pivot
|
||||||
// 3 - Move the keys (re insert them).
|
// 3 - Move the keys (re insert them).
|
||||||
for (RBMap<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
|
for (RBMap<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
|
||||||
float newpos = NEW_POS(E->get().pos);
|
float newpos = NEW_POS(E->get().pos);
|
||||||
|
@ -802,7 +802,7 @@ class AnimationTrackEditor : public VBoxContainer {
|
|||||||
|
|
||||||
void _edit_menu_about_to_popup();
|
void _edit_menu_about_to_popup();
|
||||||
void _edit_menu_pressed(int p_option);
|
void _edit_menu_pressed(int p_option);
|
||||||
int last_menu_track_opt = 0;
|
bool scale_from_cursor = false;
|
||||||
|
|
||||||
void _cleanup_animation(Ref<Animation> p_animation);
|
void _cleanup_animation(Ref<Animation> p_animation);
|
||||||
|
|
||||||
|
@ -413,7 +413,7 @@ Rect2 AnimationTrackEditSpriteFrame::get_key_rect(int p_index, float p_pixels_se
|
|||||||
animation_name = animations.front()->get();
|
animation_name = animations.front()->get();
|
||||||
} else {
|
} else {
|
||||||
// Go through other track to find if animation is set
|
// Go through other track to find if animation is set
|
||||||
String animation_path = get_animation()->track_get_path(get_track());
|
String animation_path = String(get_animation()->track_get_path(get_track()));
|
||||||
animation_path = animation_path.replace(":frame", ":animation");
|
animation_path = animation_path.replace(":frame", ":animation");
|
||||||
int animation_track = get_animation()->find_track(animation_path, get_animation()->track_get_type(get_track()));
|
int animation_track = get_animation()->find_track(animation_path, get_animation()->track_get_type(get_track()));
|
||||||
float track_time = get_animation()->track_get_key_time(get_track(), p_index);
|
float track_time = get_animation()->track_get_key_time(get_track(), p_index);
|
||||||
@ -505,7 +505,7 @@ void AnimationTrackEditSpriteFrame::draw_key(int p_index, float p_pixels_sec, in
|
|||||||
animation_name = animations.front()->get();
|
animation_name = animations.front()->get();
|
||||||
} else {
|
} else {
|
||||||
// Go through other track to find if animation is set
|
// Go through other track to find if animation is set
|
||||||
String animation_path = get_animation()->track_get_path(get_track());
|
String animation_path = String(get_animation()->track_get_path(get_track()));
|
||||||
animation_path = animation_path.replace(":frame", ":animation");
|
animation_path = animation_path.replace(":frame", ":animation");
|
||||||
int animation_track = get_animation()->find_track(animation_path, get_animation()->track_get_type(get_track()));
|
int animation_track = get_animation()->find_track(animation_path, get_animation()->track_get_type(get_track()));
|
||||||
float track_time = get_animation()->track_get_key_time(get_track(), p_index);
|
float track_time = get_animation()->track_get_key_time(get_track(), p_index);
|
||||||
|
@ -39,9 +39,13 @@
|
|||||||
#include "editor/plugins/script_editor_plugin.h"
|
#include "editor/plugins/script_editor_plugin.h"
|
||||||
#include "editor/themes/editor_scale.h"
|
#include "editor/themes/editor_scale.h"
|
||||||
#include "editor/themes/editor_theme_manager.h"
|
#include "editor/themes/editor_theme_manager.h"
|
||||||
|
#include "scene/gui/check_box.h"
|
||||||
|
#include "scene/gui/label.h"
|
||||||
#include "scene/gui/line_edit.h"
|
#include "scene/gui/line_edit.h"
|
||||||
#include "scene/gui/menu_button.h"
|
#include "scene/gui/menu_button.h"
|
||||||
|
#include "scene/gui/rich_text_label.h"
|
||||||
#include "scene/gui/separator.h"
|
#include "scene/gui/separator.h"
|
||||||
|
#include "scene/main/timer.h"
|
||||||
#include "scene/resources/font.h"
|
#include "scene/resources/font.h"
|
||||||
|
|
||||||
void GotoLinePopup::popup_find_line(CodeTextEditor *p_text_editor) {
|
void GotoLinePopup::popup_find_line(CodeTextEditor *p_text_editor) {
|
||||||
@ -716,10 +720,6 @@ bool FindReplaceBar::is_selection_only() const {
|
|||||||
return selection_only->is_pressed();
|
return selection_only->is_pressed();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FindReplaceBar::set_error(const String &p_label) {
|
|
||||||
emit_signal(SNAME("error"), p_label);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FindReplaceBar::set_text_edit(CodeTextEditor *p_text_editor) {
|
void FindReplaceBar::set_text_edit(CodeTextEditor *p_text_editor) {
|
||||||
if (p_text_editor == base_text_editor) {
|
if (p_text_editor == base_text_editor) {
|
||||||
return;
|
return;
|
||||||
@ -749,8 +749,6 @@ void FindReplaceBar::set_text_edit(CodeTextEditor *p_text_editor) {
|
|||||||
|
|
||||||
void FindReplaceBar::_bind_methods() {
|
void FindReplaceBar::_bind_methods() {
|
||||||
ClassDB::bind_method("_search_current", &FindReplaceBar::search_current);
|
ClassDB::bind_method("_search_current", &FindReplaceBar::search_current);
|
||||||
|
|
||||||
ADD_SIGNAL(MethodInfo("error"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FindReplaceBar::FindReplaceBar() {
|
FindReplaceBar::FindReplaceBar() {
|
||||||
@ -1187,7 +1185,6 @@ void CodeTextEditor::set_find_replace_bar(FindReplaceBar *p_bar) {
|
|||||||
|
|
||||||
find_replace_bar = p_bar;
|
find_replace_bar = p_bar;
|
||||||
find_replace_bar->set_text_edit(this);
|
find_replace_bar->set_text_edit(this);
|
||||||
find_replace_bar->connect("error", callable_mp(error, &Label::set_text));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CodeTextEditor::remove_find_replace_bar() {
|
void CodeTextEditor::remove_find_replace_bar() {
|
||||||
@ -1195,7 +1192,6 @@ void CodeTextEditor::remove_find_replace_bar() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
find_replace_bar->disconnect("error", callable_mp(error, &Label::set_text));
|
|
||||||
find_replace_bar = nullptr;
|
find_replace_bar = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1516,20 +1512,35 @@ Variant CodeTextEditor::get_navigation_state() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CodeTextEditor::set_error(const String &p_error) {
|
void CodeTextEditor::set_error(const String &p_error) {
|
||||||
// Trim the error message if it is more than 2 lines long.
|
error->set_text(p_error);
|
||||||
if (p_error.count("\n") >= 2) {
|
|
||||||
Vector<String> splits = p_error.split("\n");
|
_update_error_content_height();
|
||||||
String trimmed_error = String("\n").join(splits.slice(0, 2));
|
|
||||||
error->set_text(trimmed_error + "...");
|
if (p_error.is_empty()) {
|
||||||
|
error->set_default_cursor_shape(CURSOR_ARROW);
|
||||||
} else {
|
} else {
|
||||||
error->set_text(p_error);
|
error->set_default_cursor_shape(CURSOR_POINTING_HAND);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CodeTextEditor::_update_error_content_height() {
|
||||||
|
float margin_height = 0;
|
||||||
|
const Ref<StyleBox> style = error->get_theme_stylebox(CoreStringName(normal));
|
||||||
|
if (style.is_valid()) {
|
||||||
|
margin_height += style->get_content_margin(SIDE_TOP) + style->get_content_margin(SIDE_BOTTOM);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!p_error.is_empty()) {
|
const float content_height = margin_height + error->get_content_height();
|
||||||
error->set_default_cursor_shape(CURSOR_POINTING_HAND);
|
|
||||||
} else {
|
float content_max_height = margin_height;
|
||||||
error->set_default_cursor_shape(CURSOR_ARROW);
|
for (int i = 0; i < 3; i++) {
|
||||||
|
if (i >= error->get_line_count()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
content_max_height += error->get_line_height(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
error->set_custom_minimum_size(Size2(0, CLAMP(content_height, 0, content_max_height)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CodeTextEditor::set_error_pos(int p_line, int p_column) {
|
void CodeTextEditor::set_error_pos(int p_line, int p_column) {
|
||||||
@ -1559,28 +1570,36 @@ void CodeTextEditor::goto_error() {
|
|||||||
void CodeTextEditor::_update_text_editor_theme() {
|
void CodeTextEditor::_update_text_editor_theme() {
|
||||||
emit_signal(SNAME("load_theme_settings"));
|
emit_signal(SNAME("load_theme_settings"));
|
||||||
|
|
||||||
error_button->set_button_icon(get_editor_theme_icon(SNAME("StatusError")));
|
const Ref<Font> status_bar_font = get_theme_font(SNAME("status_source"), EditorStringName(EditorFonts));
|
||||||
warning_button->set_button_icon(get_editor_theme_icon(SNAME("NodeWarning")));
|
const int status_bar_font_size = get_theme_font_size(SNAME("status_source_size"), EditorStringName(EditorFonts));
|
||||||
|
|
||||||
Ref<Font> status_bar_font = get_theme_font(SNAME("status_source"), EditorStringName(EditorFonts));
|
|
||||||
int status_bar_font_size = get_theme_font_size(SNAME("status_source_size"), EditorStringName(EditorFonts));
|
|
||||||
|
|
||||||
int count = status_bar->get_child_count();
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
Control *n = Object::cast_to<Control>(status_bar->get_child(i));
|
|
||||||
if (n) {
|
|
||||||
n->add_theme_font_override(SceneStringName(font), status_bar_font);
|
|
||||||
n->add_theme_font_size_override(SceneStringName(font_size), status_bar_font_size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const Color &error_color = get_theme_color(SNAME("error_color"), EditorStringName(Editor));
|
const Color &error_color = get_theme_color(SNAME("error_color"), EditorStringName(Editor));
|
||||||
const Color &warning_color = get_theme_color(SNAME("warning_color"), EditorStringName(Editor));
|
const Color &warning_color = get_theme_color(SNAME("warning_color"), EditorStringName(Editor));
|
||||||
|
const Ref<StyleBox> label_stylebox = get_theme_stylebox(SNAME("normal"), SNAME("Label")); // Empty stylebox.
|
||||||
|
|
||||||
error->add_theme_color_override(SceneStringName(font_color), error_color);
|
error->begin_bulk_theme_override();
|
||||||
|
error->add_theme_font_override(SNAME("normal_font"), status_bar_font);
|
||||||
|
error->add_theme_font_size_override(SNAME("normal_font_size"), status_bar_font_size);
|
||||||
|
error->add_theme_color_override(SNAME("default_color"), error_color);
|
||||||
|
error->add_theme_style_override(SNAME("normal"), label_stylebox);
|
||||||
|
error->end_bulk_theme_override();
|
||||||
|
|
||||||
|
error_button->set_button_icon(get_editor_theme_icon(SNAME("StatusError")));
|
||||||
error_button->add_theme_color_override(SceneStringName(font_color), error_color);
|
error_button->add_theme_color_override(SceneStringName(font_color), error_color);
|
||||||
|
|
||||||
|
warning_button->set_button_icon(get_editor_theme_icon(SNAME("NodeWarning")));
|
||||||
warning_button->add_theme_color_override(SceneStringName(font_color), warning_color);
|
warning_button->add_theme_color_override(SceneStringName(font_color), warning_color);
|
||||||
|
|
||||||
|
const int child_count = status_bar->get_child_count();
|
||||||
|
for (int i = 0; i < child_count; i++) {
|
||||||
|
Control *child = Object::cast_to<Control>(status_bar->get_child(i));
|
||||||
|
if (child) {
|
||||||
|
child->begin_bulk_theme_override();
|
||||||
|
child->add_theme_font_override(SceneStringName(font), status_bar_font);
|
||||||
|
child->add_theme_font_size_override(SceneStringName(font_size), status_bar_font_size);
|
||||||
|
child->end_bulk_theme_override();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_update_font_ligatures();
|
_update_font_ligatures();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1909,19 +1928,16 @@ CodeTextEditor::CodeTextEditor() {
|
|||||||
toggle_files_button->hide();
|
toggle_files_button->hide();
|
||||||
|
|
||||||
// Error
|
// Error
|
||||||
ScrollContainer *scroll = memnew(ScrollContainer);
|
error = memnew(RichTextLabel);
|
||||||
scroll->set_h_size_flags(SIZE_EXPAND_FILL);
|
error->set_use_bbcode(true);
|
||||||
scroll->set_v_size_flags(SIZE_EXPAND_FILL);
|
error->set_selection_enabled(true);
|
||||||
scroll->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
|
error->set_context_menu_enabled(true);
|
||||||
status_bar->add_child(scroll);
|
|
||||||
|
|
||||||
error = memnew(Label);
|
|
||||||
error->set_focus_mode(FOCUS_ACCESSIBILITY);
|
|
||||||
error->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
|
error->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
|
||||||
error->set_v_size_flags(SIZE_EXPAND | SIZE_SHRINK_CENTER);
|
error->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||||
error->set_mouse_filter(MOUSE_FILTER_STOP);
|
error->set_v_size_flags(SIZE_SHRINK_CENTER);
|
||||||
scroll->add_child(error);
|
|
||||||
error->connect(SceneStringName(gui_input), callable_mp(this, &CodeTextEditor::_error_pressed));
|
error->connect(SceneStringName(gui_input), callable_mp(this, &CodeTextEditor::_error_pressed));
|
||||||
|
error->connect(SceneStringName(resized), callable_mp(this, &CodeTextEditor::_update_error_content_height));
|
||||||
|
status_bar->add_child(error);
|
||||||
|
|
||||||
// Errors
|
// Errors
|
||||||
error_button = memnew(Button);
|
error_button = memnew(Button);
|
||||||
|
@ -31,17 +31,17 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "scene/gui/box_container.h"
|
#include "scene/gui/box_container.h"
|
||||||
#include "scene/gui/button.h"
|
|
||||||
#include "scene/gui/check_box.h"
|
|
||||||
#include "scene/gui/code_edit.h"
|
#include "scene/gui/code_edit.h"
|
||||||
#include "scene/gui/dialogs.h"
|
#include "scene/gui/dialogs.h"
|
||||||
#include "scene/gui/label.h"
|
|
||||||
#include "scene/gui/popup.h"
|
|
||||||
#include "scene/main/timer.h"
|
|
||||||
|
|
||||||
class MenuButton;
|
class Button;
|
||||||
|
class CheckBox;
|
||||||
class CodeTextEditor;
|
class CodeTextEditor;
|
||||||
|
class Label;
|
||||||
class LineEdit;
|
class LineEdit;
|
||||||
|
class MenuButton;
|
||||||
|
class RichTextLabel;
|
||||||
|
class Timer;
|
||||||
|
|
||||||
class GotoLinePopup : public PopupPanel {
|
class GotoLinePopup : public PopupPanel {
|
||||||
GDCLASS(GotoLinePopup, PopupPanel);
|
GDCLASS(GotoLinePopup, PopupPanel);
|
||||||
@ -138,7 +138,6 @@ public:
|
|||||||
bool is_case_sensitive() const;
|
bool is_case_sensitive() const;
|
||||||
bool is_whole_words() const;
|
bool is_whole_words() const;
|
||||||
bool is_selection_only() const;
|
bool is_selection_only() const;
|
||||||
void set_error(const String &p_label);
|
|
||||||
|
|
||||||
void set_text_edit(CodeTextEditor *p_text_editor);
|
void set_text_edit(CodeTextEditor *p_text_editor);
|
||||||
|
|
||||||
@ -183,7 +182,7 @@ class CodeTextEditor : public VBoxContainer {
|
|||||||
|
|
||||||
float zoom_factor = 1.0f;
|
float zoom_factor = 1.0f;
|
||||||
|
|
||||||
Label *error = nullptr;
|
RichTextLabel *error = nullptr;
|
||||||
int error_line;
|
int error_line;
|
||||||
int error_column;
|
int error_column;
|
||||||
|
|
||||||
@ -211,6 +210,8 @@ class CodeTextEditor : public VBoxContainer {
|
|||||||
void _zoom_out();
|
void _zoom_out();
|
||||||
void _zoom_to(float p_zoom_factor);
|
void _zoom_to(float p_zoom_factor);
|
||||||
|
|
||||||
|
void _update_error_content_height();
|
||||||
|
|
||||||
void _error_button_pressed();
|
void _error_button_pressed();
|
||||||
void _warning_button_pressed();
|
void _warning_button_pressed();
|
||||||
void _set_show_errors_panel(bool p_show);
|
void _set_show_errors_panel(bool p_show);
|
||||||
|
@ -643,6 +643,7 @@ void ScriptEditorDebugger::_msg_error(uint64_t p_thread_id, const Array &p_data)
|
|||||||
// item with the original error condition.
|
// item with the original error condition.
|
||||||
error_title += oe.error_descr.is_empty() ? oe.error : oe.error_descr;
|
error_title += oe.error_descr.is_empty() ? oe.error : oe.error_descr;
|
||||||
error->set_text(1, error_title);
|
error->set_text(1, error_title);
|
||||||
|
error->set_autowrap_mode(1, TextServer::AUTOWRAP_WORD_SMART);
|
||||||
tooltip += " " + error_title + "\n";
|
tooltip += " " + error_title + "\n";
|
||||||
|
|
||||||
// Find the language of the error's source file.
|
// Find the language of the error's source file.
|
||||||
@ -980,16 +981,20 @@ void ScriptEditorDebugger::_init_parse_message_handlers() {
|
|||||||
void ScriptEditorDebugger::_set_reason_text(const String &p_reason, MessageType p_type) {
|
void ScriptEditorDebugger::_set_reason_text(const String &p_reason, MessageType p_type) {
|
||||||
switch (p_type) {
|
switch (p_type) {
|
||||||
case MESSAGE_ERROR:
|
case MESSAGE_ERROR:
|
||||||
reason->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("error_color"), EditorStringName(Editor)));
|
reason->add_theme_color_override(SNAME("default_color"), get_theme_color(SNAME("error_color"), EditorStringName(Editor)));
|
||||||
break;
|
break;
|
||||||
case MESSAGE_WARNING:
|
case MESSAGE_WARNING:
|
||||||
reason->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("warning_color"), EditorStringName(Editor)));
|
reason->add_theme_color_override(SNAME("default_color"), get_theme_color(SNAME("warning_color"), EditorStringName(Editor)));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
reason->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("success_color"), EditorStringName(Editor)));
|
reason->add_theme_color_override(SNAME("default_color"), get_theme_color(SNAME("success_color"), EditorStringName(Editor)));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
reason->set_text(p_reason);
|
reason->set_text(p_reason);
|
||||||
|
|
||||||
|
_update_reason_content_height();
|
||||||
|
|
||||||
const PackedInt32Array boundaries = TS->string_get_word_breaks(p_reason, "", 80);
|
const PackedInt32Array boundaries = TS->string_get_word_breaks(p_reason, "", 80);
|
||||||
PackedStringArray lines;
|
PackedStringArray lines;
|
||||||
for (int i = 0; i < boundaries.size(); i += 2) {
|
for (int i = 0; i < boundaries.size(); i += 2) {
|
||||||
@ -1001,6 +1006,26 @@ void ScriptEditorDebugger::_set_reason_text(const String &p_reason, MessageType
|
|||||||
reason->set_tooltip_text(String("\n").join(lines));
|
reason->set_tooltip_text(String("\n").join(lines));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ScriptEditorDebugger::_update_reason_content_height() {
|
||||||
|
float margin_height = 0;
|
||||||
|
const Ref<StyleBox> style = reason->get_theme_stylebox(CoreStringName(normal));
|
||||||
|
if (style.is_valid()) {
|
||||||
|
margin_height += style->get_content_margin(SIDE_TOP) + style->get_content_margin(SIDE_BOTTOM);
|
||||||
|
}
|
||||||
|
|
||||||
|
const float content_height = margin_height + reason->get_content_height();
|
||||||
|
|
||||||
|
float content_max_height = margin_height;
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
if (i >= reason->get_line_count()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
content_max_height += reason->get_line_height(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
reason->set_custom_minimum_size(Size2(0, CLAMP(content_height, 0, content_max_height)));
|
||||||
|
}
|
||||||
|
|
||||||
void ScriptEditorDebugger::_notification(int p_what) {
|
void ScriptEditorDebugger::_notification(int p_what) {
|
||||||
switch (p_what) {
|
switch (p_what) {
|
||||||
case NOTIFICATION_ENTER_TREE: {
|
case NOTIFICATION_ENTER_TREE: {
|
||||||
@ -1031,7 +1056,8 @@ void ScriptEditorDebugger::_notification(int p_what) {
|
|||||||
vmem_export->set_button_icon(get_editor_theme_icon(SNAME("Save")));
|
vmem_export->set_button_icon(get_editor_theme_icon(SNAME("Save")));
|
||||||
search->set_right_icon(get_editor_theme_icon(SNAME("Search")));
|
search->set_right_icon(get_editor_theme_icon(SNAME("Search")));
|
||||||
|
|
||||||
reason->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("error_color"), EditorStringName(Editor)));
|
reason->add_theme_color_override(SNAME("default_color"), get_theme_color(SNAME("error_color"), EditorStringName(Editor)));
|
||||||
|
reason->add_theme_style_override(SNAME("normal"), get_theme_stylebox(SNAME("normal"), SNAME("Label"))); // Empty stylebox.
|
||||||
|
|
||||||
TreeItem *error_root = error_tree->get_root();
|
TreeItem *error_root = error_tree->get_root();
|
||||||
if (error_root) {
|
if (error_root) {
|
||||||
@ -1245,6 +1271,7 @@ void ScriptEditorDebugger::stop() {
|
|||||||
peer.unref();
|
peer.unref();
|
||||||
reason->set_text("");
|
reason->set_text("");
|
||||||
reason->set_tooltip_text("");
|
reason->set_tooltip_text("");
|
||||||
|
reason->set_custom_minimum_size(Size2(0, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
node_path_cache.clear();
|
node_path_cache.clear();
|
||||||
@ -1531,7 +1558,7 @@ void ScriptEditorDebugger::update_live_edit_root() {
|
|||||||
msg.push_back("");
|
msg.push_back("");
|
||||||
}
|
}
|
||||||
_put_msg("scene:live_set_root", msg);
|
_put_msg("scene:live_set_root", msg);
|
||||||
live_edit_root->set_text(np);
|
live_edit_root->set_text(String(np));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptEditorDebugger::live_debug_create_node(const NodePath &p_parent, const String &p_type, const String &p_name) {
|
void ScriptEditorDebugger::live_debug_create_node(const NodePath &p_parent, const String &p_type, const String &p_name) {
|
||||||
@ -1966,14 +1993,14 @@ ScriptEditorDebugger::ScriptEditorDebugger() {
|
|||||||
HBoxContainer *hbc = memnew(HBoxContainer);
|
HBoxContainer *hbc = memnew(HBoxContainer);
|
||||||
vbc->add_child(hbc);
|
vbc->add_child(hbc);
|
||||||
|
|
||||||
reason = memnew(Label);
|
reason = memnew(RichTextLabel);
|
||||||
reason->set_focus_mode(FOCUS_ACCESSIBILITY);
|
reason->set_focus_mode(FOCUS_ACCESSIBILITY);
|
||||||
reason->set_text("");
|
reason->set_selection_enabled(true);
|
||||||
hbc->add_child(reason);
|
reason->set_context_menu_enabled(true);
|
||||||
reason->set_h_size_flags(SIZE_EXPAND_FILL);
|
reason->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||||
reason->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART);
|
reason->set_v_size_flags(SIZE_SHRINK_CENTER);
|
||||||
reason->set_max_lines_visible(3);
|
reason->connect(SceneStringName(resized), callable_mp(this, &ScriptEditorDebugger::_update_reason_content_height));
|
||||||
reason->set_mouse_filter(Control::MOUSE_FILTER_PASS);
|
hbc->add_child(reason);
|
||||||
|
|
||||||
hbc->add_child(memnew(VSeparator));
|
hbc->add_child(memnew(VSeparator));
|
||||||
|
|
||||||
|
@ -118,7 +118,7 @@ private:
|
|||||||
|
|
||||||
TabContainer *tabs = nullptr;
|
TabContainer *tabs = nullptr;
|
||||||
|
|
||||||
Label *reason = nullptr;
|
RichTextLabel *reason = nullptr;
|
||||||
|
|
||||||
Button *skip_breakpoints = nullptr;
|
Button *skip_breakpoints = nullptr;
|
||||||
Button *ignore_error_breaks = nullptr;
|
Button *ignore_error_breaks = nullptr;
|
||||||
@ -231,6 +231,7 @@ private:
|
|||||||
|
|
||||||
void _parse_message(const String &p_msg, uint64_t p_thread_id, const Array &p_data);
|
void _parse_message(const String &p_msg, uint64_t p_thread_id, const Array &p_data);
|
||||||
void _set_reason_text(const String &p_reason, MessageType p_type);
|
void _set_reason_text(const String &p_reason, MessageType p_type);
|
||||||
|
void _update_reason_content_height();
|
||||||
void _update_buttons_state();
|
void _update_buttons_state();
|
||||||
void _remote_object_selected(ObjectID p_object);
|
void _remote_object_selected(ObjectID p_object);
|
||||||
void _remote_objects_edited(const String &p_prop, const TypedDictionary<uint64_t, Variant> &p_values, const String &p_field);
|
void _remote_objects_edited(const String &p_prop, const TypedDictionary<uint64_t, Variant> &p_values, const String &p_field);
|
||||||
|
@ -6636,7 +6636,7 @@ void EditorNode::reload_instances_with_path_in_edited_scenes() {
|
|||||||
// it's a multi-level inheritance scene. We should use
|
// it's a multi-level inheritance scene. We should use
|
||||||
NodePath scene_path_to_node = current_edited_scene->get_path_to(original_node);
|
NodePath scene_path_to_node = current_edited_scene->get_path_to(original_node);
|
||||||
Ref<SceneState> scene_state = current_edited_scene->get_scene_inherited_state();
|
Ref<SceneState> scene_state = current_edited_scene->get_scene_inherited_state();
|
||||||
if (scene_path_to_node != "." && scene_state.is_valid() && scene_state->get_path() != instance_modifications.instance_path && scene_state->find_node_by_path(scene_path_to_node) >= 0) {
|
if (String(scene_path_to_node) != "." && scene_state.is_valid() && scene_state->get_path() != instance_modifications.instance_path && scene_state->find_node_by_path(scene_path_to_node) >= 0) {
|
||||||
Node *root_node = scene_state->instantiate(SceneState::GenEditState::GEN_EDIT_STATE_INSTANCE);
|
Node *root_node = scene_state->instantiate(SceneState::GenEditState::GEN_EDIT_STATE_INSTANCE);
|
||||||
instantiated_node = root_node->get_node(scene_path_to_node);
|
instantiated_node = root_node->get_node(scene_path_to_node);
|
||||||
|
|
||||||
|
@ -2866,7 +2866,7 @@ void EditorPropertyNodePath::_menu_option(int p_idx) {
|
|||||||
} break;
|
} break;
|
||||||
|
|
||||||
case ACTION_COPY: {
|
case ACTION_COPY: {
|
||||||
DisplayServer::get_singleton()->clipboard_set(_get_node_path());
|
DisplayServer::get_singleton()->clipboard_set(String(_get_node_path()));
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case ACTION_EDIT: {
|
case ACTION_EDIT: {
|
||||||
@ -2874,7 +2874,7 @@ void EditorPropertyNodePath::_menu_option(int p_idx) {
|
|||||||
menu->hide();
|
menu->hide();
|
||||||
|
|
||||||
const NodePath &np = _get_node_path();
|
const NodePath &np = _get_node_path();
|
||||||
edit->set_text(np);
|
edit->set_text(String(np));
|
||||||
edit->show();
|
edit->show();
|
||||||
callable_mp((Control *)edit, &Control::grab_focus).call_deferred();
|
callable_mp((Control *)edit, &Control::grab_focus).call_deferred();
|
||||||
} break;
|
} break;
|
||||||
@ -2976,7 +2976,7 @@ bool EditorPropertyNodePath::is_drop_valid(const Dictionary &p_drag_data) const
|
|||||||
void EditorPropertyNodePath::update_property() {
|
void EditorPropertyNodePath::update_property() {
|
||||||
const Node *base_node = get_base_node();
|
const Node *base_node = get_base_node();
|
||||||
const NodePath &p = _get_node_path();
|
const NodePath &p = _get_node_path();
|
||||||
assign->set_tooltip_text(p);
|
assign->set_tooltip_text(String(p));
|
||||||
|
|
||||||
if (p.is_empty()) {
|
if (p.is_empty()) {
|
||||||
assign->set_button_icon(Ref<Texture2D>());
|
assign->set_button_icon(Ref<Texture2D>());
|
||||||
@ -2988,7 +2988,7 @@ void EditorPropertyNodePath::update_property() {
|
|||||||
|
|
||||||
if (!base_node || !base_node->has_node(p)) {
|
if (!base_node || !base_node->has_node(p)) {
|
||||||
assign->set_button_icon(Ref<Texture2D>());
|
assign->set_button_icon(Ref<Texture2D>());
|
||||||
assign->set_text(p);
|
assign->set_text(String(p));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2997,7 +2997,7 @@ void EditorPropertyNodePath::update_property() {
|
|||||||
|
|
||||||
if (String(target_node->get_name()).contains_char('@')) {
|
if (String(target_node->get_name()).contains_char('@')) {
|
||||||
assign->set_button_icon(Ref<Texture2D>());
|
assign->set_button_icon(Ref<Texture2D>());
|
||||||
assign->set_text(p);
|
assign->set_text(String(p));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -430,7 +430,7 @@ void FileSystemDock::_update_tree(const Vector<String> &p_uncollapsed_paths, boo
|
|||||||
} else if (favorite.ends_with("/")) {
|
} else if (favorite.ends_with("/")) {
|
||||||
text = favorite.substr(0, favorite.length() - 1).get_file();
|
text = favorite.substr(0, favorite.length() - 1).get_file();
|
||||||
icon = folder_icon;
|
icon = folder_icon;
|
||||||
color = assigned_folder_colors.has(favorite) ? folder_colors[assigned_folder_colors[favorite]] : default_folder_color;
|
color = FileSystemDock::get_dir_icon_color(favorite, default_folder_color);
|
||||||
} else {
|
} else {
|
||||||
text = favorite.get_file();
|
text = favorite.get_file();
|
||||||
int index;
|
int index;
|
||||||
@ -3951,6 +3951,30 @@ void FileSystemDock::_file_sort_popup(int p_id) {
|
|||||||
set_file_sort((FileSortOption)p_id);
|
set_file_sort((FileSortOption)p_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Could use a unit test.
|
||||||
|
Color FileSystemDock::get_dir_icon_color(const String &p_dir_path, const Color &p_default) {
|
||||||
|
if (!singleton) { // This method can be called from the project manager.
|
||||||
|
return p_default;
|
||||||
|
}
|
||||||
|
Color folder_icon_color = p_default;
|
||||||
|
|
||||||
|
// Check for a folder color to inherit (if one is assigned).
|
||||||
|
String parent_dir = ProjectSettings::get_singleton()->localize_path(p_dir_path);
|
||||||
|
while (!parent_dir.is_empty() && parent_dir != "res://") {
|
||||||
|
if (!parent_dir.ends_with("/")) {
|
||||||
|
parent_dir += "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
const String color_name = singleton->assigned_folder_colors.get(parent_dir, String());
|
||||||
|
if (!color_name.is_empty()) {
|
||||||
|
folder_icon_color = singleton->folder_colors[color_name];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
parent_dir = parent_dir.trim_suffix("/").get_base_dir();
|
||||||
|
}
|
||||||
|
return folder_icon_color;
|
||||||
|
}
|
||||||
|
|
||||||
const HashMap<String, Color> &FileSystemDock::get_folder_colors() const {
|
const HashMap<String, Color> &FileSystemDock::get_folder_colors() const {
|
||||||
return folder_colors;
|
return folder_colors;
|
||||||
}
|
}
|
||||||
|
@ -383,6 +383,8 @@ public:
|
|||||||
static constexpr double ITEM_ALPHA_MAX = 0.15;
|
static constexpr double ITEM_ALPHA_MAX = 0.15;
|
||||||
static constexpr double ITEM_BG_DARK_SCALE = 0.3;
|
static constexpr double ITEM_BG_DARK_SCALE = 0.3;
|
||||||
|
|
||||||
|
static Color get_dir_icon_color(const String &p_dir_path, const Color &p_default);
|
||||||
|
|
||||||
const HashMap<String, Color> &get_folder_colors() const;
|
const HashMap<String, Color> &get_folder_colors() const;
|
||||||
Dictionary get_assigned_folder_colors() const;
|
Dictionary get_assigned_folder_colors() const;
|
||||||
|
|
||||||
|
@ -973,35 +973,6 @@ void EditorFileDialog::update_file_name() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Could use a unit test.
|
|
||||||
Color EditorFileDialog::get_dir_icon_color(const String &p_dir_path) {
|
|
||||||
if (!FileSystemDock::get_singleton()) { // This dialog can be called from the project manager.
|
|
||||||
return theme_cache.folder_icon_color;
|
|
||||||
}
|
|
||||||
|
|
||||||
const HashMap<String, Color> &folder_colors = FileSystemDock::get_singleton()->get_folder_colors();
|
|
||||||
Dictionary assigned_folder_colors = FileSystemDock::get_singleton()->get_assigned_folder_colors();
|
|
||||||
|
|
||||||
Color folder_icon_color = theme_cache.folder_icon_color;
|
|
||||||
|
|
||||||
// Check for a folder color to inherit (if one is assigned).
|
|
||||||
String parent_dir = ProjectSettings::get_singleton()->localize_path(p_dir_path);
|
|
||||||
while (!parent_dir.is_empty() && parent_dir != "res://") {
|
|
||||||
if (!parent_dir.ends_with("/")) {
|
|
||||||
parent_dir += "/";
|
|
||||||
}
|
|
||||||
if (assigned_folder_colors.has(parent_dir)) {
|
|
||||||
folder_icon_color = folder_colors[assigned_folder_colors[parent_dir]];
|
|
||||||
if (folder_icon_color != theme_cache.folder_icon_color) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
parent_dir = parent_dir.trim_suffix("/").get_base_dir();
|
|
||||||
}
|
|
||||||
|
|
||||||
return folder_icon_color;
|
|
||||||
}
|
|
||||||
|
|
||||||
// DO NOT USE THIS FUNCTION UNLESS NEEDED, CALL INVALIDATE() INSTEAD.
|
// DO NOT USE THIS FUNCTION UNLESS NEEDED, CALL INVALIDATE() INSTEAD.
|
||||||
void EditorFileDialog::update_file_list() {
|
void EditorFileDialog::update_file_list() {
|
||||||
int thumbnail_size = EDITOR_GET("filesystem/file_dialog/thumbnail_size");
|
int thumbnail_size = EDITOR_GET("filesystem/file_dialog/thumbnail_size");
|
||||||
@ -1155,7 +1126,7 @@ void EditorFileDialog::update_file_list() {
|
|||||||
d["bundle"] = bundle;
|
d["bundle"] = bundle;
|
||||||
|
|
||||||
item_list->set_item_metadata(-1, d);
|
item_list->set_item_metadata(-1, d);
|
||||||
item_list->set_item_icon_modulate(-1, get_dir_icon_color(String(d["path"])));
|
item_list->set_item_icon_modulate(-1, FileSystemDock::get_dir_icon_color(String(d["path"]), theme_cache.folder_icon_color));
|
||||||
}
|
}
|
||||||
|
|
||||||
dirs.pop_front();
|
dirs.pop_front();
|
||||||
@ -1840,7 +1811,7 @@ void EditorFileDialog::_update_favorites() {
|
|||||||
favorites->add_item(favorited_names[i], theme_cache.folder);
|
favorites->add_item(favorited_names[i], theme_cache.folder);
|
||||||
favorites->set_item_tooltip(-1, favorited_paths[i]);
|
favorites->set_item_tooltip(-1, favorited_paths[i]);
|
||||||
favorites->set_item_metadata(-1, favorited_paths[i]);
|
favorites->set_item_metadata(-1, favorited_paths[i]);
|
||||||
favorites->set_item_icon_modulate(-1, get_dir_icon_color(favorited_paths[i]));
|
favorites->set_item_icon_modulate(-1, FileSystemDock::get_dir_icon_color(favorited_paths[i], theme_cache.folder_icon_color));
|
||||||
|
|
||||||
if (i == current_favorite) {
|
if (i == current_favorite) {
|
||||||
favorite->set_pressed(true);
|
favorite->set_pressed(true);
|
||||||
@ -1925,7 +1896,7 @@ void EditorFileDialog::_update_recent() {
|
|||||||
recent->add_item(recentd_names[i], theme_cache.folder);
|
recent->add_item(recentd_names[i], theme_cache.folder);
|
||||||
recent->set_item_tooltip(-1, recentd_paths[i]);
|
recent->set_item_tooltip(-1, recentd_paths[i]);
|
||||||
recent->set_item_metadata(-1, recentd_paths[i]);
|
recent->set_item_metadata(-1, recentd_paths[i]);
|
||||||
recent->set_item_icon_modulate(-1, get_dir_icon_color(recentd_paths[i]));
|
recent->set_item_icon_modulate(-1, FileSystemDock::get_dir_icon_color(recentd_paths[i], theme_cache.folder_icon_color));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (modified) {
|
if (modified) {
|
||||||
|
@ -301,8 +301,6 @@ protected:
|
|||||||
static void _bind_methods();
|
static void _bind_methods();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Color get_dir_icon_color(const String &p_dir_path);
|
|
||||||
|
|
||||||
virtual void set_visible(bool p_visible) override;
|
virtual void set_visible(bool p_visible) override;
|
||||||
virtual void popup(const Rect2i &p_rect = Rect2i()) override;
|
virtual void popup(const Rect2i &p_rect = Rect2i()) override;
|
||||||
|
|
||||||
|
@ -99,10 +99,7 @@ void EditorObjectSelector::_show_popup() {
|
|||||||
Point2 gp = get_screen_position();
|
Point2 gp = get_screen_position();
|
||||||
gp.y += size.y;
|
gp.y += size.y;
|
||||||
|
|
||||||
sub_objects_menu->set_position(gp);
|
sub_objects_menu->popup(Rect2(gp, Size2(size.width, 0)));
|
||||||
sub_objects_menu->set_size(Size2(size.width, 1));
|
|
||||||
|
|
||||||
sub_objects_menu->popup();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorObjectSelector::_about_to_show() {
|
void EditorObjectSelector::_about_to_show() {
|
||||||
|
@ -1565,7 +1565,7 @@ void ColladaImport::create_animation(int p_clip, bool p_import_value_tracks) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
NodeMap &nm = node_map[E];
|
NodeMap &nm = node_map[E];
|
||||||
String path = scene->get_path_to(nm.node);
|
String path = String(scene->get_path_to(nm.node));
|
||||||
|
|
||||||
if (nm.bone >= 0) {
|
if (nm.bone >= 0) {
|
||||||
Skeleton3D *sk = static_cast<Skeleton3D *>(nm.node);
|
Skeleton3D *sk = static_cast<Skeleton3D *>(nm.node);
|
||||||
@ -1756,7 +1756,7 @@ void ColladaImport::create_animation(int p_clip, bool p_import_value_tracks) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
NodeMap &nm = node_map[at.target];
|
NodeMap &nm = node_map[at.target];
|
||||||
String path = scene->get_path_to(nm.node);
|
String path = String(scene->get_path_to(nm.node));
|
||||||
|
|
||||||
animation->add_track(Animation::TYPE_BLEND_SHAPE);
|
animation->add_track(Animation::TYPE_BLEND_SHAPE);
|
||||||
int track = animation->get_track_count() - 1;
|
int track = animation->get_track_count() - 1;
|
||||||
|
@ -228,9 +228,9 @@ void PostImportPluginSkeletonRenamer::internal_process(InternalImportCategory p_
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (anim->track_get_path(i).get_subname_count() > 0) {
|
if (anim->track_get_path(i).get_subname_count() > 0) {
|
||||||
anim->track_set_path(i, UNIQUE_NODE_PREFIX + unique_name + "/" + node->get_path_to(orig_node) + String(":") + anim->track_get_path(i).get_concatenated_subnames());
|
anim->track_set_path(i, UNIQUE_NODE_PREFIX + unique_name + "/" + String(node->get_path_to(orig_node)) + String(":") + anim->track_get_path(i).get_concatenated_subnames());
|
||||||
} else {
|
} else {
|
||||||
anim->track_set_path(i, UNIQUE_NODE_PREFIX + unique_name + "/" + node->get_path_to(orig_node));
|
anim->track_set_path(i, UNIQUE_NODE_PREFIX + unique_name + "/" + String(node->get_path_to(orig_node)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -1062,7 +1062,7 @@ Node *ResourceImporterScene::_pre_fix_animations(Node *p_node, Node *p_root, con
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String import_id = p_node->get_meta("import_id", "PATH:" + p_root->get_path_to(p_node));
|
String import_id = p_node->get_meta("import_id", "PATH:" + String(p_root->get_path_to(p_node)));
|
||||||
|
|
||||||
Dictionary node_settings;
|
Dictionary node_settings;
|
||||||
if (p_node_data.has(import_id)) {
|
if (p_node_data.has(import_id)) {
|
||||||
@ -1110,7 +1110,7 @@ Node *ResourceImporterScene::_post_fix_animations(Node *p_node, Node *p_root, co
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String import_id = p_node->get_meta("import_id", "PATH:" + p_root->get_path_to(p_node));
|
String import_id = p_node->get_meta("import_id", "PATH:" + String(p_root->get_path_to(p_node)));
|
||||||
|
|
||||||
Dictionary node_settings;
|
Dictionary node_settings;
|
||||||
if (p_node_data.has(import_id)) {
|
if (p_node_data.has(import_id)) {
|
||||||
@ -1437,7 +1437,7 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap<
|
|||||||
|
|
||||||
bool isroot = p_node == p_root;
|
bool isroot = p_node == p_root;
|
||||||
|
|
||||||
String import_id = p_node->get_meta("import_id", "PATH:" + p_root->get_path_to(p_node));
|
String import_id = p_node->get_meta("import_id", "PATH:" + String(p_root->get_path_to(p_node)));
|
||||||
|
|
||||||
Dictionary node_settings;
|
Dictionary node_settings;
|
||||||
if (p_node_data.has(import_id)) {
|
if (p_node_data.has(import_id)) {
|
||||||
|
@ -765,7 +765,7 @@ bool AnimationNodeBlendTreeEditor::_update_filters(const Ref<AnimationNode> &ano
|
|||||||
for (const StringName &E : animation_list) {
|
for (const StringName &E : animation_list) {
|
||||||
Ref<Animation> anim = tree->get_animation(E);
|
Ref<Animation> anim = tree->get_animation(E);
|
||||||
for (int i = 0; i < anim->get_track_count(); i++) {
|
for (int i = 0; i < anim->get_track_count(); i++) {
|
||||||
String track_path = anim->track_get_path(i);
|
String track_path = String(anim->track_get_path(i));
|
||||||
paths.insert(track_path);
|
paths.insert(track_path);
|
||||||
|
|
||||||
String track_type_name;
|
String track_type_name;
|
||||||
@ -890,8 +890,8 @@ bool AnimationNodeBlendTreeEditor::_update_filters(const Ref<AnimationNode> &ano
|
|||||||
if (ti) {
|
if (ti) {
|
||||||
//just a node, not a property track
|
//just a node, not a property track
|
||||||
String types_text = "[";
|
String types_text = "[";
|
||||||
if (types.has(path)) {
|
if (types.has(String(path))) {
|
||||||
RBSet<String>::Iterator F = types[path].begin();
|
RBSet<String>::Iterator F = types[String(path)].begin();
|
||||||
types_text += *F;
|
types_text += *F;
|
||||||
while (F) {
|
while (F) {
|
||||||
types_text += " / " + *F;
|
types_text += " / " + *F;
|
||||||
|
@ -809,7 +809,7 @@ void AnimationLibraryEditor::_save_mixer_lib_folding(TreeItem *p_item) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get unique identifier for this scene+mixer combination
|
// Get unique identifier for this scene+mixer combination
|
||||||
String md = (mixer->get_tree()->get_edited_scene_root()->get_scene_file_path() + mixer->get_path()).md5_text();
|
String md = (mixer->get_tree()->get_edited_scene_root()->get_scene_file_path() + String(mixer->get_path())).md5_text();
|
||||||
|
|
||||||
PackedStringArray collapsed_lib_names;
|
PackedStringArray collapsed_lib_names;
|
||||||
PackedStringArray collapsed_lib_ids;
|
PackedStringArray collapsed_lib_ids;
|
||||||
@ -886,7 +886,7 @@ Vector<uint64_t> AnimationLibraryEditor::_load_mixer_libs_folding() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get unique identifier for this scene+mixer combination
|
// Get unique identifier for this scene+mixer combination
|
||||||
String md = (mixer->get_tree()->get_edited_scene_root()->get_scene_file_path() + mixer->get_path()).md5_text();
|
String md = (mixer->get_tree()->get_edited_scene_root()->get_scene_file_path() + String(mixer->get_path())).md5_text();
|
||||||
|
|
||||||
Vector<uint64_t> collapsed_lib_ids;
|
Vector<uint64_t> collapsed_lib_ids;
|
||||||
|
|
||||||
|
@ -1963,7 +1963,7 @@ bool AnimationPlayerEditor::_validate_tracks(const Ref<Animation> p_anim) {
|
|||||||
for (int j = 0; j < key_len; j++) {
|
for (int j = 0; j < key_len; j++) {
|
||||||
Quaternion q;
|
Quaternion q;
|
||||||
p_anim->rotation_track_get_key(i, j, &q);
|
p_anim->rotation_track_get_key(i, j, &q);
|
||||||
ERR_BREAK_EDMSG(!q.is_normalized(), "AnimationPlayer: '" + player->get_name() + "', Animation: '" + player->get_current_animation() + "', 3D Rotation Track: '" + p_anim->track_get_path(i) + "' contains unnormalized Quaternion key.");
|
ERR_BREAK_EDMSG(!q.is_normalized(), "AnimationPlayer: '" + player->get_name() + "', Animation: '" + player->get_current_animation() + "', 3D Rotation Track: '" + String(p_anim->track_get_path(i)) + "' contains unnormalized Quaternion key.");
|
||||||
}
|
}
|
||||||
} else if (ttype == Animation::TYPE_VALUE) {
|
} else if (ttype == Animation::TYPE_VALUE) {
|
||||||
int key_len = p_anim->track_get_key_count(i);
|
int key_len = p_anim->track_get_key_count(i);
|
||||||
@ -1976,7 +1976,7 @@ bool AnimationPlayerEditor::_validate_tracks(const Ref<Animation> p_anim) {
|
|||||||
Quaternion q = Quaternion(p_anim->track_get_key_value(i, j));
|
Quaternion q = Quaternion(p_anim->track_get_key_value(i, j));
|
||||||
if (!q.is_normalized()) {
|
if (!q.is_normalized()) {
|
||||||
is_valid = false;
|
is_valid = false;
|
||||||
ERR_BREAK_EDMSG(true, "AnimationPlayer: '" + player->get_name() + "', Animation: '" + player->get_current_animation() + "', Value Track: '" + p_anim->track_get_path(i) + "' contains unnormalized Quaternion key.");
|
ERR_BREAK_EDMSG(true, "AnimationPlayer: '" + player->get_name() + "', Animation: '" + player->get_current_animation() + "', Value Track: '" + String(p_anim->track_get_path(i)) + "' contains unnormalized Quaternion key.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
@ -1985,7 +1985,7 @@ bool AnimationPlayerEditor::_validate_tracks(const Ref<Animation> p_anim) {
|
|||||||
Transform3D t = Transform3D(p_anim->track_get_key_value(i, j));
|
Transform3D t = Transform3D(p_anim->track_get_key_value(i, j));
|
||||||
if (!t.basis.orthonormalized().is_rotation()) {
|
if (!t.basis.orthonormalized().is_rotation()) {
|
||||||
is_valid = false;
|
is_valid = false;
|
||||||
ERR_BREAK_EDMSG(true, "AnimationPlayer: '" + player->get_name() + "', Animation: '" + player->get_current_animation() + "', Value Track: '" + p_anim->track_get_path(i) + "' contains corrupted basis (some axes are too close other axis or scaled by zero) Transform3D key.");
|
ERR_BREAK_EDMSG(true, "AnimationPlayer: '" + player->get_name() + "', Animation: '" + player->get_current_animation() + "', Value Track: '" + String(p_anim->track_get_path(i)) + "' contains corrupted basis (some axes are too close other axis or scaled by zero) Transform3D key.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
@ -2436,7 +2436,7 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) {
|
|||||||
CanvasItem *item = selection_results[i].item;
|
CanvasItem *item = selection_results[i].item;
|
||||||
|
|
||||||
Ref<Texture2D> icon = EditorNode::get_singleton()->get_object_icon(item, "Node");
|
Ref<Texture2D> icon = EditorNode::get_singleton()->get_object_icon(item, "Node");
|
||||||
String node_path = "/" + root_name + "/" + root_path.rel_path_to(item->get_path());
|
String node_path = "/" + root_name + "/" + String(root_path.rel_path_to(item->get_path()));
|
||||||
|
|
||||||
int locked = 0;
|
int locked = 0;
|
||||||
if (_is_node_locked(item)) {
|
if (_is_node_locked(item)) {
|
||||||
@ -2503,7 +2503,7 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) {
|
|||||||
String *paths_write = paths.ptrw();
|
String *paths_write = paths.ptrw();
|
||||||
|
|
||||||
for (int i = 0; i < paths.size(); i++) {
|
for (int i = 0; i < paths.size(); i++) {
|
||||||
paths_write[i] = selection_results[i].item->get_path();
|
paths_write[i] = String(selection_results[i].item->get_path());
|
||||||
}
|
}
|
||||||
EditorContextMenuPluginManager::get_singleton()->add_options_from_plugins(add_node_menu, EditorContextMenuPlugin::CONTEXT_SLOT_2D_EDITOR, paths);
|
EditorContextMenuPluginManager::get_singleton()->add_options_from_plugins(add_node_menu, EditorContextMenuPlugin::CONTEXT_SLOT_2D_EDITOR, paths);
|
||||||
}
|
}
|
||||||
|
@ -546,7 +546,7 @@ void MeshInstance3DEditor::_create_outline_mesh() {
|
|||||||
Node *skeleton = node->get_node_or_null(node->get_skeleton_path());
|
Node *skeleton = node->get_node_or_null(node->get_skeleton_path());
|
||||||
if (skeleton && node->get_skin().is_valid()) {
|
if (skeleton && node->get_skin().is_valid()) {
|
||||||
mi->set_skin(node->get_skin());
|
mi->set_skin(node->get_skin());
|
||||||
mi->set_skeleton_path("../" + node->get_path_to(skeleton));
|
mi->set_skeleton_path("../" + String(node->get_path_to(skeleton)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Node *owner = get_tree()->get_edited_scene_root();
|
Node *owner = get_tree()->get_edited_scene_root();
|
||||||
|
@ -222,9 +222,9 @@ void MultiMeshEditor::_browsed(const NodePath &p_path) {
|
|||||||
NodePath path = node->get_path_to(get_node(p_path));
|
NodePath path = node->get_path_to(get_node(p_path));
|
||||||
|
|
||||||
if (browsing_source) {
|
if (browsing_source) {
|
||||||
mesh_source->set_text(path);
|
mesh_source->set_text(String(path));
|
||||||
} else {
|
} else {
|
||||||
surface_source->set_text(path);
|
surface_source->set_text(String(path));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -425,7 +425,9 @@ void ViewportRotationControl::_process_click(int p_index, Vector2 p_position, bo
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ViewportRotationControl::_process_drag(Ref<InputEventWithModifiers> p_event, int p_index, Vector2 p_position, Vector2 p_relative_position) {
|
void ViewportRotationControl::_process_drag(Ref<InputEventWithModifiers> p_event, int p_index, Vector2 p_position, Vector2 p_relative_position) {
|
||||||
if (orbiting_index == p_index && gizmo_activated) {
|
Point2 mouse_pos = get_local_mouse_position();
|
||||||
|
const bool movement_threshold_passed = original_mouse_pos.distance_to(mouse_pos) > 4 * EDSCALE;
|
||||||
|
if (orbiting_index == p_index && gizmo_activated && movement_threshold_passed) {
|
||||||
if (Input::get_singleton()->get_mouse_mode() == Input::MOUSE_MODE_VISIBLE) {
|
if (Input::get_singleton()->get_mouse_mode() == Input::MOUSE_MODE_VISIBLE) {
|
||||||
Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_CAPTURED);
|
Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_CAPTURED);
|
||||||
orbiting_mouse_start = p_position;
|
orbiting_mouse_start = p_position;
|
||||||
@ -460,6 +462,7 @@ void ViewportRotationControl::gui_input(const Ref<InputEvent> &p_event) {
|
|||||||
_process_click(100, mb->get_position(), mb->is_pressed());
|
_process_click(100, mb->get_position(), mb->is_pressed());
|
||||||
if (mb->is_pressed()) {
|
if (mb->is_pressed()) {
|
||||||
gizmo_activated = true;
|
gizmo_activated = true;
|
||||||
|
original_mouse_pos = get_local_mouse_position();
|
||||||
grab_focus();
|
grab_focus();
|
||||||
}
|
}
|
||||||
} else if (mb->get_button_index() == MouseButton::RIGHT) {
|
} else if (mb->get_button_index() == MouseButton::RIGHT) {
|
||||||
@ -1631,7 +1634,7 @@ void Node3DEditorViewport::_list_select(Ref<InputEventMouseButton> b) {
|
|||||||
|
|
||||||
Ref<Texture2D> icon = EditorNode::get_singleton()->get_object_icon(spat, "Node");
|
Ref<Texture2D> icon = EditorNode::get_singleton()->get_object_icon(spat, "Node");
|
||||||
|
|
||||||
String node_path = "/" + root_name + "/" + root_path.rel_path_to(spat->get_path());
|
String node_path = "/" + root_name + "/" + String(root_path.rel_path_to(spat->get_path()));
|
||||||
|
|
||||||
int locked = 0;
|
int locked = 0;
|
||||||
if (_is_node_locked(spat)) {
|
if (_is_node_locked(spat)) {
|
||||||
@ -1985,7 +1988,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
|
|||||||
|
|
||||||
surface->queue_redraw();
|
surface->queue_redraw();
|
||||||
} else {
|
} else {
|
||||||
if (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_RULER) {
|
if (ruler->is_inside_tree()) {
|
||||||
EditorNode::get_singleton()->get_scene_root()->remove_child(ruler);
|
EditorNode::get_singleton()->get_scene_root()->remove_child(ruler);
|
||||||
ruler_start_point->set_visible(false);
|
ruler_start_point->set_visible(false);
|
||||||
ruler_end_point->set_visible(false);
|
ruler_end_point->set_visible(false);
|
||||||
|
@ -82,6 +82,7 @@ class ViewportRotationControl : public Control {
|
|||||||
Vector<Color> axis_colors;
|
Vector<Color> axis_colors;
|
||||||
Vector<int> axis_menu_options;
|
Vector<int> axis_menu_options;
|
||||||
Vector2i orbiting_mouse_start;
|
Vector2i orbiting_mouse_start;
|
||||||
|
Point2 original_mouse_pos;
|
||||||
int orbiting_index = -1;
|
int orbiting_index = -1;
|
||||||
int focused_axis = -2;
|
int focused_axis = -2;
|
||||||
bool gizmo_activated = false;
|
bool gizmo_activated = false;
|
||||||
|
@ -57,7 +57,7 @@ Error PackedSceneEditorTranslationParserPlugin::parse_file(const String &p_path,
|
|||||||
|
|
||||||
for (int i = 0; i < state->get_node_count(); i++) {
|
for (int i = 0; i < state->get_node_count(); i++) {
|
||||||
String node_type = state->get_node_type(i);
|
String node_type = state->get_node_type(i);
|
||||||
String parent_path = state->get_node_path(i, true);
|
String parent_path = String(state->get_node_path(i, true));
|
||||||
|
|
||||||
// Handle instanced scenes.
|
// Handle instanced scenes.
|
||||||
if (node_type.is_empty()) {
|
if (node_type.is_empty()) {
|
||||||
@ -83,7 +83,7 @@ Error PackedSceneEditorTranslationParserPlugin::parse_file(const String &p_path,
|
|||||||
auto_translate_mode_found = true;
|
auto_translate_mode_found = true;
|
||||||
|
|
||||||
int idx_last = atr_owners.size() - 1;
|
int idx_last = atr_owners.size() - 1;
|
||||||
if (idx_last > 0 && !parent_path.begins_with(atr_owners[idx_last].first)) {
|
if (idx_last > 0 && !parent_path.begins_with(String(atr_owners[idx_last].first))) {
|
||||||
// Exit from the current owner nesting into the previous one.
|
// Exit from the current owner nesting into the previous one.
|
||||||
atr_owners.remove_at(idx_last);
|
atr_owners.remove_at(idx_last);
|
||||||
}
|
}
|
||||||
@ -106,7 +106,7 @@ Error PackedSceneEditorTranslationParserPlugin::parse_file(const String &p_path,
|
|||||||
// If `auto_translate_mode` wasn't found, that means it is set to its default value (`AUTO_TRANSLATE_MODE_INHERIT`).
|
// If `auto_translate_mode` wasn't found, that means it is set to its default value (`AUTO_TRANSLATE_MODE_INHERIT`).
|
||||||
if (!auto_translate_mode_found) {
|
if (!auto_translate_mode_found) {
|
||||||
int idx_last = atr_owners.size() - 1;
|
int idx_last = atr_owners.size() - 1;
|
||||||
if (idx_last > 0 && parent_path.begins_with(atr_owners[idx_last].first)) {
|
if (idx_last > 0 && parent_path.begins_with(String(atr_owners[idx_last].first))) {
|
||||||
auto_translating = atr_owners[idx_last].second;
|
auto_translating = atr_owners[idx_last].second;
|
||||||
} else {
|
} else {
|
||||||
atr_owners.push_back(Pair(state->get_node_path(i), true));
|
atr_owners.push_back(Pair(state->get_node_path(i), true));
|
||||||
@ -130,7 +130,7 @@ Error PackedSceneEditorTranslationParserPlugin::parse_file(const String &p_path,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (node_type == "TabContainer") {
|
if (node_type == "TabContainer") {
|
||||||
tabcontainer_paths.push_back(state->get_node_path(i));
|
tabcontainer_paths.push_back(String(state->get_node_path(i)));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int j = 0; j < state->get_node_property_count(i); j++) {
|
for (int j = 0; j < state->get_node_property_count(i); j++) {
|
||||||
|
@ -166,7 +166,7 @@ void EditorPropertyRootMotion::_node_clear() {
|
|||||||
|
|
||||||
void EditorPropertyRootMotion::update_property() {
|
void EditorPropertyRootMotion::update_property() {
|
||||||
NodePath p = get_edited_property_value();
|
NodePath p = get_edited_property_value();
|
||||||
assign->set_tooltip_text(p);
|
assign->set_tooltip_text(String(p));
|
||||||
if (p == NodePath()) {
|
if (p == NodePath()) {
|
||||||
assign->set_button_icon(Ref<Texture2D>());
|
assign->set_button_icon(Ref<Texture2D>());
|
||||||
assign->set_text(TTR("Assign..."));
|
assign->set_text(TTR("Assign..."));
|
||||||
@ -175,7 +175,7 @@ void EditorPropertyRootMotion::update_property() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
assign->set_button_icon(Ref<Texture2D>());
|
assign->set_button_icon(Ref<Texture2D>());
|
||||||
assign->set_text(p);
|
assign->set_text(String(p));
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorPropertyRootMotion::setup(const NodePath &p_base_hint) {
|
void EditorPropertyRootMotion::setup(const NodePath &p_base_hint) {
|
||||||
|
@ -819,10 +819,12 @@ void ScriptTextEditor::_validate_script() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (errors.size() > 0) {
|
if (errors.size() > 0) {
|
||||||
// TRANSLATORS: Script error pointing to a line and column number.
|
const int line = errors.front()->get().line;
|
||||||
String error_text = vformat(TTR("Error at (%d, %d):"), errors.front()->get().line, errors.front()->get().column) + " " + errors.front()->get().message;
|
const int column = errors.front()->get().column;
|
||||||
|
const String message = errors.front()->get().message.replace("[", "[lb]");
|
||||||
|
const String error_text = vformat(TTR("Error at ([hint=Line %d, column %d]%d, %d[/hint]):"), line, column, line, column) + " " + message;
|
||||||
code_editor->set_error(error_text);
|
code_editor->set_error(error_text);
|
||||||
code_editor->set_error_pos(errors.front()->get().line - 1, errors.front()->get().column - 1);
|
code_editor->set_error_pos(line - 1, column - 1);
|
||||||
}
|
}
|
||||||
script_is_valid = false;
|
script_is_valid = false;
|
||||||
} else {
|
} else {
|
||||||
@ -861,8 +863,8 @@ void ScriptTextEditor::_update_warnings() {
|
|||||||
warnings_panel->push_table(1);
|
warnings_panel->push_table(1);
|
||||||
for (const Connection &connection : missing_connections) {
|
for (const Connection &connection : missing_connections) {
|
||||||
String base_path = base->get_name();
|
String base_path = base->get_name();
|
||||||
String source_path = base == connection.signal.get_object() ? base_path : base_path + "/" + base->get_path_to(Object::cast_to<Node>(connection.signal.get_object()));
|
String source_path = base == connection.signal.get_object() ? base_path : base_path + "/" + String(base->get_path_to(Object::cast_to<Node>(connection.signal.get_object())));
|
||||||
String target_path = base == connection.callable.get_object() ? base_path : base_path + "/" + base->get_path_to(Object::cast_to<Node>(connection.callable.get_object()));
|
String target_path = base == connection.callable.get_object() ? base_path : base_path + "/" + String(base->get_path_to(Object::cast_to<Node>(connection.callable.get_object())));
|
||||||
|
|
||||||
warnings_panel->push_cell();
|
warnings_panel->push_cell();
|
||||||
warnings_panel->push_color(warnings_panel->get_theme_color(SNAME("warning_color"), EditorStringName(Editor)));
|
warnings_panel->push_color(warnings_panel->get_theme_color(SNAME("warning_color"), EditorStringName(Editor)));
|
||||||
@ -2580,7 +2582,7 @@ void ScriptTextEditor::_make_context_menu(bool p_selection, bool p_color, bool p
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const PackedStringArray paths = { code_editor->get_text_editor()->get_path() };
|
const PackedStringArray paths = { String(code_editor->get_text_editor()->get_path()) };
|
||||||
EditorContextMenuPluginManager::get_singleton()->add_options_from_plugins(context_menu, EditorContextMenuPlugin::CONTEXT_SLOT_SCRIPT_EDITOR_CODE, paths);
|
EditorContextMenuPluginManager::get_singleton()->add_options_from_plugins(context_menu, EditorContextMenuPlugin::CONTEXT_SLOT_SCRIPT_EDITOR_CODE, paths);
|
||||||
|
|
||||||
const CodeEdit *tx = code_editor->get_text_editor();
|
const CodeEdit *tx = code_editor->get_text_editor();
|
||||||
|
@ -441,7 +441,7 @@ void Skeleton3DEditor::insert_keys(const bool p_all_bones) {
|
|||||||
|
|
||||||
int bone_len = skeleton->get_bone_count();
|
int bone_len = skeleton->get_bone_count();
|
||||||
Node *root = EditorNode::get_singleton()->get_tree()->get_root();
|
Node *root = EditorNode::get_singleton()->get_tree()->get_root();
|
||||||
String path = root->get_path_to(skeleton);
|
String path = String(root->get_path_to(skeleton));
|
||||||
|
|
||||||
AnimationTrackEditor *te = AnimationPlayerEditor::get_singleton()->get_track_editor();
|
AnimationTrackEditor *te = AnimationPlayerEditor::get_singleton()->get_track_editor();
|
||||||
te->make_insert_queue();
|
te->make_insert_queue();
|
||||||
|
@ -200,7 +200,7 @@ void TextEditor::_validate_script() {
|
|||||||
code_editor->set_error("");
|
code_editor->set_error("");
|
||||||
|
|
||||||
if (json_file->parse(te->get_text(), true) != OK) {
|
if (json_file->parse(te->get_text(), true) != OK) {
|
||||||
code_editor->set_error(json_file->get_error_message());
|
code_editor->set_error(json_file->get_error_message().replace("[", "[lb]"));
|
||||||
code_editor->set_error_pos(json_file->get_error_line(), 0);
|
code_editor->set_error_pos(json_file->get_error_line(), 0);
|
||||||
te->set_line_background_color(code_editor->get_error_pos().x, EDITOR_GET("text_editor/theme/highlighting/mark_color"));
|
te->set_line_background_color(code_editor->get_error_pos().x, EDITOR_GET("text_editor/theme/highlighting/mark_color"));
|
||||||
}
|
}
|
||||||
|
@ -515,28 +515,35 @@ void ShaderTextEditor::_validate_script() {
|
|||||||
set_error_count(0);
|
set_error_count(0);
|
||||||
|
|
||||||
if (last_compile_result != OK) {
|
if (last_compile_result != OK) {
|
||||||
//preprocessor error
|
// Preprocessor error.
|
||||||
ERR_FAIL_COND(err_positions.is_empty());
|
ERR_FAIL_COND(err_positions.is_empty());
|
||||||
|
|
||||||
String err_text = error_pp;
|
String err_text;
|
||||||
int err_line = err_positions.front()->get().line;
|
const int err_line = err_positions.front()->get().line;
|
||||||
if (err_positions.size() == 1) {
|
if (err_positions.size() == 1) {
|
||||||
// Error in main file
|
// Error in the main file.
|
||||||
err_text = "error(" + itos(err_line) + "): " + err_text;
|
const String message = error_pp.replace("[", "[lb]");
|
||||||
|
|
||||||
|
err_text = vformat(TTR("Error at line %d:"), err_line) + " " + message;
|
||||||
} else {
|
} else {
|
||||||
err_text = "error(" + itos(err_line) + ") in include " + err_positions.back()->get().file.get_file() + ":" + itos(err_positions.back()->get().line) + ": " + err_text;
|
// Error in an included file.
|
||||||
|
const String inc_file = err_positions.back()->get().file.get_file();
|
||||||
|
const int inc_line = err_positions.back()->get().line;
|
||||||
|
const String message = error_pp.replace("[", "[lb]");
|
||||||
|
|
||||||
|
err_text = vformat(TTR("Error at line %d in include %s:%d:"), err_line, inc_file, inc_line) + " " + message;
|
||||||
set_error_count(err_positions.size() - 1);
|
set_error_count(err_positions.size() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
set_error(err_text);
|
set_error(err_text);
|
||||||
set_error_pos(err_line - 1, 0);
|
set_error_pos(err_line - 1, 0);
|
||||||
|
|
||||||
for (int i = 0; i < get_text_editor()->get_line_count(); i++) {
|
for (int i = 0; i < get_text_editor()->get_line_count(); i++) {
|
||||||
get_text_editor()->set_line_background_color(i, Color(0, 0, 0, 0));
|
get_text_editor()->set_line_background_color(i, Color(0, 0, 0, 0));
|
||||||
}
|
}
|
||||||
get_text_editor()->set_line_background_color(err_line - 1, marked_line_color);
|
get_text_editor()->set_line_background_color(err_line - 1, marked_line_color);
|
||||||
|
|
||||||
set_warning_count(0);
|
set_warning_count(0);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
ShaderLanguage sl;
|
ShaderLanguage sl;
|
||||||
|
|
||||||
@ -579,21 +586,33 @@ void ShaderTextEditor::_validate_script() {
|
|||||||
last_compile_result = sl.compile(code, comp_info);
|
last_compile_result = sl.compile(code, comp_info);
|
||||||
|
|
||||||
if (last_compile_result != OK) {
|
if (last_compile_result != OK) {
|
||||||
|
Vector<ShaderLanguage::FilePosition> include_positions = sl.get_include_positions();
|
||||||
|
|
||||||
String err_text;
|
String err_text;
|
||||||
int err_line;
|
int err_line;
|
||||||
Vector<ShaderLanguage::FilePosition> include_positions = sl.get_include_positions();
|
|
||||||
if (include_positions.size() > 1) {
|
if (include_positions.size() > 1) {
|
||||||
//error is in an include
|
// Error in an included file.
|
||||||
err_line = include_positions[0].line;
|
err_line = include_positions[0].line;
|
||||||
err_text = "error(" + itos(err_line) + ") in include " + include_positions[include_positions.size() - 1].file + ":" + itos(include_positions[include_positions.size() - 1].line) + ": " + sl.get_error_text();
|
|
||||||
|
const String inc_file = include_positions[include_positions.size() - 1].file;
|
||||||
|
const int inc_line = include_positions[include_positions.size() - 1].line;
|
||||||
|
const String message = sl.get_error_text().replace("[", "[lb]");
|
||||||
|
|
||||||
|
err_text = vformat(TTR("Error at line %d in include %s:%d:"), err_line, inc_file, inc_line) + " " + message;
|
||||||
set_error_count(include_positions.size() - 1);
|
set_error_count(include_positions.size() - 1);
|
||||||
} else {
|
} else {
|
||||||
|
// Error in the main file.
|
||||||
err_line = sl.get_error_line();
|
err_line = sl.get_error_line();
|
||||||
err_text = "error(" + itos(err_line) + "): " + sl.get_error_text();
|
|
||||||
|
const String message = sl.get_error_text().replace("[", "[lb]");
|
||||||
|
|
||||||
|
err_text = vformat(TTR("Error at line %d:"), err_line) + " " + message;
|
||||||
set_error_count(0);
|
set_error_count(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
set_error(err_text);
|
set_error(err_text);
|
||||||
set_error_pos(err_line - 1, 0);
|
set_error_pos(err_line - 1, 0);
|
||||||
|
|
||||||
get_text_editor()->set_line_background_color(err_line - 1, marked_line_color);
|
get_text_editor()->set_line_background_color(err_line - 1, marked_line_color);
|
||||||
} else {
|
} else {
|
||||||
set_error("");
|
set_error("");
|
||||||
@ -624,9 +643,12 @@ void ShaderTextEditor::_update_warning_panel() {
|
|||||||
for (const ShaderWarning &w : warnings) {
|
for (const ShaderWarning &w : warnings) {
|
||||||
if (warning_count == 0) {
|
if (warning_count == 0) {
|
||||||
if (saved_treat_warning_as_errors) {
|
if (saved_treat_warning_as_errors) {
|
||||||
String error_text = "error(" + itos(w.get_line()) + "): " + w.get_message() + " " + TTR("Warnings should be fixed to prevent errors.");
|
const String message = (w.get_message() + " " + TTR("Warnings should be fixed to prevent errors.")).replace("[", "[lb]");
|
||||||
set_error_pos(w.get_line() - 1, 0);
|
const String error_text = vformat(TTR("Error at line %d:"), w.get_line()) + " " + message;
|
||||||
|
|
||||||
set_error(error_text);
|
set_error(error_text);
|
||||||
|
set_error_pos(w.get_line() - 1, 0);
|
||||||
|
|
||||||
get_text_editor()->set_line_background_color(w.get_line() - 1, marked_line_color);
|
get_text_editor()->set_line_background_color(w.get_line() - 1, marked_line_color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1218,7 +1218,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
|
|||||||
if (node) {
|
if (node) {
|
||||||
Node *root = EditorNode::get_singleton()->get_edited_scene();
|
Node *root = EditorNode::get_singleton()->get_edited_scene();
|
||||||
NodePath path = root->get_path().rel_path_to(node->get_path());
|
NodePath path = root->get_path().rel_path_to(node->get_path());
|
||||||
DisplayServer::get_singleton()->clipboard_set(path);
|
DisplayServer::get_singleton()->clipboard_set(String(path));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
@ -1998,7 +1998,7 @@ bool SceneTreeDock::_update_node_path(Node *p_root_node, NodePath &r_node_path,
|
|||||||
if (found_root_path) {
|
if (found_root_path) {
|
||||||
NodePath root_path_new = found_root_path->value;
|
NodePath root_path_new = found_root_path->value;
|
||||||
if (!root_path_new.is_empty()) {
|
if (!root_path_new.is_empty()) {
|
||||||
NodePath old_abs_path = NodePath(String(p_root_node->get_path()).path_join(r_node_path));
|
NodePath old_abs_path = NodePath(String(p_root_node->get_path()).path_join(String(r_node_path)));
|
||||||
old_abs_path.simplify();
|
old_abs_path.simplify();
|
||||||
r_node_path = root_path_new.rel_path_to(old_abs_path);
|
r_node_path = root_path_new.rel_path_to(old_abs_path);
|
||||||
}
|
}
|
||||||
@ -2463,7 +2463,7 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V
|
|||||||
NodePath fixed_node_path = NodePath(fixed_new_names, true);
|
NodePath fixed_node_path = NodePath(fixed_new_names, true);
|
||||||
path_renames[node] = fixed_node_path;
|
path_renames[node] = fixed_node_path;
|
||||||
} else {
|
} else {
|
||||||
ERR_PRINT("Internal error. Can't find renamed path for node '" + node->get_path() + "'");
|
ERR_PRINT("Internal error. Can't find renamed path for node '" + String(node->get_path()) + "'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3970,7 +3970,7 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) {
|
|||||||
Vector<String> p_paths;
|
Vector<String> p_paths;
|
||||||
Node *root = EditorNode::get_singleton()->get_edited_scene();
|
Node *root = EditorNode::get_singleton()->get_edited_scene();
|
||||||
for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
|
for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
|
||||||
String node_path = root->get_path().rel_path_to(E->get()->get_path());
|
String node_path = String(root->get_path().rel_path_to(E->get()->get_path()));
|
||||||
p_paths.push_back(node_path);
|
p_paths.push_back(node_path);
|
||||||
}
|
}
|
||||||
EditorContextMenuPluginManager::get_singleton()->add_options_from_plugins(menu, EditorContextMenuPlugin::CONTEXT_SLOT_SCENE_TREE, p_paths);
|
EditorContextMenuPluginManager::get_singleton()->add_options_from_plugins(menu, EditorContextMenuPlugin::CONTEXT_SLOT_SCENE_TREE, p_paths);
|
||||||
|
@ -1798,7 +1798,7 @@ void FBXDocument::_import_animation(Ref<FBXState> p_state, AnimationPlayer *p_an
|
|||||||
const Skeleton3D *sk = p_state->skeletons[fbx_node->skeleton]->godot_skeleton;
|
const Skeleton3D *sk = p_state->skeletons[fbx_node->skeleton]->godot_skeleton;
|
||||||
ERR_FAIL_NULL(sk);
|
ERR_FAIL_NULL(sk);
|
||||||
|
|
||||||
const String path = p_animation_player->get_parent()->get_path_to(sk);
|
const String path = String(p_animation_player->get_parent()->get_path_to(sk));
|
||||||
const String bone = fbx_node->get_name();
|
const String bone = fbx_node->get_name();
|
||||||
transform_node_path = path + ":" + bone;
|
transform_node_path = path + ":" + bone;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1585,9 +1585,21 @@ void GDScriptByteCodeGenerator::write_for_range_assignment(const Address &p_from
|
|||||||
const Address &range_step = for_range_step_variables.back()->get();
|
const Address &range_step = for_range_step_variables.back()->get();
|
||||||
|
|
||||||
// Assign range args.
|
// Assign range args.
|
||||||
write_assign(range_from, p_from);
|
if (range_from.type == p_from.type) {
|
||||||
write_assign(range_to, p_to);
|
write_assign(range_from, p_from);
|
||||||
write_assign(range_step, p_step);
|
} else {
|
||||||
|
write_assign_with_conversion(range_from, p_from);
|
||||||
|
}
|
||||||
|
if (range_to.type == p_to.type) {
|
||||||
|
write_assign(range_to, p_to);
|
||||||
|
} else {
|
||||||
|
write_assign_with_conversion(range_to, p_to);
|
||||||
|
}
|
||||||
|
if (range_step.type == p_step.type) {
|
||||||
|
write_assign(range_step, p_step);
|
||||||
|
} else {
|
||||||
|
write_assign_with_conversion(range_step, p_step);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GDScriptByteCodeGenerator::write_for(const Address &p_variable, bool p_use_conversion, bool p_is_range) {
|
void GDScriptByteCodeGenerator::write_for(const Address &p_variable, bool p_use_conversion, bool p_is_range) {
|
||||||
|
@ -234,6 +234,19 @@ public:
|
|||||||
|
|
||||||
GDScriptDataType() = default;
|
GDScriptDataType() = default;
|
||||||
|
|
||||||
|
bool operator==(const GDScriptDataType &p_other) const {
|
||||||
|
return kind == p_other.kind &&
|
||||||
|
has_type == p_other.has_type &&
|
||||||
|
builtin_type == p_other.builtin_type &&
|
||||||
|
native_type == p_other.native_type &&
|
||||||
|
(script_type == p_other.script_type || script_type_ref == p_other.script_type_ref) &&
|
||||||
|
container_element_types == p_other.container_element_types;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const GDScriptDataType &p_other) const {
|
||||||
|
return !(*this == p_other);
|
||||||
|
}
|
||||||
|
|
||||||
void operator=(const GDScriptDataType &p_other) {
|
void operator=(const GDScriptDataType &p_other) {
|
||||||
kind = p_other.kind;
|
kind = p_other.kind;
|
||||||
has_type = p_other.has_type;
|
has_type = p_other.has_type;
|
||||||
|
@ -177,7 +177,7 @@ String GDScriptFunction::_get_call_error(const String &p_where, const Variant **
|
|||||||
return "Invalid type in " + p_where + ". Cannot convert argument " + itos(p_err.argument + 1) + " from " + Variant::get_type_name(p_argptrs[p_err.argument]->get_type()) + " to " + Variant::get_type_name(Variant::Type(p_err.expected)) + ".";
|
return "Invalid type in " + p_where + ". Cannot convert argument " + itos(p_err.argument + 1) + " from " + Variant::get_type_name(p_argptrs[p_err.argument]->get_type()) + " to " + Variant::get_type_name(Variant::Type(p_err.expected)) + ".";
|
||||||
case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
|
case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
|
||||||
case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
|
case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
|
||||||
return "Invalid call to " + p_where + ". Expected " + itos(p_err.expected) + " arguments.";
|
return "Invalid call to " + p_where + ". Expected " + itos(p_err.expected) + " argument(s).";
|
||||||
case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL:
|
case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL:
|
||||||
return "Attempt to call " + p_where + " on a null instance.";
|
return "Attempt to call " + p_where + " on a null instance.";
|
||||||
case Callable::CallError::CALL_ERROR_METHOD_NOT_CONST:
|
case Callable::CallError::CALL_ERROR_METHOD_NOT_CONST:
|
||||||
|
@ -625,7 +625,7 @@ Node *GDScriptWorkspace::_get_owner_scene_node(String p_path) {
|
|||||||
|
|
||||||
for (const String &owner : owners) {
|
for (const String &owner : owners) {
|
||||||
NodePath owner_path = owner;
|
NodePath owner_path = owner;
|
||||||
Ref<Resource> owner_res = ResourceLoader::load(owner_path);
|
Ref<Resource> owner_res = ResourceLoader::load(String(owner_path));
|
||||||
if (Object::cast_to<PackedScene>(owner_res.ptr())) {
|
if (Object::cast_to<PackedScene>(owner_res.ptr())) {
|
||||||
Ref<PackedScene> owner_packed_scene = Ref<PackedScene>(Object::cast_to<PackedScene>(*owner_res));
|
Ref<PackedScene> owner_packed_scene = Ref<PackedScene>(Object::cast_to<PackedScene>(*owner_res));
|
||||||
owner_scene_node = owner_packed_scene->instantiate();
|
owner_scene_node = owner_packed_scene->instantiate();
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
# GH-83293
|
|
||||||
|
|
||||||
func test():
|
func test():
|
||||||
|
# GH-83293
|
||||||
for x in range(1 << 31, (1 << 31) + 3):
|
for x in range(1 << 31, (1 << 31) + 3):
|
||||||
print(x)
|
print(x)
|
||||||
for x in range(1 << 62, (1 << 62) + 3):
|
for x in range(1 << 62, (1 << 62) + 3):
|
||||||
print(x)
|
print(x)
|
||||||
|
|
||||||
|
# GH-107392
|
||||||
|
var n = 1.0
|
||||||
|
for x in range(n):
|
||||||
|
print(x)
|
@ -5,3 +5,4 @@ GDTEST_OK
|
|||||||
4611686018427387904
|
4611686018427387904
|
||||||
4611686018427387905
|
4611686018427387905
|
||||||
4611686018427387906
|
4611686018427387906
|
||||||
|
0
|
@ -7051,7 +7051,7 @@ Ref<GLTFObjectModelProperty> GLTFDocument::export_object_model_property(Ref<GLTF
|
|||||||
Ref<GLTFObjectModelProperty> ret;
|
Ref<GLTFObjectModelProperty> ret;
|
||||||
const Object *target_object = p_godot_node;
|
const Object *target_object = p_godot_node;
|
||||||
const Vector<StringName> subpath = p_node_path.get_subnames();
|
const Vector<StringName> subpath = p_node_path.get_subnames();
|
||||||
ERR_FAIL_COND_V_MSG(subpath.is_empty(), ret, "glTF: Cannot export empty property. No property was specified in the NodePath: " + p_node_path);
|
ERR_FAIL_COND_V_MSG(subpath.is_empty(), ret, "glTF: Cannot export empty property. No property was specified in the NodePath: " + String(p_node_path));
|
||||||
int target_prop_depth = 0;
|
int target_prop_depth = 0;
|
||||||
for (StringName subname : subpath) {
|
for (StringName subname : subpath) {
|
||||||
Variant target_property = target_object->get(subname);
|
Variant target_property = target_object->get(subname);
|
||||||
@ -7282,7 +7282,7 @@ void GLTFDocument::_import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_
|
|||||||
const Skeleton3D *sk = p_state->skeletons[gltf_node->skeleton]->godot_skeleton;
|
const Skeleton3D *sk = p_state->skeletons[gltf_node->skeleton]->godot_skeleton;
|
||||||
ERR_FAIL_NULL(sk);
|
ERR_FAIL_NULL(sk);
|
||||||
|
|
||||||
const String path = p_animation_player->get_parent()->get_path_to(sk);
|
const String path = String(p_animation_player->get_parent()->get_path_to(sk));
|
||||||
const String bone = gltf_node->get_name();
|
const String bone = gltf_node->get_name();
|
||||||
transform_node_path = path + ":" + bone;
|
transform_node_path = path + ":" + bone;
|
||||||
} else {
|
} else {
|
||||||
|
@ -51,7 +51,7 @@ void _add_nodes_suggestions(const Node *p_base, const Node *p_node, PackedString
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String path_relative_to_orig = p_base->get_path_to(p_node);
|
String path_relative_to_orig = String(p_base->get_path_to(p_node));
|
||||||
|
|
||||||
r_suggestions.push_back(quoted(path_relative_to_orig));
|
r_suggestions.push_back(quoted(path_relative_to_orig));
|
||||||
|
|
||||||
|
@ -180,7 +180,7 @@ namespace Godot
|
|||||||
/// static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
|
/// static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
|
||||||
/// {
|
/// {
|
||||||
/// if (args.Count != 1)
|
/// if (args.Count != 1)
|
||||||
/// throw new ArgumentException($"Callable expected {1} arguments but received {args.Count}.");
|
/// throw new ArgumentException($"Callable expected {1} argument but received {args.Count}.");
|
||||||
///
|
///
|
||||||
/// TResult res = ((Func<int, string>)delegateObj)(
|
/// TResult res = ((Func<int, string>)delegateObj)(
|
||||||
/// VariantConversionCallbacks.GetToManagedCallback<int>()(args[0])
|
/// VariantConversionCallbacks.GetToManagedCallback<int>()(args[0])
|
||||||
|
@ -19,7 +19,7 @@ public readonly partial struct Callable
|
|||||||
{
|
{
|
||||||
throw new ArgumentException(
|
throw new ArgumentException(
|
||||||
"Invalid argument count for invoking callable." +
|
"Invalid argument count for invoking callable." +
|
||||||
$" Expected {countExpected} arguments, received {countReceived}.",
|
$" Expected {countExpected} argument(s), received {countReceived}.",
|
||||||
paramName);
|
paramName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -211,7 +211,7 @@ namespace Godot.NativeInterop
|
|||||||
}
|
}
|
||||||
case godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_TOO_MANY_ARGUMENTS:
|
case godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_TOO_MANY_ARGUMENTS:
|
||||||
case godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_TOO_FEW_ARGUMENTS:
|
case godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_TOO_FEW_ARGUMENTS:
|
||||||
return $"Invalid call to {where}. Expected {error.Expected} arguments.";
|
return $"Invalid call to {where}. Expected {error.Expected} argument(s).";
|
||||||
case godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INVALID_METHOD:
|
case godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INVALID_METHOD:
|
||||||
return $"Invalid call. Nonexistent {where}.";
|
return $"Invalid call. Nonexistent {where}.";
|
||||||
case godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INSTANCE_IS_NULL:
|
case godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INSTANCE_IS_NULL:
|
||||||
|
@ -340,7 +340,7 @@ void ReplicationEditor::_drop_data_fw(const Point2 &p_point, const Variant &p_da
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String path = root->get_path_to(node);
|
String path = String(root->get_path_to(node));
|
||||||
path += ":" + String(d["property"]);
|
path += ":" + String(d["property"]);
|
||||||
|
|
||||||
_add_sync_property(path);
|
_add_sync_property(path);
|
||||||
@ -390,7 +390,7 @@ void ReplicationEditor::_add_pressed() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_add_sync_property(path);
|
_add_sync_property(String(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReplicationEditor::_np_text_submitted(const String &p_newtext) {
|
void ReplicationEditor::_np_text_submitted(const String &p_newtext) {
|
||||||
|
@ -195,7 +195,7 @@ void MultiplayerDebugger::RPCProfiler::init_node(const ObjectID p_node) {
|
|||||||
}
|
}
|
||||||
rpc_node_data.insert(p_node, RPCNodeInfo());
|
rpc_node_data.insert(p_node, RPCNodeInfo());
|
||||||
rpc_node_data[p_node].node = p_node;
|
rpc_node_data[p_node].node = p_node;
|
||||||
rpc_node_data[p_node].node_path = ObjectDB::get_instance<Node>(p_node)->get_path();
|
rpc_node_data[p_node].node_path = String(ObjectDB::get_instance<Node>(p_node)->get_path());
|
||||||
}
|
}
|
||||||
|
|
||||||
void MultiplayerDebugger::RPCProfiler::toggle(bool p_enable, const Array &p_opts) {
|
void MultiplayerDebugger::RPCProfiler::toggle(bool p_enable, const Array &p_opts) {
|
||||||
|
@ -116,7 +116,7 @@ void SceneCacheInterface::process_simplify_path(int p_from, const uint8_t *p_pac
|
|||||||
ERR_FAIL_NULL(node);
|
ERR_FAIL_NULL(node);
|
||||||
const bool valid_rpc_checksum = multiplayer->get_rpc_md5(node) == methods_md5;
|
const bool valid_rpc_checksum = multiplayer->get_rpc_md5(node) == methods_md5;
|
||||||
if (valid_rpc_checksum == false) {
|
if (valid_rpc_checksum == false) {
|
||||||
ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + path);
|
ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + String(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
peers_info[p_from].recv_nodes.insert(id, RecvNode(node->get_instance_id(), path));
|
peers_info[p_from].recv_nodes.insert(id, RecvNode(node->get_instance_id(), path));
|
||||||
@ -154,7 +154,7 @@ void SceneCacheInterface::process_confirm_path(int p_from, const uint8_t *p_pack
|
|||||||
if (valid_rpc_checksum == false) {
|
if (valid_rpc_checksum == false) {
|
||||||
const Node *node = ObjectDB::get_instance<Node>(*oid);
|
const Node *node = ObjectDB::get_instance<Node>(*oid);
|
||||||
ERR_FAIL_NULL(node); // Bug.
|
ERR_FAIL_NULL(node); // Bug.
|
||||||
ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + node->get_path());
|
ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + String(node->get_path()));
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeCache *cache = nodes_cache.getptr(*oid);
|
NodeCache *cache = nodes_cache.getptr(*oid);
|
||||||
|
@ -245,7 +245,7 @@ void SceneRPCInterface::_process_rpc(Node *p_node, const uint16_t p_rpc_method_i
|
|||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ERR_FAIL_COND_MSG(!can_call, "RPC '" + String(config.name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)config.rpc_mode) + ", authority is " + itos(p_node->get_multiplayer_authority()) + ".");
|
ERR_FAIL_COND_MSG(!can_call, "RPC '" + String(config.name) + "' is not allowed on node " + String(p_node->get_path()) + " from: " + itos(p_from) + ". Mode is " + itos((int)config.rpc_mode) + ", authority is " + itos(p_node->get_multiplayer_authority()) + ".");
|
||||||
|
|
||||||
int argc = 0;
|
int argc = 0;
|
||||||
|
|
||||||
|
@ -457,6 +457,19 @@ uint32_t GodotNavigationServer2D::region_get_iteration_id(RID p_region) const {
|
|||||||
return region->get_iteration_id();
|
return region->get_iteration_id();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
COMMAND_2(region_set_use_async_iterations, RID, p_region, bool, p_enabled) {
|
||||||
|
NavRegion2D *region = region_owner.get_or_null(p_region);
|
||||||
|
ERR_FAIL_NULL(region);
|
||||||
|
region->set_use_async_iterations(p_enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GodotNavigationServer2D::region_get_use_async_iterations(RID p_region) const {
|
||||||
|
NavRegion2D *region = region_owner.get_or_null(p_region);
|
||||||
|
ERR_FAIL_NULL_V(region, false);
|
||||||
|
|
||||||
|
return region->get_use_async_iterations();
|
||||||
|
}
|
||||||
|
|
||||||
COMMAND_2(region_set_enabled, RID, p_region, bool, p_enabled) {
|
COMMAND_2(region_set_enabled, RID, p_region, bool, p_enabled) {
|
||||||
NavRegion2D *region = region_owner.get_or_null(p_region);
|
NavRegion2D *region = region_owner.get_or_null(p_region);
|
||||||
ERR_FAIL_NULL(region);
|
ERR_FAIL_NULL(region);
|
||||||
@ -581,7 +594,7 @@ void GodotNavigationServer2D::region_set_navigation_polygon(RID p_region, Ref<Na
|
|||||||
NavRegion2D *region = region_owner.get_or_null(p_region);
|
NavRegion2D *region = region_owner.get_or_null(p_region);
|
||||||
ERR_FAIL_NULL(region);
|
ERR_FAIL_NULL(region);
|
||||||
|
|
||||||
region->set_navigation_polygon(p_navigation_polygon);
|
region->set_navigation_mesh(p_navigation_polygon);
|
||||||
}
|
}
|
||||||
|
|
||||||
int GodotNavigationServer2D::region_get_connections_count(RID p_region) const {
|
int GodotNavigationServer2D::region_get_connections_count(RID p_region) const {
|
||||||
|
@ -146,6 +146,9 @@ public:
|
|||||||
virtual RID region_create() override;
|
virtual RID region_create() override;
|
||||||
virtual uint32_t region_get_iteration_id(RID p_region) const override;
|
virtual uint32_t region_get_iteration_id(RID p_region) const override;
|
||||||
|
|
||||||
|
COMMAND_2(region_set_use_async_iterations, RID, p_region, bool, p_enabled);
|
||||||
|
virtual bool region_get_use_async_iterations(RID p_region) const override;
|
||||||
|
|
||||||
COMMAND_2(region_set_enabled, RID, p_region, bool, p_enabled);
|
COMMAND_2(region_set_enabled, RID, p_region, bool, p_enabled);
|
||||||
virtual bool region_get_enabled(RID p_region) const override;
|
virtual bool region_get_enabled(RID p_region) const override;
|
||||||
|
|
||||||
|
@ -32,10 +32,13 @@
|
|||||||
|
|
||||||
#include "../nav_utils_2d.h"
|
#include "../nav_utils_2d.h"
|
||||||
|
|
||||||
|
#include "core/object/ref_counted.h"
|
||||||
#include "servers/navigation/navigation_utilities.h"
|
#include "servers/navigation/navigation_utilities.h"
|
||||||
|
|
||||||
struct NavBaseIteration2D {
|
class NavBaseIteration2D : public RefCounted {
|
||||||
uint32_t id = UINT32_MAX;
|
GDCLASS(NavBaseIteration2D, RefCounted);
|
||||||
|
|
||||||
|
public:
|
||||||
bool enabled = true;
|
bool enabled = true;
|
||||||
uint32_t navigation_layers = 1;
|
uint32_t navigation_layers = 1;
|
||||||
real_t enter_cost = 0.0;
|
real_t enter_cost = 0.0;
|
||||||
@ -45,6 +48,7 @@ struct NavBaseIteration2D {
|
|||||||
RID owner_rid;
|
RID owner_rid;
|
||||||
bool owner_use_edge_connections = false;
|
bool owner_use_edge_connections = false;
|
||||||
LocalVector<Nav2D::Polygon> navmesh_polygons;
|
LocalVector<Nav2D::Polygon> navmesh_polygons;
|
||||||
|
LocalVector<LocalVector<Nav2D::Connection>> internal_connections;
|
||||||
|
|
||||||
bool get_enabled() const { return enabled; }
|
bool get_enabled() const { return enabled; }
|
||||||
NavigationUtilities::PathSegmentType get_type() const { return owner_type; }
|
NavigationUtilities::PathSegmentType get_type() const { return owner_type; }
|
||||||
@ -55,4 +59,10 @@ struct NavBaseIteration2D {
|
|||||||
real_t get_travel_cost() const { return travel_cost; }
|
real_t get_travel_cost() const { return travel_cost; }
|
||||||
bool get_use_edge_connections() const { return owner_use_edge_connections; }
|
bool get_use_edge_connections() const { return owner_use_edge_connections; }
|
||||||
const LocalVector<Nav2D::Polygon> &get_navmesh_polygons() const { return navmesh_polygons; }
|
const LocalVector<Nav2D::Polygon> &get_navmesh_polygons() const { return navmesh_polygons; }
|
||||||
|
const LocalVector<LocalVector<Nav2D::Connection>> &get_internal_connections() const { return internal_connections; }
|
||||||
|
|
||||||
|
virtual ~NavBaseIteration2D() {
|
||||||
|
navmesh_polygons.clear();
|
||||||
|
internal_connections.clear();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
@ -76,26 +76,23 @@ void NavMapBuilder2D::_build_step_gather_region_polygons(NavMapIterationBuild2D
|
|||||||
PerformanceData &performance_data = r_build.performance_data;
|
PerformanceData &performance_data = r_build.performance_data;
|
||||||
NavMapIteration2D *map_iteration = r_build.map_iteration;
|
NavMapIteration2D *map_iteration = r_build.map_iteration;
|
||||||
|
|
||||||
LocalVector<NavRegionIteration2D> ®ions = map_iteration->region_iterations;
|
const LocalVector<Ref<NavRegionIteration2D>> ®ions = map_iteration->region_iterations;
|
||||||
HashMap<uint32_t, LocalVector<Edge::Connection>> ®ion_external_connections = map_iteration->external_region_connections;
|
HashMap<const NavBaseIteration2D *, LocalVector<Connection>> ®ion_external_connections = map_iteration->external_region_connections;
|
||||||
|
|
||||||
|
map_iteration->navbases_polygons_external_connections.clear();
|
||||||
|
|
||||||
// Remove regions connections.
|
// Remove regions connections.
|
||||||
region_external_connections.clear();
|
region_external_connections.clear();
|
||||||
for (const NavRegionIteration2D ®ion : regions) {
|
|
||||||
region_external_connections[region.id] = LocalVector<Edge::Connection>();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy all region polygons in the map.
|
// Copy all region polygons in the map.
|
||||||
int polygon_count = 0;
|
int polygon_count = 0;
|
||||||
for (NavRegionIteration2D ®ion : regions) {
|
for (const Ref<NavRegionIteration2D> ®ion : regions) {
|
||||||
if (!region.get_enabled()) {
|
const uint32_t polygons_size = region->navmesh_polygons.size();
|
||||||
continue;
|
polygon_count += polygons_size;
|
||||||
}
|
|
||||||
LocalVector<Polygon> &polygons_source = region.navmesh_polygons;
|
region_external_connections[region.ptr()] = LocalVector<Connection>();
|
||||||
for (uint32_t n = 0; n < polygons_source.size(); n++) {
|
map_iteration->navbases_polygons_external_connections[region.ptr()] = LocalVector<LocalVector<Connection>>();
|
||||||
polygons_source[n].id = polygon_count;
|
map_iteration->navbases_polygons_external_connections[region.ptr()].resize(polygons_size);
|
||||||
polygon_count++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
performance_data.pm_polygon_count = polygon_count;
|
performance_data.pm_polygon_count = polygon_count;
|
||||||
@ -106,7 +103,7 @@ void NavMapBuilder2D::_build_step_find_edge_connection_pairs(NavMapIterationBuil
|
|||||||
PerformanceData &performance_data = r_build.performance_data;
|
PerformanceData &performance_data = r_build.performance_data;
|
||||||
NavMapIteration2D *map_iteration = r_build.map_iteration;
|
NavMapIteration2D *map_iteration = r_build.map_iteration;
|
||||||
int polygon_count = r_build.polygon_count;
|
int polygon_count = r_build.polygon_count;
|
||||||
const Vector2 merge_rasterizer_cell_size = r_build.merge_rasterizer_cell_size;
|
|
||||||
HashMap<EdgeKey, EdgeConnectionPair, EdgeKey> &connection_pairs_map = r_build.iter_connection_pairs_map;
|
HashMap<EdgeKey, EdgeConnectionPair, EdgeKey> &connection_pairs_map = r_build.iter_connection_pairs_map;
|
||||||
|
|
||||||
// Group all edges per key.
|
// Group all edges per key.
|
||||||
@ -114,41 +111,33 @@ void NavMapBuilder2D::_build_step_find_edge_connection_pairs(NavMapIterationBuil
|
|||||||
connection_pairs_map.reserve(polygon_count);
|
connection_pairs_map.reserve(polygon_count);
|
||||||
int free_edges_count = 0; // How many ConnectionPairs have only one Connection.
|
int free_edges_count = 0; // How many ConnectionPairs have only one Connection.
|
||||||
|
|
||||||
for (NavRegionIteration2D ®ion : map_iteration->region_iterations) {
|
for (const Ref<NavRegionIteration2D> ®ion : map_iteration->region_iterations) {
|
||||||
if (!region.get_enabled()) {
|
for (const ConnectableEdge &connectable_edge : region->get_external_edges()) {
|
||||||
continue;
|
const EdgeKey &ek = connectable_edge.ek;
|
||||||
}
|
|
||||||
|
|
||||||
for (Polygon &poly : region.navmesh_polygons) {
|
HashMap<EdgeKey, EdgeConnectionPair, EdgeKey>::Iterator pair_it = connection_pairs_map.find(ek);
|
||||||
for (uint32_t p = 0; p < poly.vertices.size(); p++) {
|
if (!pair_it) {
|
||||||
const int next_point = (p + 1) % poly.vertices.size();
|
pair_it = connection_pairs_map.insert(ek, EdgeConnectionPair());
|
||||||
const EdgeKey ek(get_point_key(poly.vertices[p], merge_rasterizer_cell_size), get_point_key(poly.vertices[next_point], merge_rasterizer_cell_size));
|
performance_data.pm_edge_count += 1;
|
||||||
|
++free_edges_count;
|
||||||
|
}
|
||||||
|
EdgeConnectionPair &pair = pair_it->value;
|
||||||
|
if (pair.size < 2) {
|
||||||
|
// Add the polygon/edge tuple to this key.
|
||||||
|
Connection new_connection;
|
||||||
|
new_connection.polygon = ®ion->navmesh_polygons[connectable_edge.polygon_index];
|
||||||
|
new_connection.pathway_start = connectable_edge.pathway_start;
|
||||||
|
new_connection.pathway_end = connectable_edge.pathway_end;
|
||||||
|
|
||||||
HashMap<EdgeKey, EdgeConnectionPair, EdgeKey>::Iterator pair_it = connection_pairs_map.find(ek);
|
pair.connections[pair.size] = new_connection;
|
||||||
if (!pair_it) {
|
++pair.size;
|
||||||
pair_it = connection_pairs_map.insert(ek, EdgeConnectionPair());
|
if (pair.size == 2) {
|
||||||
performance_data.pm_edge_count += 1;
|
--free_edges_count;
|
||||||
++free_edges_count;
|
|
||||||
}
|
}
|
||||||
EdgeConnectionPair &pair = pair_it->value;
|
|
||||||
if (pair.size < 2) {
|
|
||||||
// Add the polygon/edge tuple to this key.
|
|
||||||
Edge::Connection new_connection;
|
|
||||||
new_connection.polygon = &poly;
|
|
||||||
new_connection.edge = p;
|
|
||||||
new_connection.pathway_start = poly.vertices[p];
|
|
||||||
new_connection.pathway_end = poly.vertices[next_point];
|
|
||||||
|
|
||||||
pair.connections[pair.size] = new_connection;
|
} else {
|
||||||
++pair.size;
|
// The edge is already connected with another edge, skip.
|
||||||
if (pair.size == 2) {
|
ERR_PRINT_ONCE("Navigation map synchronization error. Attempted to merge a navigation mesh polygon edge with another already-merged edge. This is usually caused by crossing edges, overlapping polygons, or a mismatch of the NavigationMesh / NavigationPolygon baked 'cell_size' and navigation map 'cell_size'. If you're certain none of above is the case, change 'navigation/3d/merge_rasterizer_cell_scale' to 0.001.");
|
||||||
--free_edges_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// The edge is already connected with another edge, skip.
|
|
||||||
ERR_PRINT_ONCE("Navigation map synchronization error. Attempted to merge a navigation mesh polygon edge with another already-merged edge. This is usually caused by crossing edges, overlapping polygons, or a mismatch of the NavigationMesh / NavigationPolygon baked 'cell_size' and navigation map 'cell_size'. If you're certain none of above is the case, change 'navigation/3d/merge_rasterizer_cell_scale' to 0.001.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -160,23 +149,28 @@ void NavMapBuilder2D::_build_step_merge_edge_connection_pairs(NavMapIterationBui
|
|||||||
PerformanceData &performance_data = r_build.performance_data;
|
PerformanceData &performance_data = r_build.performance_data;
|
||||||
|
|
||||||
HashMap<EdgeKey, EdgeConnectionPair, EdgeKey> &connection_pairs_map = r_build.iter_connection_pairs_map;
|
HashMap<EdgeKey, EdgeConnectionPair, EdgeKey> &connection_pairs_map = r_build.iter_connection_pairs_map;
|
||||||
LocalVector<Edge::Connection> &free_edges = r_build.iter_free_edges;
|
LocalVector<Connection> &free_edges = r_build.iter_free_edges;
|
||||||
int free_edges_count = r_build.free_edge_count;
|
int free_edges_count = r_build.free_edge_count;
|
||||||
bool use_edge_connections = r_build.use_edge_connections;
|
bool use_edge_connections = r_build.use_edge_connections;
|
||||||
|
|
||||||
free_edges.clear();
|
free_edges.clear();
|
||||||
free_edges.reserve(free_edges_count);
|
free_edges.reserve(free_edges_count);
|
||||||
|
|
||||||
|
NavMapIteration2D *map_iteration = r_build.map_iteration;
|
||||||
|
|
||||||
|
HashMap<const NavBaseIteration2D *, LocalVector<LocalVector<Nav2D::Connection>>> &navbases_polygons_external_connections = map_iteration->navbases_polygons_external_connections;
|
||||||
|
|
||||||
for (const KeyValue<EdgeKey, EdgeConnectionPair> &pair_it : connection_pairs_map) {
|
for (const KeyValue<EdgeKey, EdgeConnectionPair> &pair_it : connection_pairs_map) {
|
||||||
const EdgeConnectionPair &pair = pair_it.value;
|
const EdgeConnectionPair &pair = pair_it.value;
|
||||||
if (pair.size == 2) {
|
if (pair.size == 2) {
|
||||||
// Connect edge that are shared in different polygons.
|
// Connect edge that are shared in different polygons.
|
||||||
const Edge::Connection &c1 = pair.connections[0];
|
const Connection &c1 = pair.connections[0];
|
||||||
const Edge::Connection &c2 = pair.connections[1];
|
const Connection &c2 = pair.connections[1];
|
||||||
c1.polygon->edges[c1.edge].connections.push_back(c2);
|
|
||||||
c2.polygon->edges[c2.edge].connections.push_back(c1);
|
navbases_polygons_external_connections[c1.polygon->owner][c1.polygon->id].push_back(c2);
|
||||||
// Note: The pathway_start/end are full for those connection and do not need to be modified.
|
navbases_polygons_external_connections[c2.polygon->owner][c2.polygon->id].push_back(c1);
|
||||||
performance_data.pm_edge_merge_count += 1;
|
performance_data.pm_edge_connection_count += 1;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
CRASH_COND_MSG(pair.size != 1, vformat("Number of connection != 1. Found: %d", pair.size));
|
CRASH_COND_MSG(pair.size != 1, vformat("Number of connection != 1. Found: %d", pair.size));
|
||||||
if (use_edge_connections && pair.connections[0].polygon->owner->get_use_edge_connections()) {
|
if (use_edge_connections && pair.connections[0].polygon->owner->get_use_edge_connections()) {
|
||||||
@ -191,8 +185,11 @@ void NavMapBuilder2D::_build_step_edge_connection_margin_connections(NavMapItera
|
|||||||
NavMapIteration2D *map_iteration = r_build.map_iteration;
|
NavMapIteration2D *map_iteration = r_build.map_iteration;
|
||||||
|
|
||||||
real_t edge_connection_margin = r_build.edge_connection_margin;
|
real_t edge_connection_margin = r_build.edge_connection_margin;
|
||||||
LocalVector<Edge::Connection> &free_edges = r_build.iter_free_edges;
|
|
||||||
HashMap<uint32_t, LocalVector<Edge::Connection>> ®ion_external_connections = map_iteration->external_region_connections;
|
LocalVector<Connection> &free_edges = r_build.iter_free_edges;
|
||||||
|
HashMap<const NavBaseIteration2D *, LocalVector<Connection>> ®ion_external_connections = map_iteration->external_region_connections;
|
||||||
|
|
||||||
|
HashMap<const NavBaseIteration2D *, LocalVector<LocalVector<Nav2D::Connection>>> &navbases_polygons_external_connections = map_iteration->navbases_polygons_external_connections;
|
||||||
|
|
||||||
// Find the compatible near edges.
|
// Find the compatible near edges.
|
||||||
//
|
//
|
||||||
@ -206,23 +203,23 @@ void NavMapBuilder2D::_build_step_edge_connection_margin_connections(NavMapItera
|
|||||||
const real_t edge_connection_margin_squared = edge_connection_margin * edge_connection_margin;
|
const real_t edge_connection_margin_squared = edge_connection_margin * edge_connection_margin;
|
||||||
|
|
||||||
for (uint32_t i = 0; i < free_edges.size(); i++) {
|
for (uint32_t i = 0; i < free_edges.size(); i++) {
|
||||||
const Edge::Connection &free_edge = free_edges[i];
|
const Connection &free_edge = free_edges[i];
|
||||||
Vector2 edge_p1 = free_edge.polygon->vertices[free_edge.edge];
|
const Vector2 &edge_p1 = free_edge.pathway_start;
|
||||||
Vector2 edge_p2 = free_edge.polygon->vertices[(free_edge.edge + 1) % free_edge.polygon->vertices.size()];
|
const Vector2 &edge_p2 = free_edge.pathway_end;
|
||||||
|
|
||||||
for (uint32_t j = 0; j < free_edges.size(); j++) {
|
for (uint32_t j = 0; j < free_edges.size(); j++) {
|
||||||
const Edge::Connection &other_edge = free_edges[j];
|
const Connection &other_edge = free_edges[j];
|
||||||
if (i == j || free_edge.polygon->owner == other_edge.polygon->owner) {
|
if (i == j || free_edge.polygon->owner == other_edge.polygon->owner) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2 other_edge_p1 = other_edge.polygon->vertices[other_edge.edge];
|
const Vector2 &other_edge_p1 = other_edge.pathway_start;
|
||||||
Vector2 other_edge_p2 = other_edge.polygon->vertices[(other_edge.edge + 1) % other_edge.polygon->vertices.size()];
|
const Vector2 &other_edge_p2 = other_edge.pathway_end;
|
||||||
|
|
||||||
// Compute the projection of the opposite edge on the current one.
|
// Compute the projection of the opposite edge on the current one
|
||||||
Vector2 edge_vector = edge_p2 - edge_p1;
|
Vector2 edge_vector = edge_p2 - edge_p1;
|
||||||
real_t projected_p1_ratio = edge_vector.dot(other_edge_p1 - edge_p1) / edge_vector.length_squared();
|
real_t projected_p1_ratio = edge_vector.dot(other_edge_p1 - edge_p1) / (edge_vector.length_squared());
|
||||||
real_t projected_p2_ratio = edge_vector.dot(other_edge_p2 - edge_p1) / edge_vector.length_squared();
|
real_t projected_p2_ratio = edge_vector.dot(other_edge_p2 - edge_p1) / (edge_vector.length_squared());
|
||||||
if ((projected_p1_ratio < 0.0 && projected_p2_ratio < 0.0) || (projected_p1_ratio > 1.0 && projected_p2_ratio > 1.0)) {
|
if ((projected_p1_ratio < 0.0 && projected_p2_ratio < 0.0) || (projected_p1_ratio > 1.0 && projected_p2_ratio > 1.0)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -251,13 +248,14 @@ void NavMapBuilder2D::_build_step_edge_connection_margin_connections(NavMapItera
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The edges can now be connected.
|
// The edges can now be connected.
|
||||||
Edge::Connection new_connection = other_edge;
|
Connection new_connection = other_edge;
|
||||||
new_connection.pathway_start = (self1 + other1) / 2.0;
|
new_connection.pathway_start = (self1 + other1) / 2.0;
|
||||||
new_connection.pathway_end = (self2 + other2) / 2.0;
|
new_connection.pathway_end = (self2 + other2) / 2.0;
|
||||||
free_edge.polygon->edges[free_edge.edge].connections.push_back(new_connection);
|
//free_edge.polygon->connections.push_back(new_connection);
|
||||||
|
|
||||||
// Add the connection to the region_connection map.
|
// Add the connection to the region_connection map.
|
||||||
region_external_connections[(uint32_t)free_edge.polygon->owner->id].push_back(new_connection);
|
region_external_connections[free_edge.polygon->owner].push_back(new_connection);
|
||||||
|
navbases_polygons_external_connections[free_edge.polygon->owner][free_edge.polygon->id].push_back(new_connection);
|
||||||
performance_data.pm_edge_connection_count += 1;
|
performance_data.pm_edge_connection_count += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -268,18 +266,28 @@ void NavMapBuilder2D::_build_step_navlink_connections(NavMapIterationBuild2D &r_
|
|||||||
|
|
||||||
real_t link_connection_radius = r_build.link_connection_radius;
|
real_t link_connection_radius = r_build.link_connection_radius;
|
||||||
|
|
||||||
LocalVector<NavLinkIteration2D> &links = map_iteration->link_iterations;
|
const LocalVector<Ref<NavLinkIteration2D>> &links = map_iteration->link_iterations;
|
||||||
|
|
||||||
int polygon_count = r_build.polygon_count;
|
int polygon_count = r_build.polygon_count;
|
||||||
|
|
||||||
real_t link_connection_radius_sqr = link_connection_radius * link_connection_radius;
|
real_t link_connection_radius_sqr = link_connection_radius * link_connection_radius;
|
||||||
|
|
||||||
|
HashMap<const NavBaseIteration2D *, LocalVector<LocalVector<Nav2D::Connection>>> &navbases_polygons_external_connections = map_iteration->navbases_polygons_external_connections;
|
||||||
|
LocalVector<Nav2D::Polygon> &navlink_polygons = map_iteration->navlink_polygons;
|
||||||
|
navlink_polygons.clear();
|
||||||
|
navlink_polygons.resize(links.size());
|
||||||
|
uint32_t navlink_index = 0;
|
||||||
|
|
||||||
// Search for polygons within range of a nav link.
|
// Search for polygons within range of a nav link.
|
||||||
for (NavLinkIteration2D &link : links) {
|
for (const Ref<NavLinkIteration2D> &link : links) {
|
||||||
if (!link.get_enabled()) {
|
polygon_count++;
|
||||||
continue;
|
Polygon &new_polygon = navlink_polygons[navlink_index++];
|
||||||
}
|
|
||||||
const Vector2 link_start_pos = link.get_start_position();
|
new_polygon.id = 0;
|
||||||
const Vector2 link_end_pos = link.get_end_position();
|
new_polygon.owner = link.ptr();
|
||||||
|
|
||||||
|
const Vector2 link_start_pos = link->get_start_position();
|
||||||
|
const Vector2 link_end_pos = link->get_end_position();
|
||||||
|
|
||||||
Polygon *closest_start_polygon = nullptr;
|
Polygon *closest_start_polygon = nullptr;
|
||||||
real_t closest_start_sqr_dist = link_connection_radius_sqr;
|
real_t closest_start_sqr_dist = link_connection_radius_sqr;
|
||||||
@ -289,16 +297,13 @@ void NavMapBuilder2D::_build_step_navlink_connections(NavMapIterationBuild2D &r_
|
|||||||
real_t closest_end_sqr_dist = link_connection_radius_sqr;
|
real_t closest_end_sqr_dist = link_connection_radius_sqr;
|
||||||
Vector2 closest_end_point;
|
Vector2 closest_end_point;
|
||||||
|
|
||||||
for (NavRegionIteration2D ®ion : map_iteration->region_iterations) {
|
for (const Ref<NavRegionIteration2D> ®ion : map_iteration->region_iterations) {
|
||||||
if (!region.get_enabled()) {
|
Rect2 region_bounds = region->get_bounds().grow(link_connection_radius);
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Rect2 region_bounds = region.get_bounds().grow(link_connection_radius);
|
|
||||||
if (!region_bounds.has_point(link_start_pos) && !region_bounds.has_point(link_end_pos)) {
|
if (!region_bounds.has_point(link_start_pos) && !region_bounds.has_point(link_end_pos)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Polygon &polyon : region.navmesh_polygons) {
|
for (Polygon &polyon : region->navmesh_polygons) {
|
||||||
for (uint32_t point_id = 2; point_id < polyon.vertices.size(); point_id += 1) {
|
for (uint32_t point_id = 2; point_id < polyon.vertices.size(); point_id += 1) {
|
||||||
const Triangle2 triangle(polyon.vertices[0], polyon.vertices[point_id - 1], polyon.vertices[point_id]);
|
const Triangle2 triangle(polyon.vertices[0], polyon.vertices[point_id - 1], polyon.vertices[point_id]);
|
||||||
|
|
||||||
@ -331,14 +336,6 @@ void NavMapBuilder2D::_build_step_navlink_connections(NavMapIterationBuild2D &r_
|
|||||||
|
|
||||||
// If we have both a start and end point, then create a synthetic polygon to route through.
|
// If we have both a start and end point, then create a synthetic polygon to route through.
|
||||||
if (closest_start_polygon && closest_end_polygon) {
|
if (closest_start_polygon && closest_end_polygon) {
|
||||||
link.navmesh_polygons.clear();
|
|
||||||
link.navmesh_polygons.resize(1);
|
|
||||||
Polygon &new_polygon = link.navmesh_polygons[0];
|
|
||||||
new_polygon.id = polygon_count++;
|
|
||||||
new_polygon.owner = &link;
|
|
||||||
|
|
||||||
new_polygon.edges.clear();
|
|
||||||
new_polygon.edges.resize(4);
|
|
||||||
new_polygon.vertices.resize(4);
|
new_polygon.vertices.resize(4);
|
||||||
|
|
||||||
// Build a set of vertices that create a thin polygon going from the start to the end point.
|
// Build a set of vertices that create a thin polygon going from the start to the end point.
|
||||||
@ -349,36 +346,38 @@ void NavMapBuilder2D::_build_step_navlink_connections(NavMapIterationBuild2D &r_
|
|||||||
|
|
||||||
// Setup connections to go forward in the link.
|
// Setup connections to go forward in the link.
|
||||||
{
|
{
|
||||||
Edge::Connection entry_connection;
|
Connection entry_connection;
|
||||||
entry_connection.polygon = &new_polygon;
|
entry_connection.polygon = &new_polygon;
|
||||||
entry_connection.edge = -1;
|
entry_connection.edge = -1;
|
||||||
entry_connection.pathway_start = new_polygon.vertices[0];
|
entry_connection.pathway_start = new_polygon.vertices[0];
|
||||||
entry_connection.pathway_end = new_polygon.vertices[1];
|
entry_connection.pathway_end = new_polygon.vertices[1];
|
||||||
closest_start_polygon->edges[0].connections.push_back(entry_connection);
|
navbases_polygons_external_connections[closest_start_polygon->owner][closest_start_polygon->id].push_back(entry_connection);
|
||||||
|
|
||||||
Edge::Connection exit_connection;
|
Connection exit_connection;
|
||||||
exit_connection.polygon = closest_end_polygon;
|
exit_connection.polygon = closest_end_polygon;
|
||||||
exit_connection.edge = -1;
|
exit_connection.edge = -1;
|
||||||
exit_connection.pathway_start = new_polygon.vertices[2];
|
exit_connection.pathway_start = new_polygon.vertices[2];
|
||||||
exit_connection.pathway_end = new_polygon.vertices[3];
|
exit_connection.pathway_end = new_polygon.vertices[3];
|
||||||
new_polygon.edges[2].connections.push_back(exit_connection);
|
navbases_polygons_external_connections[link.ptr()].push_back(LocalVector<Nav2D::Connection>());
|
||||||
|
navbases_polygons_external_connections[link.ptr()][new_polygon.id].push_back(exit_connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the link is bi-directional, create connections from the end to the start.
|
// If the link is bi-directional, create connections from the end to the start.
|
||||||
if (link.is_bidirectional()) {
|
if (link->is_bidirectional()) {
|
||||||
Edge::Connection entry_connection;
|
Connection entry_connection;
|
||||||
entry_connection.polygon = &new_polygon;
|
entry_connection.polygon = &new_polygon;
|
||||||
entry_connection.edge = -1;
|
entry_connection.edge = -1;
|
||||||
entry_connection.pathway_start = new_polygon.vertices[2];
|
entry_connection.pathway_start = new_polygon.vertices[2];
|
||||||
entry_connection.pathway_end = new_polygon.vertices[3];
|
entry_connection.pathway_end = new_polygon.vertices[3];
|
||||||
closest_end_polygon->edges[0].connections.push_back(entry_connection);
|
navbases_polygons_external_connections[closest_end_polygon->owner][closest_end_polygon->id].push_back(entry_connection);
|
||||||
|
|
||||||
Edge::Connection exit_connection;
|
Connection exit_connection;
|
||||||
exit_connection.polygon = closest_start_polygon;
|
exit_connection.polygon = closest_start_polygon;
|
||||||
exit_connection.edge = -1;
|
exit_connection.edge = -1;
|
||||||
exit_connection.pathway_start = new_polygon.vertices[0];
|
exit_connection.pathway_start = new_polygon.vertices[0];
|
||||||
exit_connection.pathway_end = new_polygon.vertices[1];
|
exit_connection.pathway_end = new_polygon.vertices[1];
|
||||||
new_polygon.edges[0].connections.push_back(exit_connection);
|
navbases_polygons_external_connections[link.ptr()].push_back(LocalVector<Nav2D::Connection>());
|
||||||
|
navbases_polygons_external_connections[link.ptr()][new_polygon.id].push_back(exit_connection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -391,12 +390,35 @@ void NavMapBuilder2D::_build_update_map_iteration(NavMapIterationBuild2D &r_buil
|
|||||||
|
|
||||||
map_iteration->navmesh_polygon_count = r_build.polygon_count;
|
map_iteration->navmesh_polygon_count = r_build.polygon_count;
|
||||||
|
|
||||||
|
uint32_t navmesh_polygon_count = r_build.polygon_count;
|
||||||
|
uint32_t total_polygon_count = navmesh_polygon_count;
|
||||||
|
|
||||||
map_iteration->path_query_slots_mutex.lock();
|
map_iteration->path_query_slots_mutex.lock();
|
||||||
for (NavMeshQueries2D::PathQuerySlot &p_path_query_slot : map_iteration->path_query_slots) {
|
for (NavMeshQueries2D::PathQuerySlot &p_path_query_slot : map_iteration->path_query_slots) {
|
||||||
p_path_query_slot.traversable_polys.clear();
|
p_path_query_slot.traversable_polys.clear();
|
||||||
p_path_query_slot.traversable_polys.reserve(map_iteration->navmesh_polygon_count * 0.25);
|
p_path_query_slot.traversable_polys.reserve(navmesh_polygon_count * 0.25);
|
||||||
p_path_query_slot.path_corridor.clear();
|
p_path_query_slot.path_corridor.clear();
|
||||||
p_path_query_slot.path_corridor.resize(map_iteration->navmesh_polygon_count);
|
|
||||||
|
p_path_query_slot.path_corridor.resize(total_polygon_count);
|
||||||
|
|
||||||
|
p_path_query_slot.poly_to_id.clear();
|
||||||
|
p_path_query_slot.poly_to_id.reserve(total_polygon_count);
|
||||||
|
|
||||||
|
int polygon_id = 0;
|
||||||
|
for (Ref<NavRegionIteration2D> ®ion : map_iteration->region_iterations) {
|
||||||
|
for (const Polygon &polygon : region->navmesh_polygons) {
|
||||||
|
p_path_query_slot.poly_to_id[&polygon] = polygon_id;
|
||||||
|
polygon_id++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const Polygon &polygon : map_iteration->navlink_polygons) {
|
||||||
|
p_path_query_slot.poly_to_id[&polygon] = polygon_id;
|
||||||
|
polygon_id++;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEV_ASSERT(p_path_query_slot.path_corridor.size() == p_path_query_slot.poly_to_id.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
map_iteration->path_query_slots_mutex.unlock();
|
map_iteration->path_query_slots_mutex.unlock();
|
||||||
}
|
}
|
||||||
|
@ -37,9 +37,9 @@
|
|||||||
#include "core/math/math_defs.h"
|
#include "core/math/math_defs.h"
|
||||||
#include "core/os/semaphore.h"
|
#include "core/os/semaphore.h"
|
||||||
|
|
||||||
struct NavLinkIteration2D;
|
class NavLinkIteration2D;
|
||||||
class NavRegion2D;
|
class NavRegion2D;
|
||||||
struct NavRegionIteration2D;
|
class NavRegionIteration2D;
|
||||||
struct NavMapIteration2D;
|
struct NavMapIteration2D;
|
||||||
|
|
||||||
struct NavMapIterationBuild2D {
|
struct NavMapIterationBuild2D {
|
||||||
@ -52,7 +52,7 @@ struct NavMapIterationBuild2D {
|
|||||||
int free_edge_count = 0;
|
int free_edge_count = 0;
|
||||||
|
|
||||||
HashMap<Nav2D::EdgeKey, Nav2D::EdgeConnectionPair, Nav2D::EdgeKey> iter_connection_pairs_map;
|
HashMap<Nav2D::EdgeKey, Nav2D::EdgeConnectionPair, Nav2D::EdgeKey> iter_connection_pairs_map;
|
||||||
LocalVector<Nav2D::Edge::Connection> iter_free_edges;
|
LocalVector<Nav2D::Connection> iter_free_edges;
|
||||||
|
|
||||||
NavMapIteration2D *map_iteration = nullptr;
|
NavMapIteration2D *map_iteration = nullptr;
|
||||||
|
|
||||||
@ -74,19 +74,33 @@ struct NavMapIteration2D {
|
|||||||
mutable SafeNumeric<uint32_t> users;
|
mutable SafeNumeric<uint32_t> users;
|
||||||
RWLock rwlock;
|
RWLock rwlock;
|
||||||
|
|
||||||
LocalVector<NavRegionIteration2D> region_iterations;
|
LocalVector<Ref<NavRegionIteration2D>> region_iterations;
|
||||||
LocalVector<NavLinkIteration2D> link_iterations;
|
LocalVector<Ref<NavLinkIteration2D>> link_iterations;
|
||||||
|
|
||||||
int navmesh_polygon_count = 0;
|
int navmesh_polygon_count = 0;
|
||||||
|
|
||||||
// The edge connections that the map builds on top with the edge connection margin.
|
// The edge connections that the map builds on top with the edge connection margin.
|
||||||
HashMap<uint32_t, LocalVector<Nav2D::Edge::Connection>> external_region_connections;
|
HashMap<const NavBaseIteration2D *, LocalVector<Nav2D::Connection>> external_region_connections;
|
||||||
|
HashMap<const NavBaseIteration2D *, LocalVector<LocalVector<Nav2D::Connection>>> navbases_polygons_external_connections;
|
||||||
|
|
||||||
HashMap<NavRegion2D *, uint32_t> region_ptr_to_region_id;
|
LocalVector<Nav2D::Polygon> navlink_polygons;
|
||||||
|
|
||||||
|
HashMap<NavRegion2D *, Ref<NavRegionIteration2D>> region_ptr_to_region_iteration;
|
||||||
|
|
||||||
LocalVector<NavMeshQueries2D::PathQuerySlot> path_query_slots;
|
LocalVector<NavMeshQueries2D::PathQuerySlot> path_query_slots;
|
||||||
Mutex path_query_slots_mutex;
|
Mutex path_query_slots_mutex;
|
||||||
Semaphore path_query_slots_semaphore;
|
Semaphore path_query_slots_semaphore;
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
navmesh_polygon_count = 0;
|
||||||
|
|
||||||
|
region_iterations.clear();
|
||||||
|
link_iterations.clear();
|
||||||
|
external_region_connections.clear();
|
||||||
|
navbases_polygons_external_connections.clear();
|
||||||
|
navlink_polygons.clear();
|
||||||
|
region_ptr_to_region_iteration.clear();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class NavMapIterationRead2D {
|
class NavMapIterationRead2D {
|
||||||
|
@ -234,22 +234,15 @@ void NavMeshQueries2D::_query_task_find_start_end_positions(NavMeshPathQueryTask
|
|||||||
real_t begin_d = FLT_MAX;
|
real_t begin_d = FLT_MAX;
|
||||||
real_t end_d = FLT_MAX;
|
real_t end_d = FLT_MAX;
|
||||||
|
|
||||||
const LocalVector<NavRegionIteration2D> ®ions = p_map_iteration.region_iterations;
|
const LocalVector<Ref<NavRegionIteration2D>> ®ions = p_map_iteration.region_iterations;
|
||||||
|
|
||||||
for (const NavRegionIteration2D ®ion : regions) {
|
for (const Ref<NavRegionIteration2D> ®ion : regions) {
|
||||||
if (!region.get_enabled()) {
|
if (!_query_task_is_connection_owner_usable(p_query_task, region.ptr())) {
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p_query_task.exclude_regions && p_query_task.excluded_regions.has(region.get_self())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (p_query_task.include_regions && !p_query_task.included_regions.has(region.get_self())) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the initial poly and the end poly on this map.
|
// Find the initial poly and the end poly on this map.
|
||||||
for (const Polygon &p : region.get_navmesh_polygons()) {
|
for (const Polygon &p : region->get_navmesh_polygons()) {
|
||||||
// Only consider the polygon if it in a region with compatible layers.
|
// Only consider the polygon if it in a region with compatible layers.
|
||||||
if ((p_query_task.navigation_layers & p.owner->get_navigation_layers()) == 0) {
|
if ((p_query_task.navigation_layers & p.owner->get_navigation_layers()) == 0) {
|
||||||
continue;
|
continue;
|
||||||
@ -279,7 +272,47 @@ void NavMeshQueries2D::_query_task_find_start_end_positions(NavMeshPathQueryTask
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavMeshQueries2D::_query_task_build_path_corridor(NavMeshPathQueryTask2D &p_query_task) {
|
void NavMeshQueries2D::_query_task_search_polygon_connections(NavMeshPathQueryTask2D &p_query_task, const Connection &p_connection, uint32_t p_least_cost_id, const NavigationPoly &p_least_cost_poly, real_t p_poly_enter_cost, const Vector2 &p_end_point) {
|
||||||
|
const NavBaseIteration2D *connection_owner = p_connection.polygon->owner;
|
||||||
|
ERR_FAIL_NULL(connection_owner);
|
||||||
|
const bool owner_is_usable = _query_task_is_connection_owner_usable(p_query_task, connection_owner);
|
||||||
|
if (!owner_is_usable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Heap<NavigationPoly *, NavPolyTravelCostGreaterThan, NavPolyHeapIndexer>
|
||||||
|
&traversable_polys = p_query_task.path_query_slot->traversable_polys;
|
||||||
|
LocalVector<NavigationPoly> &navigation_polys = p_query_task.path_query_slot->path_corridor;
|
||||||
|
|
||||||
|
real_t poly_travel_cost = p_least_cost_poly.poly->owner->get_travel_cost();
|
||||||
|
|
||||||
|
Vector2 new_entry = Geometry2D::get_closest_point_to_segment(p_least_cost_poly.entry, p_connection.pathway_start, p_connection.pathway_end);
|
||||||
|
real_t new_traveled_distance = p_least_cost_poly.entry.distance_to(new_entry) * poly_travel_cost + p_poly_enter_cost + p_least_cost_poly.traveled_distance;
|
||||||
|
|
||||||
|
// Check if the neighbor polygon has already been processed.
|
||||||
|
NavigationPoly &neighbor_poly = navigation_polys[p_query_task.path_query_slot->poly_to_id[p_connection.polygon]];
|
||||||
|
if (new_traveled_distance < neighbor_poly.traveled_distance) {
|
||||||
|
// Add the polygon to the heap of polygons to traverse next.
|
||||||
|
neighbor_poly.back_navigation_poly_id = p_least_cost_id;
|
||||||
|
neighbor_poly.back_navigation_edge = p_connection.edge;
|
||||||
|
neighbor_poly.back_navigation_edge_pathway_start = p_connection.pathway_start;
|
||||||
|
neighbor_poly.back_navigation_edge_pathway_end = p_connection.pathway_end;
|
||||||
|
neighbor_poly.traveled_distance = new_traveled_distance;
|
||||||
|
neighbor_poly.distance_to_destination =
|
||||||
|
new_entry.distance_to(p_end_point) *
|
||||||
|
connection_owner->get_travel_cost();
|
||||||
|
neighbor_poly.entry = new_entry;
|
||||||
|
|
||||||
|
if (neighbor_poly.traversable_poly_index != traversable_polys.INVALID_INDEX) {
|
||||||
|
traversable_polys.shift(neighbor_poly.traversable_poly_index);
|
||||||
|
} else {
|
||||||
|
neighbor_poly.poly = p_connection.polygon;
|
||||||
|
traversable_polys.push(&neighbor_poly);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NavMeshQueries2D::_query_task_build_path_corridor(NavMeshPathQueryTask2D &p_query_task, const NavMapIteration2D &p_map_iteration) {
|
||||||
const Vector2 p_target_position = p_query_task.target_position;
|
const Vector2 p_target_position = p_query_task.target_position;
|
||||||
const Polygon *begin_poly = p_query_task.begin_polygon;
|
const Polygon *begin_poly = p_query_task.begin_polygon;
|
||||||
const Polygon *end_poly = p_query_task.end_polygon;
|
const Polygon *end_poly = p_query_task.end_polygon;
|
||||||
@ -297,15 +330,15 @@ void NavMeshQueries2D::_query_task_build_path_corridor(NavMeshPathQueryTask2D &p
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the matching navigation polygon.
|
// Initialize the matching navigation polygon.
|
||||||
NavigationPoly &begin_navigation_poly = navigation_polys[begin_poly->id];
|
NavigationPoly &begin_navigation_poly = navigation_polys[p_query_task.path_query_slot->poly_to_id[begin_poly]];
|
||||||
begin_navigation_poly.poly = begin_poly;
|
begin_navigation_poly.poly = begin_poly;
|
||||||
begin_navigation_poly.entry = begin_point;
|
begin_navigation_poly.entry = begin_point;
|
||||||
begin_navigation_poly.back_navigation_edge_pathway_start = begin_point;
|
begin_navigation_poly.back_navigation_edge_pathway_start = begin_point;
|
||||||
begin_navigation_poly.back_navigation_edge_pathway_end = begin_point;
|
begin_navigation_poly.back_navigation_edge_pathway_end = begin_point;
|
||||||
begin_navigation_poly.traveled_distance = 0.0;
|
begin_navigation_poly.traveled_distance = 0.f;
|
||||||
|
|
||||||
// This is an implementation of the A* algorithm.
|
// This is an implementation of the A* algorithm.
|
||||||
uint32_t least_cost_id = begin_poly->id;
|
uint32_t least_cost_id = p_query_task.path_query_slot->poly_to_id[begin_poly];
|
||||||
bool found_route = false;
|
bool found_route = false;
|
||||||
|
|
||||||
const Polygon *reachable_end = nullptr;
|
const Polygon *reachable_end = nullptr;
|
||||||
@ -313,49 +346,29 @@ void NavMeshQueries2D::_query_task_build_path_corridor(NavMeshPathQueryTask2D &p
|
|||||||
bool is_reachable = true;
|
bool is_reachable = true;
|
||||||
real_t poly_enter_cost = 0.0;
|
real_t poly_enter_cost = 0.0;
|
||||||
|
|
||||||
|
const HashMap<const NavBaseIteration2D *, LocalVector<LocalVector<Nav2D::Connection>>> &navbases_polygons_external_connections = p_map_iteration.navbases_polygons_external_connections;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const NavigationPoly &least_cost_poly = navigation_polys[least_cost_id];
|
const NavigationPoly &least_cost_poly = navigation_polys[least_cost_id];
|
||||||
real_t poly_travel_cost = least_cost_poly.poly->owner->get_travel_cost();
|
|
||||||
|
|
||||||
// Takes the current least_cost_poly neighbors (iterating over its edges) and compute the traveled_distance.
|
const NavBaseIteration2D *least_cost_navbase = least_cost_poly.poly->owner;
|
||||||
for (const Edge &edge : least_cost_poly.poly->edges) {
|
|
||||||
// Iterate over connections in this edge, then compute the new optimized travel distance assigned to this polygon.
|
|
||||||
for (uint32_t connection_index = 0; connection_index < edge.connections.size(); connection_index++) {
|
|
||||||
const Edge::Connection &connection = edge.connections[connection_index];
|
|
||||||
|
|
||||||
const NavBaseIteration2D *connection_owner = connection.polygon->owner;
|
const uint32_t navbase_local_polygon_id = least_cost_poly.poly->id;
|
||||||
const bool owner_is_usable = _query_task_is_connection_owner_usable(p_query_task, connection_owner);
|
const LocalVector<LocalVector<Connection>> &navbase_polygons_to_connections = least_cost_poly.poly->owner->get_internal_connections();
|
||||||
if (!owner_is_usable) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Vector2 new_entry = Geometry2D::get_closest_point_to_segment(least_cost_poly.entry, connection.pathway_start, connection.pathway_end);
|
if (navbase_polygons_to_connections.size() > 0) {
|
||||||
const real_t new_traveled_distance = least_cost_poly.entry.distance_to(new_entry) * poly_travel_cost + poly_enter_cost + least_cost_poly.traveled_distance;
|
const LocalVector<Connection> &polygon_connections = navbase_polygons_to_connections[navbase_local_polygon_id];
|
||||||
|
|
||||||
// Check if the neighbor polygon has already been processed.
|
for (const Connection &connection : polygon_connections) {
|
||||||
NavigationPoly &neighbor_poly = navigation_polys[connection.polygon->id];
|
_query_task_search_polygon_connections(p_query_task, connection, least_cost_id, least_cost_poly, poly_enter_cost, end_point);
|
||||||
if (new_traveled_distance < neighbor_poly.traveled_distance) {
|
|
||||||
// Add the polygon to the heap of polygons to traverse next.
|
|
||||||
neighbor_poly.back_navigation_poly_id = least_cost_id;
|
|
||||||
neighbor_poly.back_navigation_edge = connection.edge;
|
|
||||||
neighbor_poly.back_navigation_edge_pathway_start = connection.pathway_start;
|
|
||||||
neighbor_poly.back_navigation_edge_pathway_end = connection.pathway_end;
|
|
||||||
neighbor_poly.traveled_distance = new_traveled_distance;
|
|
||||||
neighbor_poly.distance_to_destination =
|
|
||||||
new_entry.distance_to(end_point) *
|
|
||||||
connection_owner->get_travel_cost();
|
|
||||||
neighbor_poly.entry = new_entry;
|
|
||||||
|
|
||||||
if (neighbor_poly.traversable_poly_index != traversable_polys.INVALID_INDEX) {
|
|
||||||
traversable_polys.shift(neighbor_poly.traversable_poly_index);
|
|
||||||
} else {
|
|
||||||
neighbor_poly.poly = connection.polygon;
|
|
||||||
traversable_polys.push(&neighbor_poly);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Search region external navmesh polygon connections, aka connections to other regions created by outline edge merge or links.
|
||||||
|
for (const Connection &connection : navbases_polygons_external_connections[least_cost_navbase][navbase_local_polygon_id]) {
|
||||||
|
_query_task_search_polygon_connections(p_query_task, connection, least_cost_id, least_cost_poly, poly_enter_cost, end_point);
|
||||||
|
}
|
||||||
|
|
||||||
poly_enter_cost = 0;
|
poly_enter_cost = 0;
|
||||||
// When the heap of traversable polygons is empty at this point it means the end polygon is
|
// When the heap of traversable polygons is empty at this point it means the end polygon is
|
||||||
// unreachable.
|
// unreachable.
|
||||||
@ -371,6 +384,7 @@ void NavMeshQueries2D::_query_task_build_path_corridor(NavMeshPathQueryTask2D &p
|
|||||||
// Set as end point the furthest reachable point.
|
// Set as end point the furthest reachable point.
|
||||||
end_poly = reachable_end;
|
end_poly = reachable_end;
|
||||||
real_t end_d = FLT_MAX;
|
real_t end_d = FLT_MAX;
|
||||||
|
|
||||||
for (uint32_t point_id = 2; point_id < end_poly->vertices.size(); point_id++) {
|
for (uint32_t point_id = 2; point_id < end_poly->vertices.size(); point_id++) {
|
||||||
Triangle2 t(end_poly->vertices[0], end_poly->vertices[point_id - 1], end_poly->vertices[point_id]);
|
Triangle2 t(end_poly->vertices[0], end_poly->vertices[point_id - 1], end_poly->vertices[point_id]);
|
||||||
Vector2 spoint = t.get_closest_point_to(p_target_position);
|
Vector2 spoint = t.get_closest_point_to(p_target_position);
|
||||||
@ -383,6 +397,7 @@ void NavMeshQueries2D::_query_task_build_path_corridor(NavMeshPathQueryTask2D &p
|
|||||||
|
|
||||||
// Search all faces of start polygon as well.
|
// Search all faces of start polygon as well.
|
||||||
bool closest_point_on_start_poly = false;
|
bool closest_point_on_start_poly = false;
|
||||||
|
|
||||||
for (uint32_t point_id = 2; point_id < begin_poly->vertices.size(); point_id++) {
|
for (uint32_t point_id = 2; point_id < begin_poly->vertices.size(); point_id++) {
|
||||||
Triangle2 t(begin_poly->vertices[0], begin_poly->vertices[point_id - 1], begin_poly->vertices[point_id]);
|
Triangle2 t(begin_poly->vertices[0], begin_poly->vertices[point_id - 1], begin_poly->vertices[point_id]);
|
||||||
Vector2 spoint = t.get_closest_point_to(p_target_position);
|
Vector2 spoint = t.get_closest_point_to(p_target_position);
|
||||||
@ -408,13 +423,14 @@ void NavMeshQueries2D::_query_task_build_path_corridor(NavMeshPathQueryTask2D &p
|
|||||||
nav_poly.poly = nullptr;
|
nav_poly.poly = nullptr;
|
||||||
nav_poly.traveled_distance = FLT_MAX;
|
nav_poly.traveled_distance = FLT_MAX;
|
||||||
}
|
}
|
||||||
navigation_polys[begin_poly->id].poly = begin_poly;
|
uint32_t _bp_id = p_query_task.path_query_slot->poly_to_id[begin_poly];
|
||||||
navigation_polys[begin_poly->id].traveled_distance = 0;
|
navigation_polys[_bp_id].poly = begin_poly;
|
||||||
least_cost_id = begin_poly->id;
|
navigation_polys[_bp_id].traveled_distance = 0;
|
||||||
|
least_cost_id = _bp_id;
|
||||||
reachable_end = nullptr;
|
reachable_end = nullptr;
|
||||||
} else {
|
} else {
|
||||||
// Pop the polygon with the lowest travel cost from the heap of traversable polygons.
|
// Pop the polygon with the lowest travel cost from the heap of traversable polygons.
|
||||||
least_cost_id = traversable_polys.pop()->poly->id;
|
least_cost_id = p_query_task.path_query_slot->poly_to_id[traversable_polys.pop()->poly];
|
||||||
|
|
||||||
// Store the farthest reachable end polygon in case our goal is not reachable.
|
// Store the farthest reachable end polygon in case our goal is not reachable.
|
||||||
if (is_reachable) {
|
if (is_reachable) {
|
||||||
@ -432,6 +448,7 @@ void NavMeshQueries2D::_query_task_build_path_corridor(NavMeshPathQueryTask2D &p
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (navigation_polys[least_cost_id].poly->owner->get_self() != least_cost_poly.poly->owner->get_self()) {
|
if (navigation_polys[least_cost_id].poly->owner->get_self() != least_cost_poly.poly->owner->get_self()) {
|
||||||
|
ERR_FAIL_NULL(least_cost_poly.poly->owner);
|
||||||
poly_enter_cost = least_cost_poly.poly->owner->get_enter_cost();
|
poly_enter_cost = least_cost_poly.poly->owner->get_enter_cost();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -442,6 +459,7 @@ void NavMeshQueries2D::_query_task_build_path_corridor(NavMeshPathQueryTask2D &p
|
|||||||
if (!found_route) {
|
if (!found_route) {
|
||||||
real_t end_d = FLT_MAX;
|
real_t end_d = FLT_MAX;
|
||||||
// Search all faces of the start polygon for the closest point to our target position.
|
// Search all faces of the start polygon for the closest point to our target position.
|
||||||
|
|
||||||
for (uint32_t point_id = 2; point_id < begin_poly->vertices.size(); point_id++) {
|
for (uint32_t point_id = 2; point_id < begin_poly->vertices.size(); point_id++) {
|
||||||
Triangle2 t(begin_poly->vertices[0], begin_poly->vertices[point_id - 1], begin_poly->vertices[point_id]);
|
Triangle2 t(begin_poly->vertices[0], begin_poly->vertices[point_id - 1], begin_poly->vertices[point_id]);
|
||||||
Vector2 spoint = t.get_closest_point_to(p_target_position);
|
Vector2 spoint = t.get_closest_point_to(p_target_position);
|
||||||
@ -484,7 +502,7 @@ void NavMeshQueries2D::query_task_map_iteration_get_path(NavMeshPathQueryTask2D
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_query_task_build_path_corridor(p_query_task);
|
_query_task_build_path_corridor(p_query_task, p_map_iteration);
|
||||||
|
|
||||||
if (p_query_task.status == NavMeshPathQueryTask2D::TaskStatus::QUERY_FINISHED || p_query_task.status == NavMeshPathQueryTask2D::TaskStatus::QUERY_FAILED) {
|
if (p_query_task.status == NavMeshPathQueryTask2D::TaskStatus::QUERY_FINISHED || p_query_task.status == NavMeshPathQueryTask2D::TaskStatus::QUERY_FAILED) {
|
||||||
return;
|
return;
|
||||||
@ -733,9 +751,9 @@ ClosestPointQueryResult NavMeshQueries2D::map_iteration_get_closest_point_info(c
|
|||||||
|
|
||||||
// TODO: Check for further 2D improvements.
|
// TODO: Check for further 2D improvements.
|
||||||
|
|
||||||
const LocalVector<NavRegionIteration2D> ®ions = p_map_iteration.region_iterations;
|
const LocalVector<Ref<NavRegionIteration2D>> ®ions = p_map_iteration.region_iterations;
|
||||||
for (const NavRegionIteration2D ®ion : regions) {
|
for (const Ref<NavRegionIteration2D> ®ion : regions) {
|
||||||
for (const Polygon &polygon : region.get_navmesh_polygons()) {
|
for (const Polygon &polygon : region->get_navmesh_polygons()) {
|
||||||
real_t cross = (polygon.vertices[1] - polygon.vertices[0]).cross(polygon.vertices[2] - polygon.vertices[0]);
|
real_t cross = (polygon.vertices[1] - polygon.vertices[0]).cross(polygon.vertices[2] - polygon.vertices[0]);
|
||||||
Vector2 closest_on_polygon;
|
Vector2 closest_on_polygon;
|
||||||
real_t closest = FLT_MAX;
|
real_t closest = FLT_MAX;
|
||||||
@ -803,8 +821,8 @@ Vector2 NavMeshQueries2D::map_iteration_get_random_point(const NavMapIteration2D
|
|||||||
accessible_regions.reserve(p_map_iteration.region_iterations.size());
|
accessible_regions.reserve(p_map_iteration.region_iterations.size());
|
||||||
|
|
||||||
for (uint32_t i = 0; i < p_map_iteration.region_iterations.size(); i++) {
|
for (uint32_t i = 0; i < p_map_iteration.region_iterations.size(); i++) {
|
||||||
const NavRegionIteration2D ®ion = p_map_iteration.region_iterations[i];
|
const Ref<NavRegionIteration2D> ®ion = p_map_iteration.region_iterations[i];
|
||||||
if (!region.enabled || (p_navigation_layers & region.navigation_layers) == 0) {
|
if (!region->get_enabled() || (p_navigation_layers & region->get_navigation_layers()) == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
accessible_regions.push_back(i);
|
accessible_regions.push_back(i);
|
||||||
@ -820,9 +838,9 @@ Vector2 NavMeshQueries2D::map_iteration_get_random_point(const NavMapIteration2D
|
|||||||
RBMap<real_t, uint32_t> accessible_regions_area_map;
|
RBMap<real_t, uint32_t> accessible_regions_area_map;
|
||||||
|
|
||||||
for (uint32_t accessible_region_index = 0; accessible_region_index < accessible_regions.size(); accessible_region_index++) {
|
for (uint32_t accessible_region_index = 0; accessible_region_index < accessible_regions.size(); accessible_region_index++) {
|
||||||
const NavRegionIteration2D ®ion = p_map_iteration.region_iterations[accessible_regions[accessible_region_index]];
|
const Ref<NavRegionIteration2D> ®ion = p_map_iteration.region_iterations[accessible_regions[accessible_region_index]];
|
||||||
|
|
||||||
real_t region_surface_area = region.surface_area;
|
real_t region_surface_area = region->surface_area;
|
||||||
|
|
||||||
if (region_surface_area == 0.0f) {
|
if (region_surface_area == 0.0f) {
|
||||||
continue;
|
continue;
|
||||||
@ -843,16 +861,16 @@ Vector2 NavMeshQueries2D::map_iteration_get_random_point(const NavMapIteration2D
|
|||||||
uint32_t random_region_index = E->value;
|
uint32_t random_region_index = E->value;
|
||||||
ERR_FAIL_UNSIGNED_INDEX_V(random_region_index, accessible_regions.size(), Vector2());
|
ERR_FAIL_UNSIGNED_INDEX_V(random_region_index, accessible_regions.size(), Vector2());
|
||||||
|
|
||||||
const NavRegionIteration2D &random_region = p_map_iteration.region_iterations[accessible_regions[random_region_index]];
|
const Ref<NavRegionIteration2D> &random_region = p_map_iteration.region_iterations[accessible_regions[random_region_index]];
|
||||||
|
|
||||||
return NavMeshQueries2D::polygons_get_random_point(random_region.navmesh_polygons, p_navigation_layers, p_uniformly);
|
return NavMeshQueries2D::polygons_get_random_point(random_region->navmesh_polygons, p_navigation_layers, p_uniformly);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
uint32_t random_region_index = Math::random(int(0), accessible_regions.size() - 1);
|
uint32_t random_region_index = Math::random(int(0), accessible_regions.size() - 1);
|
||||||
|
|
||||||
const NavRegionIteration2D &random_region = p_map_iteration.region_iterations[accessible_regions[random_region_index]];
|
const Ref<NavRegionIteration2D> &random_region = p_map_iteration.region_iterations[accessible_regions[random_region_index]];
|
||||||
|
|
||||||
return NavMeshQueries2D::polygons_get_random_point(random_region.navmesh_polygons, p_navigation_layers, p_uniformly);
|
return NavMeshQueries2D::polygons_get_random_point(random_region->navmesh_polygons, p_navigation_layers, p_uniformly);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -978,8 +996,15 @@ void NavMeshQueries2D::_query_task_clip_path(NavMeshPathQueryTask2D &p_query_tas
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool NavMeshQueries2D::_query_task_is_connection_owner_usable(const NavMeshPathQueryTask2D &p_query_task, const NavBaseIteration2D *p_owner) {
|
bool NavMeshQueries2D::_query_task_is_connection_owner_usable(const NavMeshPathQueryTask2D &p_query_task, const NavBaseIteration2D *p_owner) {
|
||||||
|
ERR_FAIL_NULL_V(p_owner, false);
|
||||||
|
|
||||||
bool owner_usable = true;
|
bool owner_usable = true;
|
||||||
|
|
||||||
|
if (!p_owner->get_enabled()) {
|
||||||
|
owner_usable = false;
|
||||||
|
return owner_usable;
|
||||||
|
}
|
||||||
|
|
||||||
if ((p_query_task.navigation_layers & p_owner->get_navigation_layers()) == 0) {
|
if ((p_query_task.navigation_layers & p_owner->get_navigation_layers()) == 0) {
|
||||||
// Not usable. No matching bit between task filter bitmask and owner bitmask.
|
// Not usable. No matching bit between task filter bitmask and owner bitmask.
|
||||||
owner_usable = false;
|
owner_usable = false;
|
||||||
|
@ -32,6 +32,8 @@
|
|||||||
|
|
||||||
#include "../nav_utils_2d.h"
|
#include "../nav_utils_2d.h"
|
||||||
|
|
||||||
|
#include "core/templates/a_hash_map.h"
|
||||||
|
|
||||||
#include "servers/navigation/navigation_path_query_parameters_2d.h"
|
#include "servers/navigation/navigation_path_query_parameters_2d.h"
|
||||||
#include "servers/navigation/navigation_path_query_result_2d.h"
|
#include "servers/navigation/navigation_path_query_result_2d.h"
|
||||||
#include "servers/navigation/navigation_utilities.h"
|
#include "servers/navigation/navigation_utilities.h"
|
||||||
@ -48,6 +50,7 @@ public:
|
|||||||
Heap<Nav2D::NavigationPoly *, Nav2D::NavPolyTravelCostGreaterThan, Nav2D::NavPolyHeapIndexer> traversable_polys;
|
Heap<Nav2D::NavigationPoly *, Nav2D::NavPolyTravelCostGreaterThan, Nav2D::NavPolyHeapIndexer> traversable_polys;
|
||||||
bool in_use = false;
|
bool in_use = false;
|
||||||
uint32_t slot_index = 0;
|
uint32_t slot_index = 0;
|
||||||
|
AHashMap<const Nav2D::Polygon *, uint32_t> poly_to_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct NavMeshPathQueryTask2D {
|
struct NavMeshPathQueryTask2D {
|
||||||
@ -128,7 +131,7 @@ public:
|
|||||||
static void query_task_map_iteration_get_path(NavMeshPathQueryTask2D &p_query_task, const NavMapIteration2D &p_map_iteration);
|
static void query_task_map_iteration_get_path(NavMeshPathQueryTask2D &p_query_task, const NavMapIteration2D &p_map_iteration);
|
||||||
static void _query_task_push_back_point_with_metadata(NavMeshPathQueryTask2D &p_query_task, const Vector2 &p_point, const Nav2D::Polygon *p_point_polygon);
|
static void _query_task_push_back_point_with_metadata(NavMeshPathQueryTask2D &p_query_task, const Vector2 &p_point, const Nav2D::Polygon *p_point_polygon);
|
||||||
static void _query_task_find_start_end_positions(NavMeshPathQueryTask2D &p_query_task, const NavMapIteration2D &p_map_iteration);
|
static void _query_task_find_start_end_positions(NavMeshPathQueryTask2D &p_query_task, const NavMapIteration2D &p_map_iteration);
|
||||||
static void _query_task_build_path_corridor(NavMeshPathQueryTask2D &p_query_task);
|
static void _query_task_build_path_corridor(NavMeshPathQueryTask2D &p_query_task, const NavMapIteration2D &p_map_iteration);
|
||||||
static void _query_task_post_process_corridorfunnel(NavMeshPathQueryTask2D &p_query_task);
|
static void _query_task_post_process_corridorfunnel(NavMeshPathQueryTask2D &p_query_task);
|
||||||
static void _query_task_post_process_edgecentered(NavMeshPathQueryTask2D &p_query_task);
|
static void _query_task_post_process_edgecentered(NavMeshPathQueryTask2D &p_query_task);
|
||||||
static void _query_task_post_process_nopostprocessing(NavMeshPathQueryTask2D &p_query_task);
|
static void _query_task_post_process_nopostprocessing(NavMeshPathQueryTask2D &p_query_task);
|
||||||
@ -136,6 +139,8 @@ public:
|
|||||||
static void _query_task_simplified_path_points(NavMeshPathQueryTask2D &p_query_task);
|
static void _query_task_simplified_path_points(NavMeshPathQueryTask2D &p_query_task);
|
||||||
static bool _query_task_is_connection_owner_usable(const NavMeshPathQueryTask2D &p_query_task, const NavBaseIteration2D *p_owner);
|
static bool _query_task_is_connection_owner_usable(const NavMeshPathQueryTask2D &p_query_task, const NavBaseIteration2D *p_owner);
|
||||||
|
|
||||||
|
static void _query_task_search_polygon_connections(NavMeshPathQueryTask2D &p_query_task, const Nav2D::Connection &p_connection, uint32_t p_least_cost_id, const Nav2D::NavigationPoly &p_least_cost_poly, real_t p_poly_enter_cost, const Vector2 &p_end_point);
|
||||||
|
|
||||||
static void simplify_path_segment(int p_start_inx, int p_end_inx, const LocalVector<Vector2> &p_points, real_t p_epsilon, LocalVector<uint32_t> &r_simplified_path_indices);
|
static void simplify_path_segment(int p_start_inx, int p_end_inx, const LocalVector<Vector2> &p_points, real_t p_epsilon, LocalVector<uint32_t> &r_simplified_path_indices);
|
||||||
static LocalVector<uint32_t> get_simplified_path_indices(const LocalVector<Vector2> &p_path, real_t p_epsilon);
|
static LocalVector<uint32_t> get_simplified_path_indices(const LocalVector<Vector2> &p_path, real_t p_epsilon);
|
||||||
};
|
};
|
||||||
|
255
modules/navigation_2d/2d/nav_region_builder_2d.cpp
Normal file
255
modules/navigation_2d/2d/nav_region_builder_2d.cpp
Normal file
@ -0,0 +1,255 @@
|
|||||||
|
/**************************************************************************/
|
||||||
|
/* nav_region_builder_2d.cpp */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||||
|
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* */
|
||||||
|
/* 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 "nav_region_builder_2d.h"
|
||||||
|
|
||||||
|
#include "../nav_map_2d.h"
|
||||||
|
#include "../nav_region_2d.h"
|
||||||
|
#include "../triangle2.h"
|
||||||
|
#include "nav_region_iteration_2d.h"
|
||||||
|
|
||||||
|
using namespace Nav2D;
|
||||||
|
|
||||||
|
void NavRegionBuilder2D::build_iteration(NavRegionIterationBuild2D &r_build) {
|
||||||
|
PerformanceData &performance_data = r_build.performance_data;
|
||||||
|
|
||||||
|
performance_data.pm_polygon_count = 0;
|
||||||
|
performance_data.pm_edge_count = 0;
|
||||||
|
performance_data.pm_edge_merge_count = 0;
|
||||||
|
performance_data.pm_edge_connection_count = 0;
|
||||||
|
performance_data.pm_edge_free_count = 0;
|
||||||
|
|
||||||
|
_build_step_process_navmesh_data(r_build);
|
||||||
|
|
||||||
|
_build_step_find_edge_connection_pairs(r_build);
|
||||||
|
|
||||||
|
_build_step_merge_edge_connection_pairs(r_build);
|
||||||
|
|
||||||
|
_build_update_iteration(r_build);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NavRegionBuilder2D::_build_step_process_navmesh_data(NavRegionIterationBuild2D &r_build) {
|
||||||
|
Vector<Vector2> _navmesh_vertices = r_build.navmesh_data.vertices;
|
||||||
|
Vector<Vector<int>> _navmesh_polygons = r_build.navmesh_data.polygons;
|
||||||
|
|
||||||
|
if (_navmesh_vertices.is_empty() || _navmesh_polygons.is_empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PerformanceData &performance_data = r_build.performance_data;
|
||||||
|
Ref<NavRegionIteration2D> region_iteration = r_build.region_iteration;
|
||||||
|
|
||||||
|
const Transform2D ®ion_transform = region_iteration->transform;
|
||||||
|
LocalVector<Nav2D::Polygon> &navmesh_polygons = region_iteration->navmesh_polygons;
|
||||||
|
|
||||||
|
const int vertex_count = _navmesh_vertices.size();
|
||||||
|
|
||||||
|
const Vector2 *vertices_ptr = _navmesh_vertices.ptr();
|
||||||
|
const Vector<int> *polygons_ptr = _navmesh_polygons.ptr();
|
||||||
|
|
||||||
|
navmesh_polygons.resize(_navmesh_polygons.size());
|
||||||
|
|
||||||
|
real_t _new_region_surface_area = 0.0;
|
||||||
|
Rect2 _new_region_bounds;
|
||||||
|
|
||||||
|
bool first_vertex = true;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < navmesh_polygons.size(); i++) {
|
||||||
|
Polygon &polygon = navmesh_polygons[i];
|
||||||
|
polygon.id = i;
|
||||||
|
polygon.owner = region_iteration.ptr();
|
||||||
|
polygon.surface_area = 0.0;
|
||||||
|
|
||||||
|
Vector<int> polygon_indices = polygons_ptr[i];
|
||||||
|
|
||||||
|
int polygon_size = polygon_indices.size();
|
||||||
|
if (polygon_size < 3) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int *indices_ptr = polygon_indices.ptr();
|
||||||
|
|
||||||
|
bool polygon_valid = true;
|
||||||
|
|
||||||
|
polygon.vertices.resize(polygon_size);
|
||||||
|
|
||||||
|
{
|
||||||
|
real_t _new_polygon_surface_area = 0.0;
|
||||||
|
|
||||||
|
for (int j(2); j < polygon_size; j++) {
|
||||||
|
const Triangle2 triangle = Triangle2(
|
||||||
|
region_transform.xform(vertices_ptr[indices_ptr[0]]),
|
||||||
|
region_transform.xform(vertices_ptr[indices_ptr[j - 1]]),
|
||||||
|
region_transform.xform(vertices_ptr[indices_ptr[j]]));
|
||||||
|
|
||||||
|
_new_polygon_surface_area += triangle.get_area();
|
||||||
|
}
|
||||||
|
|
||||||
|
polygon.surface_area = _new_polygon_surface_area;
|
||||||
|
_new_region_surface_area += _new_polygon_surface_area;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int j(0); j < polygon_size; j++) {
|
||||||
|
int vertex_index = indices_ptr[j];
|
||||||
|
if (vertex_index < 0 || vertex_index >= vertex_count) {
|
||||||
|
polygon_valid = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Vector2 point_position = region_transform.xform(vertices_ptr[vertex_index]);
|
||||||
|
polygon.vertices[j] = point_position;
|
||||||
|
|
||||||
|
if (first_vertex) {
|
||||||
|
first_vertex = false;
|
||||||
|
_new_region_bounds.position = point_position;
|
||||||
|
} else {
|
||||||
|
_new_region_bounds.expand_to(point_position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!polygon_valid) {
|
||||||
|
polygon.surface_area = 0.0;
|
||||||
|
polygon.vertices.clear();
|
||||||
|
ERR_FAIL_COND_MSG(!polygon_valid, "Corrupted navigation mesh set on region. The indices of a polygon are out of range.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
region_iteration->surface_area = _new_region_surface_area;
|
||||||
|
region_iteration->bounds = _new_region_bounds;
|
||||||
|
|
||||||
|
performance_data.pm_polygon_count = navmesh_polygons.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
Nav2D::PointKey NavRegionBuilder2D::get_point_key(const Vector2 &p_pos, const Vector2 &p_cell_size) {
|
||||||
|
const int x = static_cast<int>(Math::floor(p_pos.x / p_cell_size.x));
|
||||||
|
const int y = static_cast<int>(Math::floor(p_pos.y / p_cell_size.y));
|
||||||
|
|
||||||
|
PointKey p;
|
||||||
|
p.key = 0;
|
||||||
|
p.x = x;
|
||||||
|
p.y = y;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
Nav2D::EdgeKey NavRegionBuilder2D::get_edge_key(const Vector2 &p_vertex1, const Vector2 &p_vertex2, const Vector2 &p_cell_size) {
|
||||||
|
EdgeKey ek(get_point_key(p_vertex1, p_cell_size), get_point_key(p_vertex2, p_cell_size));
|
||||||
|
return ek;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NavRegionBuilder2D::_build_step_find_edge_connection_pairs(NavRegionIterationBuild2D &r_build) {
|
||||||
|
PerformanceData &performance_data = r_build.performance_data;
|
||||||
|
|
||||||
|
const Vector2 &map_cell_size = r_build.map_cell_size;
|
||||||
|
Ref<NavRegionIteration2D> region_iteration = r_build.region_iteration;
|
||||||
|
LocalVector<Nav2D::Polygon> &navmesh_polygons = region_iteration->navmesh_polygons;
|
||||||
|
|
||||||
|
HashMap<EdgeKey, EdgeConnectionPair, EdgeKey> &connection_pairs_map = r_build.iter_connection_pairs_map;
|
||||||
|
connection_pairs_map.clear();
|
||||||
|
|
||||||
|
region_iteration->internal_connections.clear();
|
||||||
|
region_iteration->internal_connections.resize(navmesh_polygons.size());
|
||||||
|
|
||||||
|
region_iteration->external_edges.clear();
|
||||||
|
|
||||||
|
int free_edges_count = 0;
|
||||||
|
|
||||||
|
for (Polygon &poly : region_iteration->navmesh_polygons) {
|
||||||
|
for (uint32_t p = 0; p < poly.vertices.size(); p++) {
|
||||||
|
const int next_point = (p + 1) % poly.vertices.size();
|
||||||
|
const EdgeKey ek = get_edge_key(poly.vertices[p], poly.vertices[next_point], map_cell_size);
|
||||||
|
|
||||||
|
HashMap<EdgeKey, EdgeConnectionPair, EdgeKey>::Iterator pair_it = connection_pairs_map.find(ek);
|
||||||
|
if (!pair_it) {
|
||||||
|
pair_it = connection_pairs_map.insert(ek, EdgeConnectionPair());
|
||||||
|
performance_data.pm_edge_count += 1;
|
||||||
|
++free_edges_count;
|
||||||
|
}
|
||||||
|
EdgeConnectionPair &pair = pair_it->value;
|
||||||
|
if (pair.size < 2) {
|
||||||
|
// Add the polygon/edge tuple to this key.
|
||||||
|
Connection new_connection;
|
||||||
|
new_connection.polygon = &poly;
|
||||||
|
new_connection.edge = p;
|
||||||
|
new_connection.pathway_start = poly.vertices[p];
|
||||||
|
new_connection.pathway_end = poly.vertices[next_point];
|
||||||
|
|
||||||
|
pair.connections[pair.size] = new_connection;
|
||||||
|
++pair.size;
|
||||||
|
if (pair.size == 2) {
|
||||||
|
--free_edges_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// The edge is already connected with another edge, skip.
|
||||||
|
ERR_FAIL_COND_MSG(pair.size >= 2, "Navigation region synchronization error. More than 2 edges tried to occupy the same map rasterization space. This is a logical error in the navigation mesh caused by overlap or too densely placed edges.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
performance_data.pm_edge_free_count = free_edges_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NavRegionBuilder2D::_build_step_merge_edge_connection_pairs(NavRegionIterationBuild2D &r_build) {
|
||||||
|
PerformanceData &performance_data = r_build.performance_data;
|
||||||
|
|
||||||
|
Ref<NavRegionIteration2D> region_iteration = r_build.region_iteration;
|
||||||
|
|
||||||
|
HashMap<EdgeKey, EdgeConnectionPair, EdgeKey> &connection_pairs_map = r_build.iter_connection_pairs_map;
|
||||||
|
|
||||||
|
for (const KeyValue<EdgeKey, EdgeConnectionPair> &pair_it : connection_pairs_map) {
|
||||||
|
const EdgeConnectionPair &pair = pair_it.value;
|
||||||
|
if (pair.size == 2) {
|
||||||
|
// Connect edge that are shared in different polygons.
|
||||||
|
const Connection &c1 = pair.connections[0];
|
||||||
|
const Connection &c2 = pair.connections[1];
|
||||||
|
region_iteration->internal_connections[c1.polygon->id].push_back(c2);
|
||||||
|
region_iteration->internal_connections[c2.polygon->id].push_back(c1);
|
||||||
|
performance_data.pm_edge_merge_count += 1;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ERR_FAIL_COND_MSG(pair.size != 1, vformat("Number of connection != 1. Found: %d", pair.size));
|
||||||
|
|
||||||
|
const Connection &connection = pair.connections[0];
|
||||||
|
|
||||||
|
ConnectableEdge ce;
|
||||||
|
ce.ek = pair_it.key;
|
||||||
|
ce.polygon_index = connection.polygon->id;
|
||||||
|
ce.pathway_start = connection.pathway_start;
|
||||||
|
ce.pathway_end = connection.pathway_end;
|
||||||
|
|
||||||
|
region_iteration->external_edges.push_back(ce);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NavRegionBuilder2D::_build_update_iteration(NavRegionIterationBuild2D &r_build) {
|
||||||
|
ERR_FAIL_NULL(r_build.region);
|
||||||
|
// Stub. End of the build.
|
||||||
|
}
|
48
modules/navigation_2d/2d/nav_region_builder_2d.h
Normal file
48
modules/navigation_2d/2d/nav_region_builder_2d.h
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/**************************************************************************/
|
||||||
|
/* nav_region_builder_2d.h */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||||
|
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* */
|
||||||
|
/* 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. */
|
||||||
|
/**************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../nav_utils_2d.h"
|
||||||
|
|
||||||
|
struct NavRegionIterationBuild2D;
|
||||||
|
|
||||||
|
class NavRegionBuilder2D {
|
||||||
|
static void _build_step_process_navmesh_data(NavRegionIterationBuild2D &r_build);
|
||||||
|
static void _build_step_find_edge_connection_pairs(NavRegionIterationBuild2D &r_build);
|
||||||
|
static void _build_step_merge_edge_connection_pairs(NavRegionIterationBuild2D &r_build);
|
||||||
|
static void _build_update_iteration(NavRegionIterationBuild2D &r_build);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static Nav2D::PointKey get_point_key(const Vector2 &p_pos, const Vector2 &p_cell_size);
|
||||||
|
static Nav2D::EdgeKey get_edge_key(const Vector2 &p_vertex1, const Vector2 &p_vertex2, const Vector2 &p_cell_size);
|
||||||
|
|
||||||
|
static void build_iteration(NavRegionIterationBuild2D &r_build);
|
||||||
|
};
|
@ -32,15 +32,61 @@
|
|||||||
|
|
||||||
#include "../nav_utils_2d.h"
|
#include "../nav_utils_2d.h"
|
||||||
#include "nav_base_iteration_2d.h"
|
#include "nav_base_iteration_2d.h"
|
||||||
|
#include "scene/resources/2d/navigation_polygon.h"
|
||||||
|
|
||||||
#include "core/math/rect2.h"
|
#include "core/math/rect2.h"
|
||||||
|
|
||||||
struct NavRegionIteration2D : NavBaseIteration2D {
|
class NavRegion2D;
|
||||||
|
class NavRegionIteration2D;
|
||||||
|
|
||||||
|
struct NavRegionIterationBuild2D {
|
||||||
|
Nav2D::PerformanceData performance_data;
|
||||||
|
|
||||||
|
NavRegion2D *region = nullptr;
|
||||||
|
|
||||||
|
Vector2 map_cell_size;
|
||||||
|
Transform2D region_transform;
|
||||||
|
|
||||||
|
struct NavMeshData {
|
||||||
|
Vector<Vector2> vertices;
|
||||||
|
Vector<Vector<int>> polygons;
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
vertices.clear();
|
||||||
|
polygons.clear();
|
||||||
|
}
|
||||||
|
} navmesh_data;
|
||||||
|
|
||||||
|
Ref<NavRegionIteration2D> region_iteration;
|
||||||
|
|
||||||
|
HashMap<Nav2D::EdgeKey, Nav2D::EdgeConnectionPair, Nav2D::EdgeKey> iter_connection_pairs_map;
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
performance_data.reset();
|
||||||
|
|
||||||
|
navmesh_data.clear();
|
||||||
|
region_iteration = Ref<NavRegionIteration2D>();
|
||||||
|
iter_connection_pairs_map.clear();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class NavRegionIteration2D : public NavBaseIteration2D {
|
||||||
|
GDCLASS(NavRegionIteration2D, NavBaseIteration2D);
|
||||||
|
|
||||||
|
public:
|
||||||
Transform2D transform;
|
Transform2D transform;
|
||||||
real_t surface_area = 0.0;
|
real_t surface_area = 0.0;
|
||||||
Rect2 bounds;
|
Rect2 bounds;
|
||||||
|
LocalVector<Nav2D::ConnectableEdge> external_edges;
|
||||||
|
|
||||||
const Transform2D &get_transform() const { return transform; }
|
const Transform2D &get_transform() const { return transform; }
|
||||||
real_t get_surface_area() const { return surface_area; }
|
real_t get_surface_area() const { return surface_area; }
|
||||||
Rect2 get_bounds() const { return bounds; }
|
Rect2 get_bounds() const { return bounds; }
|
||||||
|
const LocalVector<Nav2D::ConnectableEdge> &get_external_edges() const { return external_edges; }
|
||||||
|
|
||||||
|
virtual ~NavRegionIteration2D() override {
|
||||||
|
external_edges.clear();
|
||||||
|
navmesh_polygons.clear();
|
||||||
|
internal_connections.clear();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
@ -44,7 +44,7 @@ void NavLink2D::set_map(NavMap2D *p_map) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
map = p_map;
|
map = p_map;
|
||||||
link_dirty = true;
|
iteration_dirty = true;
|
||||||
|
|
||||||
if (map) {
|
if (map) {
|
||||||
map->add_link(this);
|
map->add_link(this);
|
||||||
@ -57,9 +57,7 @@ void NavLink2D::set_enabled(bool p_enabled) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
enabled = p_enabled;
|
enabled = p_enabled;
|
||||||
|
iteration_dirty = true;
|
||||||
// TODO: This should not require a full rebuild as the link has not really changed.
|
|
||||||
link_dirty = true;
|
|
||||||
|
|
||||||
request_sync();
|
request_sync();
|
||||||
}
|
}
|
||||||
@ -69,27 +67,27 @@ void NavLink2D::set_bidirectional(bool p_bidirectional) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
bidirectional = p_bidirectional;
|
bidirectional = p_bidirectional;
|
||||||
link_dirty = true;
|
iteration_dirty = true;
|
||||||
|
|
||||||
request_sync();
|
request_sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavLink2D::set_start_position(const Vector2 &p_position) {
|
void NavLink2D::set_start_position(const Vector2 p_position) {
|
||||||
if (start_position == p_position) {
|
if (start_position == p_position) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
start_position = p_position;
|
start_position = p_position;
|
||||||
link_dirty = true;
|
iteration_dirty = true;
|
||||||
|
|
||||||
request_sync();
|
request_sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavLink2D::set_end_position(const Vector2 &p_position) {
|
void NavLink2D::set_end_position(const Vector2 p_position) {
|
||||||
if (end_position == p_position) {
|
if (end_position == p_position) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
end_position = p_position;
|
end_position = p_position;
|
||||||
link_dirty = true;
|
iteration_dirty = true;
|
||||||
|
|
||||||
request_sync();
|
request_sync();
|
||||||
}
|
}
|
||||||
@ -99,7 +97,7 @@ void NavLink2D::set_navigation_layers(uint32_t p_navigation_layers) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
navigation_layers = p_navigation_layers;
|
navigation_layers = p_navigation_layers;
|
||||||
link_dirty = true;
|
iteration_dirty = true;
|
||||||
|
|
||||||
request_sync();
|
request_sync();
|
||||||
}
|
}
|
||||||
@ -110,7 +108,7 @@ void NavLink2D::set_enter_cost(real_t p_enter_cost) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
enter_cost = new_enter_cost;
|
enter_cost = new_enter_cost;
|
||||||
link_dirty = true;
|
iteration_dirty = true;
|
||||||
|
|
||||||
request_sync();
|
request_sync();
|
||||||
}
|
}
|
||||||
@ -121,7 +119,7 @@ void NavLink2D::set_travel_cost(real_t p_travel_cost) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
travel_cost = new_travel_cost;
|
travel_cost = new_travel_cost;
|
||||||
link_dirty = true;
|
iteration_dirty = true;
|
||||||
|
|
||||||
request_sync();
|
request_sync();
|
||||||
}
|
}
|
||||||
@ -131,21 +129,59 @@ void NavLink2D::set_owner_id(ObjectID p_owner_id) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
owner_id = p_owner_id;
|
owner_id = p_owner_id;
|
||||||
link_dirty = true;
|
iteration_dirty = true;
|
||||||
|
|
||||||
request_sync();
|
request_sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NavLink2D::is_dirty() const {
|
bool NavLink2D::sync() {
|
||||||
return link_dirty;
|
bool requires_map_update = false;
|
||||||
}
|
if (!map) {
|
||||||
|
return requires_map_update;
|
||||||
void NavLink2D::sync() {
|
|
||||||
if (link_dirty) {
|
|
||||||
iteration_id = iteration_id % UINT32_MAX + 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
link_dirty = false;
|
if (iteration_dirty && !iteration_building && !iteration_ready) {
|
||||||
|
_build_iteration();
|
||||||
|
iteration_ready = false;
|
||||||
|
requires_map_update = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return requires_map_update;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NavLink2D::_build_iteration() {
|
||||||
|
if (!iteration_dirty || iteration_building || iteration_ready) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
iteration_dirty = false;
|
||||||
|
iteration_building = true;
|
||||||
|
iteration_ready = false;
|
||||||
|
|
||||||
|
Ref<NavLinkIteration2D> new_iteration;
|
||||||
|
new_iteration.instantiate();
|
||||||
|
|
||||||
|
new_iteration->navigation_layers = get_navigation_layers();
|
||||||
|
new_iteration->enter_cost = get_enter_cost();
|
||||||
|
new_iteration->travel_cost = get_travel_cost();
|
||||||
|
new_iteration->owner_object_id = get_owner_id();
|
||||||
|
new_iteration->owner_type = get_type();
|
||||||
|
new_iteration->owner_rid = get_self();
|
||||||
|
|
||||||
|
new_iteration->enabled = get_enabled();
|
||||||
|
new_iteration->start_position = get_start_position();
|
||||||
|
new_iteration->end_position = get_end_position();
|
||||||
|
new_iteration->bidirectional = is_bidirectional();
|
||||||
|
|
||||||
|
RWLockWrite write_lock(iteration_rwlock);
|
||||||
|
ERR_FAIL_COND(iteration.is_null());
|
||||||
|
iteration = Ref<NavLinkIteration2D>();
|
||||||
|
DEV_ASSERT(iteration.is_null());
|
||||||
|
iteration = new_iteration;
|
||||||
|
iteration_id = iteration_id % UINT32_MAX + 1;
|
||||||
|
|
||||||
|
iteration_building = false;
|
||||||
|
iteration_ready = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavLink2D::request_sync() {
|
void NavLink2D::request_sync() {
|
||||||
@ -160,27 +196,19 @@ void NavLink2D::cancel_sync_request() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ref<NavLinkIteration2D> NavLink2D::get_iteration() {
|
||||||
|
RWLockRead read_lock(iteration_rwlock);
|
||||||
|
return iteration;
|
||||||
|
}
|
||||||
|
|
||||||
NavLink2D::NavLink2D() :
|
NavLink2D::NavLink2D() :
|
||||||
sync_dirty_request_list_element(this) {
|
sync_dirty_request_list_element(this) {
|
||||||
type = NavigationUtilities::PathSegmentType::PATH_SEGMENT_TYPE_LINK;
|
type = NavigationUtilities::PathSegmentType::PATH_SEGMENT_TYPE_LINK;
|
||||||
|
iteration.instantiate();
|
||||||
}
|
}
|
||||||
|
|
||||||
NavLink2D::~NavLink2D() {
|
NavLink2D::~NavLink2D() {
|
||||||
cancel_sync_request();
|
cancel_sync_request();
|
||||||
}
|
|
||||||
|
iteration = Ref<NavLinkIteration2D>();
|
||||||
void NavLink2D::get_iteration_update(NavLinkIteration2D &r_iteration) {
|
|
||||||
r_iteration.navigation_layers = get_navigation_layers();
|
|
||||||
r_iteration.enter_cost = get_enter_cost();
|
|
||||||
r_iteration.travel_cost = get_travel_cost();
|
|
||||||
r_iteration.owner_object_id = get_owner_id();
|
|
||||||
r_iteration.owner_type = get_type();
|
|
||||||
r_iteration.owner_rid = get_self();
|
|
||||||
|
|
||||||
r_iteration.enabled = get_enabled();
|
|
||||||
r_iteration.start_position = get_start_position();
|
|
||||||
r_iteration.end_position = get_end_position();
|
|
||||||
r_iteration.bidirectional = is_bidirectional();
|
|
||||||
|
|
||||||
r_iteration.navmesh_polygons.clear();
|
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,10 @@
|
|||||||
#include "nav_base_2d.h"
|
#include "nav_base_2d.h"
|
||||||
#include "nav_utils_2d.h"
|
#include "nav_utils_2d.h"
|
||||||
|
|
||||||
struct NavLinkIteration2D : NavBaseIteration2D {
|
class NavLinkIteration2D : public NavBaseIteration2D {
|
||||||
|
GDCLASS(NavLinkIteration2D, NavBaseIteration2D);
|
||||||
|
|
||||||
|
public:
|
||||||
bool bidirectional = true;
|
bool bidirectional = true;
|
||||||
Vector2 start_position;
|
Vector2 start_position;
|
||||||
Vector2 end_position;
|
Vector2 end_position;
|
||||||
@ -42,6 +45,11 @@ struct NavLinkIteration2D : NavBaseIteration2D {
|
|||||||
Vector2 get_start_position() const { return start_position; }
|
Vector2 get_start_position() const { return start_position; }
|
||||||
Vector2 get_end_position() const { return end_position; }
|
Vector2 get_end_position() const { return end_position; }
|
||||||
bool is_bidirectional() const { return bidirectional; }
|
bool is_bidirectional() const { return bidirectional; }
|
||||||
|
|
||||||
|
virtual ~NavLinkIteration2D() override {
|
||||||
|
navmesh_polygons.clear();
|
||||||
|
internal_connections.clear();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#include "core/templates/self_list.h"
|
#include "core/templates/self_list.h"
|
||||||
@ -53,12 +61,20 @@ class NavLink2D : public NavBase2D {
|
|||||||
Vector2 end_position;
|
Vector2 end_position;
|
||||||
bool enabled = true;
|
bool enabled = true;
|
||||||
|
|
||||||
bool link_dirty = true;
|
|
||||||
|
|
||||||
SelfList<NavLink2D> sync_dirty_request_list_element;
|
SelfList<NavLink2D> sync_dirty_request_list_element;
|
||||||
|
|
||||||
uint32_t iteration_id = 0;
|
uint32_t iteration_id = 0;
|
||||||
|
|
||||||
|
mutable RWLock iteration_rwlock;
|
||||||
|
Ref<NavLinkIteration2D> iteration;
|
||||||
|
|
||||||
|
bool iteration_dirty = true;
|
||||||
|
bool iteration_building = false;
|
||||||
|
bool iteration_ready = false;
|
||||||
|
|
||||||
|
void _build_iteration();
|
||||||
|
void _sync_iteration();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
NavLink2D();
|
NavLink2D();
|
||||||
~NavLink2D();
|
~NavLink2D();
|
||||||
@ -78,12 +94,12 @@ public:
|
|||||||
return bidirectional;
|
return bidirectional;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_start_position(const Vector2 &p_position);
|
void set_start_position(Vector2 p_position);
|
||||||
Vector2 get_start_position() const {
|
Vector2 get_start_position() const {
|
||||||
return start_position;
|
return start_position;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_end_position(const Vector2 &p_position);
|
void set_end_position(Vector2 p_position);
|
||||||
Vector2 get_end_position() const {
|
Vector2 get_end_position() const {
|
||||||
return end_position;
|
return end_position;
|
||||||
}
|
}
|
||||||
@ -94,10 +110,9 @@ public:
|
|||||||
virtual void set_travel_cost(real_t p_travel_cost) override;
|
virtual void set_travel_cost(real_t p_travel_cost) override;
|
||||||
virtual void set_owner_id(ObjectID p_owner_id) override;
|
virtual void set_owner_id(ObjectID p_owner_id) override;
|
||||||
|
|
||||||
bool is_dirty() const;
|
bool sync();
|
||||||
void sync();
|
|
||||||
void request_sync();
|
void request_sync();
|
||||||
void cancel_sync_request();
|
void cancel_sync_request();
|
||||||
|
|
||||||
void get_iteration_update(NavLinkIteration2D &r_iteration);
|
Ref<NavLinkIteration2D> get_iteration();
|
||||||
};
|
};
|
||||||
|
@ -109,7 +109,7 @@ void NavMap2D::set_link_connection_radius(real_t p_link_connection_radius) {
|
|||||||
iteration_dirty = true;
|
iteration_dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2 NavMap2D::get_merge_rasterizer_cell_size() const {
|
const Vector2 &NavMap2D::get_merge_rasterizer_cell_size() const {
|
||||||
return merge_rasterizer_cell_size;
|
return merge_rasterizer_cell_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,6 +188,8 @@ ClosestPointQueryResult NavMap2D::get_closest_point_info(const Vector2 &p_point)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void NavMap2D::add_region(NavRegion2D *p_region) {
|
void NavMap2D::add_region(NavRegion2D *p_region) {
|
||||||
|
DEV_ASSERT(!regions.has(p_region));
|
||||||
|
|
||||||
regions.push_back(p_region);
|
regions.push_back(p_region);
|
||||||
iteration_dirty = true;
|
iteration_dirty = true;
|
||||||
}
|
}
|
||||||
@ -199,6 +201,8 @@ void NavMap2D::remove_region(NavRegion2D *p_region) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void NavMap2D::add_link(NavLink2D *p_link) {
|
void NavMap2D::add_link(NavLink2D *p_link) {
|
||||||
|
DEV_ASSERT(!links.has(p_link));
|
||||||
|
|
||||||
links.push_back(p_link);
|
links.push_back(p_link);
|
||||||
iteration_dirty = true;
|
iteration_dirty = true;
|
||||||
}
|
}
|
||||||
@ -311,49 +315,22 @@ void NavMap2D::_build_iteration() {
|
|||||||
iteration_build.edge_connection_margin = get_edge_connection_margin();
|
iteration_build.edge_connection_margin = get_edge_connection_margin();
|
||||||
iteration_build.link_connection_radius = get_link_connection_radius();
|
iteration_build.link_connection_radius = get_link_connection_radius();
|
||||||
|
|
||||||
uint32_t enabled_region_count = 0;
|
next_map_iteration.clear();
|
||||||
uint32_t enabled_link_count = 0;
|
|
||||||
|
|
||||||
for (NavRegion2D *region : regions) {
|
next_map_iteration.region_iterations.resize(regions.size());
|
||||||
if (!region->get_enabled()) {
|
next_map_iteration.link_iterations.resize(links.size());
|
||||||
continue;
|
|
||||||
}
|
|
||||||
enabled_region_count++;
|
|
||||||
}
|
|
||||||
for (NavLink2D *link : links) {
|
|
||||||
if (!link->get_enabled()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
enabled_link_count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
next_map_iteration.region_ptr_to_region_id.clear();
|
|
||||||
|
|
||||||
next_map_iteration.region_iterations.clear();
|
|
||||||
next_map_iteration.link_iterations.clear();
|
|
||||||
|
|
||||||
next_map_iteration.region_iterations.resize(enabled_region_count);
|
|
||||||
next_map_iteration.link_iterations.resize(enabled_link_count);
|
|
||||||
|
|
||||||
uint32_t region_id_count = 0;
|
uint32_t region_id_count = 0;
|
||||||
uint32_t link_id_count = 0;
|
uint32_t link_id_count = 0;
|
||||||
|
|
||||||
for (NavRegion2D *region : regions) {
|
for (NavRegion2D *region : regions) {
|
||||||
if (!region->get_enabled()) {
|
const Ref<NavRegionIteration2D> region_iteration = region->get_iteration();
|
||||||
continue;
|
next_map_iteration.region_iterations[region_id_count++] = region_iteration;
|
||||||
}
|
next_map_iteration.region_ptr_to_region_iteration[region] = region_iteration;
|
||||||
NavRegionIteration2D ®ion_iteration = next_map_iteration.region_iterations[region_id_count];
|
|
||||||
region_iteration.id = region_id_count++;
|
|
||||||
region->get_iteration_update(region_iteration);
|
|
||||||
next_map_iteration.region_ptr_to_region_id[region] = (uint32_t)region_iteration.id;
|
|
||||||
}
|
}
|
||||||
for (NavLink2D *link : links) {
|
for (NavLink2D *link : links) {
|
||||||
if (!link->get_enabled()) {
|
const Ref<NavLinkIteration2D> link_iteration = link->get_iteration();
|
||||||
continue;
|
next_map_iteration.link_iterations[link_id_count++] = link_iteration;
|
||||||
}
|
|
||||||
NavLinkIteration2D &link_iteration = next_map_iteration.link_iterations[link_id_count];
|
|
||||||
link_iteration.id = link_id_count++;
|
|
||||||
link->get_iteration_update(link_iteration);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
iteration_build.map_iteration = &next_map_iteration;
|
iteration_build.map_iteration = &next_map_iteration;
|
||||||
@ -379,9 +356,6 @@ void NavMap2D::_sync_iteration() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
performance_data.pm_polygon_count = iteration_build.performance_data.pm_polygon_count;
|
|
||||||
performance_data.pm_edge_count = iteration_build.performance_data.pm_edge_count;
|
|
||||||
performance_data.pm_edge_merge_count = iteration_build.performance_data.pm_edge_merge_count;
|
|
||||||
performance_data.pm_edge_connection_count = iteration_build.performance_data.pm_edge_connection_count;
|
performance_data.pm_edge_connection_count = iteration_build.performance_data.pm_edge_connection_count;
|
||||||
performance_data.pm_edge_free_count = iteration_build.performance_data.pm_edge_free_count;
|
performance_data.pm_edge_free_count = iteration_build.performance_data.pm_edge_free_count;
|
||||||
|
|
||||||
@ -403,6 +377,8 @@ void NavMap2D::sync() {
|
|||||||
performance_data.pm_link_count = links.size();
|
performance_data.pm_link_count = links.size();
|
||||||
performance_data.pm_obstacle_count = obstacles.size();
|
performance_data.pm_obstacle_count = obstacles.size();
|
||||||
|
|
||||||
|
_sync_async_tasks();
|
||||||
|
|
||||||
_sync_dirty_map_update_requests();
|
_sync_dirty_map_update_requests();
|
||||||
|
|
||||||
if (iteration_dirty && !iteration_building && !iteration_ready) {
|
if (iteration_dirty && !iteration_building && !iteration_ready) {
|
||||||
@ -426,6 +402,16 @@ void NavMap2D::sync() {
|
|||||||
map_settings_dirty = false;
|
map_settings_dirty = false;
|
||||||
|
|
||||||
_sync_avoidance();
|
_sync_avoidance();
|
||||||
|
|
||||||
|
performance_data.pm_polygon_count = 0;
|
||||||
|
performance_data.pm_edge_count = 0;
|
||||||
|
performance_data.pm_edge_merge_count = 0;
|
||||||
|
|
||||||
|
for (NavRegion2D *region : regions) {
|
||||||
|
performance_data.pm_polygon_count += region->get_pm_polygon_count();
|
||||||
|
performance_data.pm_edge_count += region->get_pm_edge_count();
|
||||||
|
performance_data.pm_edge_merge_count += region->get_pm_edge_merge_count();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavMap2D::_sync_avoidance() {
|
void NavMap2D::_sync_avoidance() {
|
||||||
@ -569,9 +555,9 @@ int NavMap2D::get_region_connections_count(NavRegion2D *p_region) const {
|
|||||||
|
|
||||||
GET_MAP_ITERATION_CONST();
|
GET_MAP_ITERATION_CONST();
|
||||||
|
|
||||||
HashMap<NavRegion2D *, uint32_t>::ConstIterator found_id = map_iteration.region_ptr_to_region_id.find(p_region);
|
HashMap<NavRegion2D *, Ref<NavRegionIteration2D>>::ConstIterator found_id = map_iteration.region_ptr_to_region_iteration.find(p_region);
|
||||||
if (found_id) {
|
if (found_id) {
|
||||||
HashMap<uint32_t, LocalVector<Edge::Connection>>::ConstIterator found_connections = map_iteration.external_region_connections.find(found_id->value);
|
HashMap<const NavBaseIteration2D *, LocalVector<Connection>>::ConstIterator found_connections = map_iteration.external_region_connections.find(found_id->value.ptr());
|
||||||
if (found_connections) {
|
if (found_connections) {
|
||||||
return found_connections->value.size();
|
return found_connections->value.size();
|
||||||
}
|
}
|
||||||
@ -585,9 +571,9 @@ Vector2 NavMap2D::get_region_connection_pathway_start(NavRegion2D *p_region, int
|
|||||||
|
|
||||||
GET_MAP_ITERATION_CONST();
|
GET_MAP_ITERATION_CONST();
|
||||||
|
|
||||||
HashMap<NavRegion2D *, uint32_t>::ConstIterator found_id = map_iteration.region_ptr_to_region_id.find(p_region);
|
HashMap<NavRegion2D *, Ref<NavRegionIteration2D>>::ConstIterator found_id = map_iteration.region_ptr_to_region_iteration.find(p_region);
|
||||||
if (found_id) {
|
if (found_id) {
|
||||||
HashMap<uint32_t, LocalVector<Edge::Connection>>::ConstIterator found_connections = map_iteration.external_region_connections.find(found_id->value);
|
HashMap<const NavBaseIteration2D *, LocalVector<Connection>>::ConstIterator found_connections = map_iteration.external_region_connections.find(found_id->value.ptr());
|
||||||
if (found_connections) {
|
if (found_connections) {
|
||||||
ERR_FAIL_INDEX_V(p_connection_id, int(found_connections->value.size()), Vector2());
|
ERR_FAIL_INDEX_V(p_connection_id, int(found_connections->value.size()), Vector2());
|
||||||
return found_connections->value[p_connection_id].pathway_start;
|
return found_connections->value[p_connection_id].pathway_start;
|
||||||
@ -602,9 +588,9 @@ Vector2 NavMap2D::get_region_connection_pathway_end(NavRegion2D *p_region, int p
|
|||||||
|
|
||||||
GET_MAP_ITERATION_CONST();
|
GET_MAP_ITERATION_CONST();
|
||||||
|
|
||||||
HashMap<NavRegion2D *, uint32_t>::ConstIterator found_id = map_iteration.region_ptr_to_region_id.find(p_region);
|
HashMap<NavRegion2D *, Ref<NavRegionIteration2D>>::ConstIterator found_id = map_iteration.region_ptr_to_region_iteration.find(p_region);
|
||||||
if (found_id) {
|
if (found_id) {
|
||||||
HashMap<uint32_t, LocalVector<Edge::Connection>>::ConstIterator found_connections = map_iteration.external_region_connections.find(found_id->value);
|
HashMap<const NavBaseIteration2D *, LocalVector<Connection>>::ConstIterator found_connections = map_iteration.external_region_connections.find(found_id->value.ptr());
|
||||||
if (found_connections) {
|
if (found_connections) {
|
||||||
ERR_FAIL_INDEX_V(p_connection_id, int(found_connections->value.size()), Vector2());
|
ERR_FAIL_INDEX_V(p_connection_id, int(found_connections->value.size()), Vector2());
|
||||||
return found_connections->value[p_connection_id].pathway_end;
|
return found_connections->value[p_connection_id].pathway_end;
|
||||||
@ -618,56 +604,60 @@ void NavMap2D::add_region_sync_dirty_request(SelfList<NavRegion2D> *p_sync_reque
|
|||||||
if (p_sync_request->in_list()) {
|
if (p_sync_request->in_list()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sync_dirty_requests.regions.add(p_sync_request);
|
RWLockWrite write_lock(sync_dirty_requests.regions.rwlock);
|
||||||
|
sync_dirty_requests.regions.list.add(p_sync_request);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavMap2D::add_link_sync_dirty_request(SelfList<NavLink2D> *p_sync_request) {
|
void NavMap2D::add_link_sync_dirty_request(SelfList<NavLink2D> *p_sync_request) {
|
||||||
if (p_sync_request->in_list()) {
|
if (p_sync_request->in_list()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sync_dirty_requests.links.add(p_sync_request);
|
RWLockWrite write_lock(sync_dirty_requests.links.rwlock);
|
||||||
|
sync_dirty_requests.links.list.add(p_sync_request);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavMap2D::add_agent_sync_dirty_request(SelfList<NavAgent2D> *p_sync_request) {
|
void NavMap2D::add_agent_sync_dirty_request(SelfList<NavAgent2D> *p_sync_request) {
|
||||||
if (p_sync_request->in_list()) {
|
if (p_sync_request->in_list()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sync_dirty_requests.agents.add(p_sync_request);
|
sync_dirty_requests.agents.list.add(p_sync_request);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavMap2D::add_obstacle_sync_dirty_request(SelfList<NavObstacle2D> *p_sync_request) {
|
void NavMap2D::add_obstacle_sync_dirty_request(SelfList<NavObstacle2D> *p_sync_request) {
|
||||||
if (p_sync_request->in_list()) {
|
if (p_sync_request->in_list()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sync_dirty_requests.obstacles.add(p_sync_request);
|
sync_dirty_requests.obstacles.list.add(p_sync_request);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavMap2D::remove_region_sync_dirty_request(SelfList<NavRegion2D> *p_sync_request) {
|
void NavMap2D::remove_region_sync_dirty_request(SelfList<NavRegion2D> *p_sync_request) {
|
||||||
if (!p_sync_request->in_list()) {
|
if (!p_sync_request->in_list()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sync_dirty_requests.regions.remove(p_sync_request);
|
RWLockWrite write_lock(sync_dirty_requests.regions.rwlock);
|
||||||
|
sync_dirty_requests.regions.list.remove(p_sync_request);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavMap2D::remove_link_sync_dirty_request(SelfList<NavLink2D> *p_sync_request) {
|
void NavMap2D::remove_link_sync_dirty_request(SelfList<NavLink2D> *p_sync_request) {
|
||||||
if (!p_sync_request->in_list()) {
|
if (!p_sync_request->in_list()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sync_dirty_requests.links.remove(p_sync_request);
|
RWLockWrite write_lock(sync_dirty_requests.links.rwlock);
|
||||||
|
sync_dirty_requests.links.list.remove(p_sync_request);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavMap2D::remove_agent_sync_dirty_request(SelfList<NavAgent2D> *p_sync_request) {
|
void NavMap2D::remove_agent_sync_dirty_request(SelfList<NavAgent2D> *p_sync_request) {
|
||||||
if (!p_sync_request->in_list()) {
|
if (!p_sync_request->in_list()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sync_dirty_requests.agents.remove(p_sync_request);
|
sync_dirty_requests.agents.list.remove(p_sync_request);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavMap2D::remove_obstacle_sync_dirty_request(SelfList<NavObstacle2D> *p_sync_request) {
|
void NavMap2D::remove_obstacle_sync_dirty_request(SelfList<NavObstacle2D> *p_sync_request) {
|
||||||
if (!p_sync_request->in_list()) {
|
if (!p_sync_request->in_list()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sync_dirty_requests.obstacles.remove(p_sync_request);
|
sync_dirty_requests.obstacles.list.remove(p_sync_request);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavMap2D::_sync_dirty_map_update_requests() {
|
void NavMap2D::_sync_dirty_map_update_requests() {
|
||||||
@ -679,41 +669,69 @@ void NavMap2D::_sync_dirty_map_update_requests() {
|
|||||||
iteration_dirty = true;
|
iteration_dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!iteration_dirty) {
|
|
||||||
iteration_dirty = sync_dirty_requests.regions.first() || sync_dirty_requests.links.first();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sync NavRegions.
|
// Sync NavRegions.
|
||||||
for (SelfList<NavRegion2D> *element = sync_dirty_requests.regions.first(); element; element = element->next()) {
|
RWLockWrite write_lock_regions(sync_dirty_requests.regions.rwlock);
|
||||||
element->self()->sync();
|
for (SelfList<NavRegion2D> *element = sync_dirty_requests.regions.list.first(); element; element = element->next()) {
|
||||||
|
bool requires_map_update = element->self()->sync();
|
||||||
|
if (requires_map_update) {
|
||||||
|
iteration_dirty = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
sync_dirty_requests.regions.clear();
|
sync_dirty_requests.regions.list.clear();
|
||||||
|
|
||||||
// Sync NavLinks.
|
// Sync NavLinks.
|
||||||
for (SelfList<NavLink2D> *element = sync_dirty_requests.links.first(); element; element = element->next()) {
|
RWLockWrite write_lock_links(sync_dirty_requests.links.rwlock);
|
||||||
element->self()->sync();
|
for (SelfList<NavLink2D> *element = sync_dirty_requests.links.list.first(); element; element = element->next()) {
|
||||||
|
bool requires_map_update = element->self()->sync();
|
||||||
|
if (requires_map_update) {
|
||||||
|
iteration_dirty = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
sync_dirty_requests.links.clear();
|
sync_dirty_requests.links.list.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavMap2D::_sync_dirty_avoidance_update_requests() {
|
void NavMap2D::_sync_dirty_avoidance_update_requests() {
|
||||||
// Sync NavAgents.
|
// Sync NavAgents.
|
||||||
if (!agents_dirty) {
|
if (!agents_dirty) {
|
||||||
agents_dirty = sync_dirty_requests.agents.first();
|
agents_dirty = sync_dirty_requests.agents.list.first();
|
||||||
}
|
}
|
||||||
for (SelfList<NavAgent2D> *element = sync_dirty_requests.agents.first(); element; element = element->next()) {
|
for (SelfList<NavAgent2D> *element = sync_dirty_requests.agents.list.first(); element; element = element->next()) {
|
||||||
element->self()->sync();
|
element->self()->sync();
|
||||||
}
|
}
|
||||||
sync_dirty_requests.agents.clear();
|
sync_dirty_requests.agents.list.clear();
|
||||||
|
|
||||||
// Sync NavObstacles.
|
// Sync NavObstacles.
|
||||||
if (!obstacles_dirty) {
|
if (!obstacles_dirty) {
|
||||||
obstacles_dirty = sync_dirty_requests.obstacles.first();
|
obstacles_dirty = sync_dirty_requests.obstacles.list.first();
|
||||||
}
|
}
|
||||||
for (SelfList<NavObstacle2D> *element = sync_dirty_requests.obstacles.first(); element; element = element->next()) {
|
for (SelfList<NavObstacle2D> *element = sync_dirty_requests.obstacles.list.first(); element; element = element->next()) {
|
||||||
element->self()->sync();
|
element->self()->sync();
|
||||||
}
|
}
|
||||||
sync_dirty_requests.obstacles.clear();
|
sync_dirty_requests.obstacles.list.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NavMap2D::add_region_async_thread_join_request(SelfList<NavRegion2D> *p_async_request) {
|
||||||
|
if (p_async_request->in_list()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
RWLockWrite write_lock(async_dirty_requests.regions.rwlock);
|
||||||
|
async_dirty_requests.regions.list.add(p_async_request);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NavMap2D::remove_region_async_thread_join_request(SelfList<NavRegion2D> *p_async_request) {
|
||||||
|
if (!p_async_request->in_list()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
RWLockWrite write_lock(async_dirty_requests.regions.rwlock);
|
||||||
|
async_dirty_requests.regions.list.remove(p_async_request);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NavMap2D::_sync_async_tasks() {
|
||||||
|
// Sync NavRegions that run async thread tasks.
|
||||||
|
RWLockWrite write_lock_regions(async_dirty_requests.regions.rwlock);
|
||||||
|
for (SelfList<NavRegion2D> *element = async_dirty_requests.regions.list.first(); element; element = element->next()) {
|
||||||
|
element->self()->sync_async_tasks();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavMap2D::set_use_async_iterations(bool p_enabled) {
|
void NavMap2D::set_use_async_iterations(bool p_enabled) {
|
||||||
@ -768,4 +786,9 @@ NavMap2D::~NavMap2D() {
|
|||||||
WorkerThreadPool::get_singleton()->wait_for_task_completion(iteration_build_thread_task_id);
|
WorkerThreadPool::get_singleton()->wait_for_task_completion(iteration_build_thread_task_id);
|
||||||
iteration_build_thread_task_id = WorkerThreadPool::INVALID_TASK_ID;
|
iteration_build_thread_task_id = WorkerThreadPool::INVALID_TASK_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RWLockWrite write_lock(iteration_slot_rwlock);
|
||||||
|
for (NavMapIteration2D &iteration_slot : iteration_slots) {
|
||||||
|
iteration_slot.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,12 +102,31 @@ class NavMap2D : public NavRid2D {
|
|||||||
Nav2D::PerformanceData performance_data;
|
Nav2D::PerformanceData performance_data;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
SelfList<NavRegion2D>::List regions;
|
struct {
|
||||||
SelfList<NavLink2D>::List links;
|
RWLock rwlock;
|
||||||
SelfList<NavAgent2D>::List agents;
|
SelfList<NavRegion2D>::List list;
|
||||||
SelfList<NavObstacle2D>::List obstacles;
|
} regions;
|
||||||
|
struct {
|
||||||
|
RWLock rwlock;
|
||||||
|
SelfList<NavLink2D>::List list;
|
||||||
|
} links;
|
||||||
|
struct {
|
||||||
|
RWLock rwlock;
|
||||||
|
SelfList<NavAgent2D>::List list;
|
||||||
|
} agents;
|
||||||
|
struct {
|
||||||
|
RWLock rwlock;
|
||||||
|
SelfList<NavObstacle2D>::List list;
|
||||||
|
} obstacles;
|
||||||
} sync_dirty_requests;
|
} sync_dirty_requests;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
struct {
|
||||||
|
RWLock rwlock;
|
||||||
|
SelfList<NavRegion2D>::List list;
|
||||||
|
} regions;
|
||||||
|
} async_dirty_requests;
|
||||||
|
|
||||||
int path_query_slots_max = 4;
|
int path_query_slots_max = 4;
|
||||||
|
|
||||||
bool use_async_iterations = true;
|
bool use_async_iterations = true;
|
||||||
@ -117,7 +136,6 @@ class NavMap2D : public NavRid2D {
|
|||||||
mutable RWLock iteration_slot_rwlock;
|
mutable RWLock iteration_slot_rwlock;
|
||||||
|
|
||||||
NavMapIterationBuild2D iteration_build;
|
NavMapIterationBuild2D iteration_build;
|
||||||
bool iteration_build_use_threads = false;
|
|
||||||
WorkerThreadPool::TaskID iteration_build_thread_task_id = WorkerThreadPool::INVALID_TASK_ID;
|
WorkerThreadPool::TaskID iteration_build_thread_task_id = WorkerThreadPool::INVALID_TASK_ID;
|
||||||
static void _build_iteration_threaded(void *p_arg);
|
static void _build_iteration_threaded(void *p_arg);
|
||||||
|
|
||||||
@ -160,7 +178,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
Nav2D::PointKey get_point_key(const Vector2 &p_pos) const;
|
Nav2D::PointKey get_point_key(const Vector2 &p_pos) const;
|
||||||
Vector2 get_merge_rasterizer_cell_size() const;
|
const Vector2 &get_merge_rasterizer_cell_size() const;
|
||||||
|
|
||||||
void query_path(NavMeshQueries2D::NavMeshPathQueryTask2D &p_query_task);
|
void query_path(NavMeshQueries2D::NavMeshPathQueryTask2D &p_query_task);
|
||||||
|
|
||||||
@ -218,6 +236,9 @@ public:
|
|||||||
Vector2 get_region_connection_pathway_start(NavRegion2D *p_region, int p_connection_id) const;
|
Vector2 get_region_connection_pathway_start(NavRegion2D *p_region, int p_connection_id) const;
|
||||||
Vector2 get_region_connection_pathway_end(NavRegion2D *p_region, int p_connection_id) const;
|
Vector2 get_region_connection_pathway_end(NavRegion2D *p_region, int p_connection_id) const;
|
||||||
|
|
||||||
|
void add_region_async_thread_join_request(SelfList<NavRegion2D> *p_async_request);
|
||||||
|
void remove_region_async_thread_join_request(SelfList<NavRegion2D> *p_async_request);
|
||||||
|
|
||||||
void add_region_sync_dirty_request(SelfList<NavRegion2D> *p_sync_request);
|
void add_region_sync_dirty_request(SelfList<NavRegion2D> *p_sync_request);
|
||||||
void add_link_sync_dirty_request(SelfList<NavLink2D> *p_sync_request);
|
void add_link_sync_dirty_request(SelfList<NavLink2D> *p_sync_request);
|
||||||
void add_agent_sync_dirty_request(SelfList<NavAgent2D> *p_sync_request);
|
void add_agent_sync_dirty_request(SelfList<NavAgent2D> *p_sync_request);
|
||||||
@ -234,6 +255,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
void _sync_dirty_map_update_requests();
|
void _sync_dirty_map_update_requests();
|
||||||
void _sync_dirty_avoidance_update_requests();
|
void _sync_dirty_avoidance_update_requests();
|
||||||
|
void _sync_async_tasks();
|
||||||
|
|
||||||
void compute_single_step(uint32_t p_index, NavAgent2D **p_agent);
|
void compute_single_step(uint32_t p_index, NavAgent2D **p_agent);
|
||||||
|
|
||||||
|
@ -31,11 +31,11 @@
|
|||||||
#include "nav_region_2d.h"
|
#include "nav_region_2d.h"
|
||||||
|
|
||||||
#include "nav_map_2d.h"
|
#include "nav_map_2d.h"
|
||||||
#include "triangle2.h"
|
|
||||||
|
|
||||||
#include "2d/nav_map_builder_2d.h"
|
|
||||||
#include "2d/nav_mesh_queries_2d.h"
|
#include "2d/nav_mesh_queries_2d.h"
|
||||||
|
#include "2d/nav_region_builder_2d.h"
|
||||||
#include "2d/nav_region_iteration_2d.h"
|
#include "2d/nav_region_iteration_2d.h"
|
||||||
|
#include "core/config/project_settings.h"
|
||||||
|
|
||||||
using namespace Nav2D;
|
using namespace Nav2D;
|
||||||
|
|
||||||
@ -44,6 +44,7 @@ void NavRegion2D::set_map(NavMap2D *p_map) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cancel_async_thread_join();
|
||||||
cancel_sync_request();
|
cancel_sync_request();
|
||||||
|
|
||||||
if (map) {
|
if (map) {
|
||||||
@ -51,11 +52,14 @@ void NavRegion2D::set_map(NavMap2D *p_map) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
map = p_map;
|
map = p_map;
|
||||||
polygons_dirty = true;
|
iteration_dirty = true;
|
||||||
|
|
||||||
if (map) {
|
if (map) {
|
||||||
map->add_region(this);
|
map->add_region(this);
|
||||||
request_sync();
|
request_sync();
|
||||||
|
if (iteration_build_thread_task_id != WorkerThreadPool::INVALID_TASK_ID) {
|
||||||
|
request_async_thread_join();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,9 +68,7 @@ void NavRegion2D::set_enabled(bool p_enabled) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
enabled = p_enabled;
|
enabled = p_enabled;
|
||||||
|
iteration_dirty = true;
|
||||||
// TODO: This should not require a full rebuild as the region has not really changed.
|
|
||||||
polygons_dirty = true;
|
|
||||||
|
|
||||||
request_sync();
|
request_sync();
|
||||||
}
|
}
|
||||||
@ -74,39 +76,32 @@ void NavRegion2D::set_enabled(bool p_enabled) {
|
|||||||
void NavRegion2D::set_use_edge_connections(bool p_enabled) {
|
void NavRegion2D::set_use_edge_connections(bool p_enabled) {
|
||||||
if (use_edge_connections != p_enabled) {
|
if (use_edge_connections != p_enabled) {
|
||||||
use_edge_connections = p_enabled;
|
use_edge_connections = p_enabled;
|
||||||
polygons_dirty = true;
|
iteration_dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
request_sync();
|
request_sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavRegion2D::set_transform(const Transform2D &p_transform) {
|
void NavRegion2D::set_transform(Transform2D p_transform) {
|
||||||
if (transform == p_transform) {
|
if (transform == p_transform) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
transform = p_transform;
|
transform = p_transform;
|
||||||
polygons_dirty = true;
|
iteration_dirty = true;
|
||||||
|
|
||||||
request_sync();
|
request_sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavRegion2D::set_navigation_polygon(Ref<NavigationPolygon> p_navigation_polygon) {
|
void NavRegion2D::set_navigation_mesh(Ref<NavigationPolygon> p_navigation_mesh) {
|
||||||
#ifdef DEBUG_ENABLED
|
#ifdef DEBUG_ENABLED
|
||||||
if (map && p_navigation_polygon.is_valid() && !Math::is_equal_approx(double(map->get_cell_size()), double(p_navigation_polygon->get_cell_size()))) {
|
if (map && p_navigation_mesh.is_valid() && !Math::is_equal_approx(double(map->get_cell_size()), double(p_navigation_mesh->get_cell_size()))) {
|
||||||
ERR_PRINT_ONCE(vformat("Attempted to update a navigation region with a navigation mesh that uses a `cell_size` of %s while assigned to a navigation map set to a `cell_size` of %s. The cell size for navigation maps can be changed by using the NavigationServer map_set_cell_size() function. The cell size for default navigation maps can also be changed in the ProjectSettings.", double(p_navigation_polygon->get_cell_size()), double(map->get_cell_size())));
|
ERR_PRINT_ONCE(vformat("Attempted to update a navigation region with a navigation mesh that uses a `cell_size` of %s while assigned to a navigation map set to a `cell_size` of %s. The cell size for navigation maps can be changed by using the NavigationServer map_set_cell_size() function. The cell size for default navigation maps can also be changed in the ProjectSettings.", double(p_navigation_mesh->get_cell_size()), double(map->get_cell_size())));
|
||||||
}
|
}
|
||||||
#endif // DEBUG_ENABLED
|
#endif // DEBUG_ENABLED
|
||||||
|
|
||||||
RWLockWrite write_lock(navmesh_rwlock);
|
navmesh = p_navigation_mesh;
|
||||||
|
|
||||||
pending_navmesh_vertices.clear();
|
iteration_dirty = true;
|
||||||
pending_navmesh_polygons.clear();
|
|
||||||
|
|
||||||
if (p_navigation_polygon.is_valid()) {
|
|
||||||
p_navigation_polygon->get_data(pending_navmesh_vertices, pending_navmesh_polygons);
|
|
||||||
}
|
|
||||||
|
|
||||||
polygons_dirty = true;
|
|
||||||
|
|
||||||
request_sync();
|
request_sync();
|
||||||
}
|
}
|
||||||
@ -132,7 +127,7 @@ void NavRegion2D::set_navigation_layers(uint32_t p_navigation_layers) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
navigation_layers = p_navigation_layers;
|
navigation_layers = p_navigation_layers;
|
||||||
region_dirty = true;
|
iteration_dirty = true;
|
||||||
|
|
||||||
request_sync();
|
request_sync();
|
||||||
}
|
}
|
||||||
@ -143,7 +138,7 @@ void NavRegion2D::set_enter_cost(real_t p_enter_cost) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
enter_cost = new_enter_cost;
|
enter_cost = new_enter_cost;
|
||||||
region_dirty = true;
|
iteration_dirty = true;
|
||||||
|
|
||||||
request_sync();
|
request_sync();
|
||||||
}
|
}
|
||||||
@ -154,7 +149,7 @@ void NavRegion2D::set_travel_cost(real_t p_travel_cost) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
travel_cost = new_travel_cost;
|
travel_cost = new_travel_cost;
|
||||||
region_dirty = true;
|
iteration_dirty = true;
|
||||||
|
|
||||||
request_sync();
|
request_sync();
|
||||||
}
|
}
|
||||||
@ -164,139 +159,150 @@ void NavRegion2D::set_owner_id(ObjectID p_owner_id) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
owner_id = p_owner_id;
|
owner_id = p_owner_id;
|
||||||
region_dirty = true;
|
iteration_dirty = true;
|
||||||
|
|
||||||
request_sync();
|
request_sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NavRegion2D::scratch_polygons() {
|
||||||
|
iteration_dirty = true;
|
||||||
|
|
||||||
|
request_sync();
|
||||||
|
}
|
||||||
|
|
||||||
|
real_t NavRegion2D::get_surface_area() const {
|
||||||
|
RWLockRead read_lock(iteration_rwlock);
|
||||||
|
return iteration->get_surface_area();
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect2 NavRegion2D::get_bounds() const {
|
||||||
|
RWLockRead read_lock(iteration_rwlock);
|
||||||
|
return iteration->get_bounds();
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalVector<Nav2D::Polygon> const &NavRegion2D::get_polygons() const {
|
||||||
|
RWLockRead read_lock(iteration_rwlock);
|
||||||
|
return iteration->get_navmesh_polygons();
|
||||||
|
}
|
||||||
|
|
||||||
bool NavRegion2D::sync() {
|
bool NavRegion2D::sync() {
|
||||||
RWLockWrite write_lock(region_rwlock);
|
bool requires_map_update = false;
|
||||||
|
if (!map) {
|
||||||
bool something_changed = region_dirty || polygons_dirty;
|
return requires_map_update;
|
||||||
|
|
||||||
region_dirty = false;
|
|
||||||
|
|
||||||
update_polygons();
|
|
||||||
|
|
||||||
if (something_changed) {
|
|
||||||
iteration_id = iteration_id % UINT32_MAX + 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return something_changed;
|
if (iteration_dirty && !iteration_building && !iteration_ready) {
|
||||||
|
_build_iteration();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iteration_ready) {
|
||||||
|
_sync_iteration();
|
||||||
|
requires_map_update = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return requires_map_update;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavRegion2D::update_polygons() {
|
void NavRegion2D::sync_async_tasks() {
|
||||||
if (!polygons_dirty) {
|
if (iteration_build_thread_task_id != WorkerThreadPool::INVALID_TASK_ID) {
|
||||||
return;
|
if (WorkerThreadPool::get_singleton()->is_task_completed(iteration_build_thread_task_id)) {
|
||||||
}
|
WorkerThreadPool::get_singleton()->wait_for_task_completion(iteration_build_thread_task_id);
|
||||||
navmesh_polygons.clear();
|
|
||||||
surface_area = 0.0;
|
|
||||||
bounds = Rect2();
|
|
||||||
polygons_dirty = false;
|
|
||||||
|
|
||||||
if (map == nullptr) {
|
iteration_build_thread_task_id = WorkerThreadPool::INVALID_TASK_ID;
|
||||||
return;
|
iteration_building = false;
|
||||||
}
|
iteration_ready = true;
|
||||||
|
request_sync();
|
||||||
RWLockRead read_lock(navmesh_rwlock);
|
|
||||||
|
|
||||||
if (pending_navmesh_vertices.is_empty() || pending_navmesh_polygons.is_empty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int len = pending_navmesh_vertices.size();
|
|
||||||
if (len == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Vector2 *vertices_r = pending_navmesh_vertices.ptr();
|
|
||||||
|
|
||||||
navmesh_polygons.resize(pending_navmesh_polygons.size());
|
|
||||||
|
|
||||||
real_t _new_region_surface_area = 0.0;
|
|
||||||
Rect2 _new_bounds;
|
|
||||||
|
|
||||||
bool first_vertex = true;
|
|
||||||
int navigation_mesh_polygon_index = 0;
|
|
||||||
|
|
||||||
for (Polygon &polygon : navmesh_polygons) {
|
|
||||||
polygon.surface_area = 0.0;
|
|
||||||
|
|
||||||
Vector<int> navigation_mesh_polygon = pending_navmesh_polygons[navigation_mesh_polygon_index];
|
|
||||||
navigation_mesh_polygon_index += 1;
|
|
||||||
|
|
||||||
int navigation_mesh_polygon_size = navigation_mesh_polygon.size();
|
|
||||||
if (navigation_mesh_polygon_size < 3) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const int *indices = navigation_mesh_polygon.ptr();
|
|
||||||
bool valid(true);
|
|
||||||
|
|
||||||
polygon.vertices.resize(navigation_mesh_polygon_size);
|
|
||||||
polygon.edges.resize(navigation_mesh_polygon_size);
|
|
||||||
|
|
||||||
real_t _new_polygon_surface_area = 0.0;
|
|
||||||
|
|
||||||
for (int j(2); j < navigation_mesh_polygon_size; j++) {
|
|
||||||
const Triangle2 triangle = Triangle2(
|
|
||||||
transform.xform(vertices_r[indices[0]]),
|
|
||||||
transform.xform(vertices_r[indices[j - 1]]),
|
|
||||||
transform.xform(vertices_r[indices[j]]));
|
|
||||||
|
|
||||||
_new_polygon_surface_area += triangle.get_area();
|
|
||||||
}
|
|
||||||
|
|
||||||
polygon.surface_area = _new_polygon_surface_area;
|
|
||||||
_new_region_surface_area += _new_polygon_surface_area;
|
|
||||||
|
|
||||||
for (int j(0); j < navigation_mesh_polygon_size; j++) {
|
|
||||||
int idx = indices[j];
|
|
||||||
if (idx < 0 || idx >= len) {
|
|
||||||
valid = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector2 point_position = transform.xform(vertices_r[idx]);
|
|
||||||
polygon.vertices[j] = point_position;
|
|
||||||
|
|
||||||
if (first_vertex) {
|
|
||||||
first_vertex = false;
|
|
||||||
_new_bounds.position = point_position;
|
|
||||||
} else {
|
|
||||||
_new_bounds.expand_to(point_position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!valid) {
|
|
||||||
ERR_BREAK_MSG(!valid, "The navigation polygon set in this region is not valid!");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
surface_area = _new_region_surface_area;
|
|
||||||
bounds = _new_bounds;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavRegion2D::get_iteration_update(NavRegionIteration2D &r_iteration) {
|
void NavRegion2D::_build_iteration() {
|
||||||
r_iteration.navigation_layers = get_navigation_layers();
|
if (!iteration_dirty || iteration_building || iteration_ready) {
|
||||||
r_iteration.enter_cost = get_enter_cost();
|
return;
|
||||||
r_iteration.travel_cost = get_travel_cost();
|
}
|
||||||
r_iteration.owner_object_id = get_owner_id();
|
|
||||||
r_iteration.owner_type = get_type();
|
|
||||||
r_iteration.owner_rid = get_self();
|
|
||||||
|
|
||||||
r_iteration.enabled = get_enabled();
|
iteration_dirty = false;
|
||||||
r_iteration.transform = get_transform();
|
iteration_building = true;
|
||||||
r_iteration.owner_use_edge_connections = get_use_edge_connections();
|
iteration_ready = false;
|
||||||
r_iteration.bounds = get_bounds();
|
|
||||||
r_iteration.surface_area = get_surface_area();
|
|
||||||
|
|
||||||
r_iteration.navmesh_polygons.clear();
|
iteration_build.reset();
|
||||||
r_iteration.navmesh_polygons.resize(navmesh_polygons.size());
|
|
||||||
for (uint32_t i = 0; i < navmesh_polygons.size(); i++) {
|
if (navmesh.is_valid()) {
|
||||||
Polygon &navmesh_polygon = navmesh_polygons[i];
|
navmesh->get_data(iteration_build.navmesh_data.vertices, iteration_build.navmesh_data.polygons);
|
||||||
navmesh_polygon.owner = &r_iteration;
|
}
|
||||||
r_iteration.navmesh_polygons[i] = navmesh_polygon;
|
|
||||||
|
iteration_build.map_cell_size = map->get_merge_rasterizer_cell_size();
|
||||||
|
|
||||||
|
Ref<NavRegionIteration2D> new_iteration;
|
||||||
|
new_iteration.instantiate();
|
||||||
|
|
||||||
|
new_iteration->navigation_layers = get_navigation_layers();
|
||||||
|
new_iteration->enter_cost = get_enter_cost();
|
||||||
|
new_iteration->travel_cost = get_travel_cost();
|
||||||
|
new_iteration->owner_object_id = get_owner_id();
|
||||||
|
new_iteration->owner_type = get_type();
|
||||||
|
new_iteration->owner_rid = get_self();
|
||||||
|
new_iteration->enabled = get_enabled();
|
||||||
|
new_iteration->transform = get_transform();
|
||||||
|
new_iteration->owner_use_edge_connections = get_use_edge_connections();
|
||||||
|
|
||||||
|
iteration_build.region_iteration = new_iteration;
|
||||||
|
|
||||||
|
if (use_async_iterations) {
|
||||||
|
iteration_build_thread_task_id = WorkerThreadPool::get_singleton()->add_native_task(&NavRegion2D::_build_iteration_threaded, &iteration_build, true, SNAME("NavRegionBuilder2D"));
|
||||||
|
request_async_thread_join();
|
||||||
|
} else {
|
||||||
|
NavRegionBuilder2D::build_iteration(iteration_build);
|
||||||
|
|
||||||
|
iteration_building = false;
|
||||||
|
iteration_ready = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NavRegion2D::_build_iteration_threaded(void *p_arg) {
|
||||||
|
NavRegionIterationBuild2D *_iteration_build = static_cast<NavRegionIterationBuild2D *>(p_arg);
|
||||||
|
|
||||||
|
NavRegionBuilder2D::build_iteration(*_iteration_build);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NavRegion2D::_sync_iteration() {
|
||||||
|
if (iteration_building || !iteration_ready) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
performance_data.pm_polygon_count = iteration_build.performance_data.pm_polygon_count;
|
||||||
|
performance_data.pm_edge_count = iteration_build.performance_data.pm_edge_count;
|
||||||
|
performance_data.pm_edge_merge_count = iteration_build.performance_data.pm_edge_merge_count;
|
||||||
|
|
||||||
|
RWLockWrite write_lock(iteration_rwlock);
|
||||||
|
ERR_FAIL_COND(iteration.is_null());
|
||||||
|
iteration = Ref<NavRegionIteration2D>();
|
||||||
|
DEV_ASSERT(iteration.is_null());
|
||||||
|
iteration = iteration_build.region_iteration;
|
||||||
|
iteration_build.region_iteration = Ref<NavRegionIteration2D>();
|
||||||
|
DEV_ASSERT(iteration_build.region_iteration.is_null());
|
||||||
|
iteration_id = iteration_id % UINT32_MAX + 1;
|
||||||
|
|
||||||
|
iteration_ready = false;
|
||||||
|
|
||||||
|
cancel_async_thread_join();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<NavRegionIteration2D> NavRegion2D::get_iteration() {
|
||||||
|
RWLockRead read_lock(iteration_rwlock);
|
||||||
|
return iteration;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NavRegion2D::request_async_thread_join() {
|
||||||
|
DEV_ASSERT(map);
|
||||||
|
if (map && !async_list_element.in_list()) {
|
||||||
|
map->add_region_async_thread_join_request(&async_list_element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NavRegion2D::cancel_async_thread_join() {
|
||||||
|
if (map && async_list_element.in_list()) {
|
||||||
|
map->remove_region_async_thread_join_request(&async_list_element);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -312,11 +318,42 @@ void NavRegion2D::cancel_sync_request() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NavRegion2D::set_use_async_iterations(bool p_enabled) {
|
||||||
|
if (use_async_iterations == p_enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#ifdef THREADS_ENABLED
|
||||||
|
use_async_iterations = p_enabled;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NavRegion2D::get_use_async_iterations() const {
|
||||||
|
return use_async_iterations;
|
||||||
|
}
|
||||||
|
|
||||||
NavRegion2D::NavRegion2D() :
|
NavRegion2D::NavRegion2D() :
|
||||||
sync_dirty_request_list_element(this) {
|
sync_dirty_request_list_element(this), async_list_element(this) {
|
||||||
type = NavigationUtilities::PathSegmentType::PATH_SEGMENT_TYPE_REGION;
|
type = NavigationUtilities::PathSegmentType::PATH_SEGMENT_TYPE_REGION;
|
||||||
|
iteration_build.region = this;
|
||||||
|
iteration.instantiate();
|
||||||
|
|
||||||
|
#ifdef THREADS_ENABLED
|
||||||
|
use_async_iterations = GLOBAL_GET("navigation/world/region_use_async_iterations");
|
||||||
|
#else
|
||||||
|
use_async_iterations = false;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
NavRegion2D::~NavRegion2D() {
|
NavRegion2D::~NavRegion2D() {
|
||||||
|
cancel_async_thread_join();
|
||||||
cancel_sync_request();
|
cancel_sync_request();
|
||||||
|
|
||||||
|
if (iteration_build_thread_task_id != WorkerThreadPool::INVALID_TASK_ID) {
|
||||||
|
WorkerThreadPool::get_singleton()->wait_for_task_completion(iteration_build_thread_task_id);
|
||||||
|
iteration_build_thread_task_id = WorkerThreadPool::INVALID_TASK_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
iteration_build.region = nullptr;
|
||||||
|
iteration_build.region_iteration = Ref<NavRegionIteration2D>();
|
||||||
|
iteration = Ref<NavRegionIteration2D>();
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
#include "core/os/rw_lock.h"
|
#include "core/os/rw_lock.h"
|
||||||
#include "scene/resources/2d/navigation_polygon.h"
|
#include "scene/resources/2d/navigation_polygon.h"
|
||||||
|
|
||||||
struct NavRegionIteration2D;
|
#include "2d/nav_region_iteration_2d.h"
|
||||||
|
|
||||||
class NavRegion2D : public NavBase2D {
|
class NavRegion2D : public NavBase2D {
|
||||||
RWLock region_rwlock;
|
RWLock region_rwlock;
|
||||||
@ -47,21 +47,30 @@ class NavRegion2D : public NavBase2D {
|
|||||||
|
|
||||||
bool use_edge_connections = true;
|
bool use_edge_connections = true;
|
||||||
|
|
||||||
bool region_dirty = true;
|
|
||||||
bool polygons_dirty = true;
|
|
||||||
|
|
||||||
LocalVector<Nav2D::Polygon> navmesh_polygons;
|
|
||||||
|
|
||||||
real_t surface_area = 0.0;
|
|
||||||
Rect2 bounds;
|
Rect2 bounds;
|
||||||
|
|
||||||
RWLock navmesh_rwlock;
|
Ref<NavigationPolygon> navmesh;
|
||||||
Vector<Vector2> pending_navmesh_vertices;
|
|
||||||
Vector<Vector<int>> pending_navmesh_polygons;
|
Nav2D::PerformanceData performance_data;
|
||||||
|
|
||||||
uint32_t iteration_id = 0;
|
uint32_t iteration_id = 0;
|
||||||
|
|
||||||
SelfList<NavRegion2D> sync_dirty_request_list_element;
|
SelfList<NavRegion2D> sync_dirty_request_list_element;
|
||||||
|
mutable RWLock iteration_rwlock;
|
||||||
|
Ref<NavRegionIteration2D> iteration;
|
||||||
|
|
||||||
|
NavRegionIterationBuild2D iteration_build;
|
||||||
|
bool use_async_iterations = true;
|
||||||
|
SelfList<NavRegion2D> async_list_element;
|
||||||
|
WorkerThreadPool::TaskID iteration_build_thread_task_id = WorkerThreadPool::INVALID_TASK_ID;
|
||||||
|
static void _build_iteration_threaded(void *p_arg);
|
||||||
|
|
||||||
|
bool iteration_dirty = true;
|
||||||
|
bool iteration_building = false;
|
||||||
|
bool iteration_ready = false;
|
||||||
|
|
||||||
|
void _build_iteration();
|
||||||
|
void _sync_iteration();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
NavRegion2D();
|
NavRegion2D();
|
||||||
@ -69,9 +78,7 @@ public:
|
|||||||
|
|
||||||
uint32_t get_iteration_id() const { return iteration_id; }
|
uint32_t get_iteration_id() const { return iteration_id; }
|
||||||
|
|
||||||
void scratch_polygons() {
|
void scratch_polygons();
|
||||||
polygons_dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_enabled(bool p_enabled);
|
void set_enabled(bool p_enabled);
|
||||||
bool get_enabled() const { return enabled; }
|
bool get_enabled() const { return enabled; }
|
||||||
@ -84,22 +91,21 @@ public:
|
|||||||
virtual void set_use_edge_connections(bool p_enabled) override;
|
virtual void set_use_edge_connections(bool p_enabled) override;
|
||||||
virtual bool get_use_edge_connections() const override { return use_edge_connections; }
|
virtual bool get_use_edge_connections() const override { return use_edge_connections; }
|
||||||
|
|
||||||
void set_transform(const Transform2D &p_transform);
|
void set_transform(Transform2D transform);
|
||||||
const Transform2D &get_transform() const {
|
const Transform2D &get_transform() const {
|
||||||
return transform;
|
return transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_navigation_polygon(Ref<NavigationPolygon> p_navigation_polygon);
|
void set_navigation_mesh(Ref<NavigationPolygon> p_navigation_mesh);
|
||||||
|
Ref<NavigationPolygon> get_navigation_mesh() const { return navmesh; }
|
||||||
|
|
||||||
LocalVector<Nav2D::Polygon> const &get_polygons() const {
|
LocalVector<Nav2D::Polygon> const &get_polygons() const;
|
||||||
return navmesh_polygons;
|
|
||||||
}
|
|
||||||
|
|
||||||
Nav2D::ClosestPointQueryResult get_closest_point_info(const Vector2 &p_point) const;
|
Nav2D::ClosestPointQueryResult get_closest_point_info(const Vector2 &p_point) const;
|
||||||
Vector2 get_random_point(uint32_t p_navigation_layers, bool p_uniformly) const;
|
Vector2 get_random_point(uint32_t p_navigation_layers, bool p_uniformly) const;
|
||||||
|
|
||||||
real_t get_surface_area() const { return surface_area; }
|
real_t get_surface_area() const;
|
||||||
Rect2 get_bounds() const { return bounds; }
|
Rect2 get_bounds() const;
|
||||||
|
|
||||||
// NavBase properties.
|
// NavBase properties.
|
||||||
virtual void set_navigation_layers(uint32_t p_navigation_layers) override;
|
virtual void set_navigation_layers(uint32_t p_navigation_layers) override;
|
||||||
@ -111,8 +117,17 @@ public:
|
|||||||
void request_sync();
|
void request_sync();
|
||||||
void cancel_sync_request();
|
void cancel_sync_request();
|
||||||
|
|
||||||
void get_iteration_update(NavRegionIteration2D &r_iteration);
|
void sync_async_tasks();
|
||||||
|
void request_async_thread_join();
|
||||||
|
void cancel_async_thread_join();
|
||||||
|
|
||||||
private:
|
Ref<NavRegionIteration2D> get_iteration();
|
||||||
void update_polygons();
|
|
||||||
|
// Performance Monitor
|
||||||
|
int get_pm_polygon_count() const { return performance_data.pm_polygon_count; }
|
||||||
|
int get_pm_edge_count() const { return performance_data.pm_edge_count; }
|
||||||
|
int get_pm_edge_merge_count() const { return performance_data.pm_edge_merge_count; }
|
||||||
|
|
||||||
|
void set_use_async_iterations(bool p_enabled);
|
||||||
|
bool get_use_async_iterations() const;
|
||||||
};
|
};
|
||||||
|
@ -31,12 +31,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "core/math/vector3.h"
|
#include "core/math/vector3.h"
|
||||||
|
#include "core/object/ref_counted.h"
|
||||||
#include "core/templates/hash_map.h"
|
#include "core/templates/hash_map.h"
|
||||||
#include "core/templates/hashfuncs.h"
|
#include "core/templates/hashfuncs.h"
|
||||||
#include "servers/navigation/nav_heap.h"
|
#include "servers/navigation/nav_heap.h"
|
||||||
#include "servers/navigation/navigation_utilities.h"
|
#include "servers/navigation/navigation_utilities.h"
|
||||||
|
|
||||||
struct NavBaseIteration2D;
|
class NavBaseIteration2D;
|
||||||
|
|
||||||
namespace Nav2D {
|
namespace Nav2D {
|
||||||
struct Polygon;
|
struct Polygon;
|
||||||
@ -71,28 +72,28 @@ struct EdgeKey {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Edge {
|
struct ConnectableEdge {
|
||||||
/// The gateway in the edge, as, in some case, the whole edge might not be navigable.
|
EdgeKey ek;
|
||||||
struct Connection {
|
uint32_t polygon_index;
|
||||||
/// Polygon that this connection leads to.
|
Vector2 pathway_start;
|
||||||
Polygon *polygon = nullptr;
|
Vector2 pathway_end;
|
||||||
|
};
|
||||||
|
|
||||||
/// Edge of the source polygon where this connection starts from.
|
struct Connection {
|
||||||
int edge = -1;
|
/// Polygon that this connection leads to.
|
||||||
|
Polygon *polygon = nullptr;
|
||||||
|
|
||||||
/// Point on the edge where the gateway leading to the poly starts.
|
/// Edge of the source polygon where this connection starts from.
|
||||||
Vector2 pathway_start;
|
int edge = -1;
|
||||||
|
|
||||||
/// Point on the edge where the gateway leading to the poly ends.
|
/// Point on the edge where the gateway leading to the poly starts.
|
||||||
Vector2 pathway_end;
|
Vector2 pathway_start;
|
||||||
};
|
|
||||||
|
|
||||||
/// Connections from this edge to other polygons.
|
/// Point on the edge where the gateway leading to the poly ends.
|
||||||
LocalVector<Connection> connections;
|
Vector2 pathway_end;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Polygon {
|
struct Polygon {
|
||||||
/// Id of the polygon in the map.
|
|
||||||
uint32_t id = UINT32_MAX;
|
uint32_t id = UINT32_MAX;
|
||||||
|
|
||||||
/// Navigation region or link that contains this polygon.
|
/// Navigation region or link that contains this polygon.
|
||||||
@ -100,9 +101,6 @@ struct Polygon {
|
|||||||
|
|
||||||
LocalVector<Vector2> vertices;
|
LocalVector<Vector2> vertices;
|
||||||
|
|
||||||
/// The edges of this `Polygon`
|
|
||||||
LocalVector<Edge> edges;
|
|
||||||
|
|
||||||
real_t surface_area = 0.0;
|
real_t surface_area = 0.0;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -177,7 +175,7 @@ struct ClosestPointQueryResult {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct EdgeConnectionPair {
|
struct EdgeConnectionPair {
|
||||||
Edge::Connection connections[2];
|
Connection connections[2];
|
||||||
int size = 0;
|
int size = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1504,7 +1504,7 @@ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_font_data, const
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
unsigned int text_size = hb_ot_name_get_utf32(hb_face, names[i].name_id, names[i].language, nullptr, nullptr) + 1;
|
unsigned int text_size = hb_ot_name_get_utf32(hb_face, names[i].name_id, names[i].language, nullptr, nullptr) + 1;
|
||||||
p_font_data->font_name.resize(text_size);
|
p_font_data->font_name.resize_uninitialized(text_size);
|
||||||
hb_ot_name_get_utf32(hb_face, names[i].name_id, names[i].language, &text_size, (uint32_t *)p_font_data->font_name.ptrw());
|
hb_ot_name_get_utf32(hb_face, names[i].name_id, names[i].language, &text_size, (uint32_t *)p_font_data->font_name.ptrw());
|
||||||
}
|
}
|
||||||
if (p_font_data->font_name.is_empty() && fd->face->family_name != nullptr) {
|
if (p_font_data->font_name.is_empty() && fd->face->family_name != nullptr) {
|
||||||
@ -2308,7 +2308,7 @@ Dictionary TextServerAdvanced::_font_get_ot_name_strings(const RID &p_font_rid)
|
|||||||
}
|
}
|
||||||
String text;
|
String text;
|
||||||
unsigned int text_size = hb_ot_name_get_utf32(hb_face, names[i].name_id, names[i].language, nullptr, nullptr) + 1;
|
unsigned int text_size = hb_ot_name_get_utf32(hb_face, names[i].name_id, names[i].language, nullptr, nullptr) + 1;
|
||||||
text.resize(text_size);
|
text.resize_uninitialized(text_size);
|
||||||
hb_ot_name_get_utf32(hb_face, names[i].name_id, names[i].language, &text_size, (uint32_t *)text.ptrw());
|
hb_ot_name_get_utf32(hb_face, names[i].name_id, names[i].language, &text_size, (uint32_t *)text.ptrw());
|
||||||
if (!text.is_empty()) {
|
if (!text.is_empty()) {
|
||||||
Dictionary &id_string = names_for_lang[String(hb_language_to_string(names[i].language))];
|
Dictionary &id_string = names_for_lang[String(hb_language_to_string(names[i].language))];
|
||||||
|
@ -76,7 +76,7 @@ bool DisplayServerAndroid::has_feature(Feature p_feature) const {
|
|||||||
//case FEATURE_NATIVE_DIALOG_FILE_EXTRA:
|
//case FEATURE_NATIVE_DIALOG_FILE_EXTRA:
|
||||||
case FEATURE_NATIVE_DIALOG_FILE_MIME:
|
case FEATURE_NATIVE_DIALOG_FILE_MIME:
|
||||||
//case FEATURE_NATIVE_ICON:
|
//case FEATURE_NATIVE_ICON:
|
||||||
//case FEATURE_WINDOW_TRANSPARENCY:
|
case FEATURE_WINDOW_TRANSPARENCY:
|
||||||
case FEATURE_CLIPBOARD:
|
case FEATURE_CLIPBOARD:
|
||||||
case FEATURE_KEEP_SCREEN_ON:
|
case FEATURE_KEEP_SCREEN_ON:
|
||||||
case FEATURE_ORIENTATION:
|
case FEATURE_ORIENTATION:
|
||||||
@ -592,7 +592,13 @@ void DisplayServerAndroid::window_set_flag(DisplayServer::WindowFlags p_flag, bo
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool DisplayServerAndroid::window_get_flag(DisplayServer::WindowFlags p_flag, DisplayServer::WindowID p_window) const {
|
bool DisplayServerAndroid::window_get_flag(DisplayServer::WindowFlags p_flag, DisplayServer::WindowID p_window) const {
|
||||||
return false;
|
switch (p_flag) {
|
||||||
|
case WindowFlags::WINDOW_FLAG_TRANSPARENT:
|
||||||
|
return is_window_transparency_available();
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DisplayServerAndroid::window_request_attention(DisplayServer::WindowID p_window) {
|
void DisplayServerAndroid::window_request_attention(DisplayServer::WindowID p_window) {
|
||||||
@ -961,3 +967,7 @@ void DisplayServerAndroid::set_native_icon(const String &p_filename) {
|
|||||||
void DisplayServerAndroid::set_icon(const Ref<Image> &p_icon) {
|
void DisplayServerAndroid::set_icon(const Ref<Image> &p_icon) {
|
||||||
// NOT SUPPORTED
|
// NOT SUPPORTED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DisplayServerAndroid::is_window_transparency_available() const {
|
||||||
|
return GLOBAL_GET_CACHED(bool, "display/window/per_pixel_transparency/allowed");
|
||||||
|
}
|
||||||
|
@ -256,6 +256,8 @@ public:
|
|||||||
virtual void set_native_icon(const String &p_filename) override;
|
virtual void set_native_icon(const String &p_filename) override;
|
||||||
virtual void set_icon(const Ref<Image> &p_icon) override;
|
virtual void set_icon(const Ref<Image> &p_icon) override;
|
||||||
|
|
||||||
|
virtual bool is_window_transparency_available() const override;
|
||||||
|
|
||||||
DisplayServerAndroid(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error);
|
DisplayServerAndroid(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error);
|
||||||
~DisplayServerAndroid();
|
~DisplayServerAndroid();
|
||||||
};
|
};
|
||||||
|
@ -61,6 +61,11 @@
|
|||||||
<member name="gradle_build/export_format" type="int" setter="" getter="">
|
<member name="gradle_build/export_format" type="int" setter="" getter="">
|
||||||
Application export format (*.apk or *.aab).
|
Application export format (*.apk or *.aab).
|
||||||
</member>
|
</member>
|
||||||
|
<member name="gradle_build/google_play_instant" type="bool" setter="" getter="">
|
||||||
|
If [code]true[/code], configures the exported project for Play Instant Build.
|
||||||
|
Use this option when targeting Play Instant to allow users to launch the app without installation. See [url=https://developer.android.com/topic/google-play-instant/overview]Google Play Instant[/url].
|
||||||
|
[b]Note:[/b] Instant play games also need to be optimized for size. See [url=https://docs.godotengine.org/en/stable/contributing/development/compiling/optimizing_for_size.html]Optimizing a build for size[/url].
|
||||||
|
</member>
|
||||||
<member name="gradle_build/gradle_build_directory" type="String" setter="" getter="">
|
<member name="gradle_build/gradle_build_directory" type="String" setter="" getter="">
|
||||||
Path to the Gradle build directory. If left empty, then [code]res://android[/code] will be used.
|
Path to the Gradle build directory. If left empty, then [code]res://android[/code] will be used.
|
||||||
</member>
|
</member>
|
||||||
|
@ -280,6 +280,7 @@ static const int EXPORT_FORMAT_AAB = 1;
|
|||||||
|
|
||||||
static const char *APK_ASSETS_DIRECTORY = "assets";
|
static const char *APK_ASSETS_DIRECTORY = "assets";
|
||||||
static const char *AAB_ASSETS_DIRECTORY = "assetPackInstallTime/src/main/assets";
|
static const char *AAB_ASSETS_DIRECTORY = "assetPackInstallTime/src/main/assets";
|
||||||
|
static const char *INSTANT_APP_ASSETS_DIRECTORY = "assets"; // instant build doesn't support installTime assetspacks, so using the same directory as APK
|
||||||
|
|
||||||
static const int DEFAULT_MIN_SDK_VERSION = 24; // Should match the value in 'platform/android/java/app/config.gradle#minSdk'
|
static const int DEFAULT_MIN_SDK_VERSION = 24; // Should match the value in 'platform/android/java/app/config.gradle#minSdk'
|
||||||
static const int DEFAULT_TARGET_SDK_VERSION = 35; // Should match the value in 'platform/android/java/app/config.gradle#targetSdk'
|
static const int DEFAULT_TARGET_SDK_VERSION = 35; // Should match the value in 'platform/android/java/app/config.gradle#targetSdk'
|
||||||
@ -524,6 +525,12 @@ String EditorExportPlatformAndroid::get_valid_basename(const Ref<EditorExportPre
|
|||||||
|
|
||||||
String EditorExportPlatformAndroid::get_assets_directory(const Ref<EditorExportPreset> &p_preset, int p_export_format) const {
|
String EditorExportPlatformAndroid::get_assets_directory(const Ref<EditorExportPreset> &p_preset, int p_export_format) const {
|
||||||
String gradle_build_directory = ExportTemplateManager::get_android_build_directory(p_preset);
|
String gradle_build_directory = ExportTemplateManager::get_android_build_directory(p_preset);
|
||||||
|
|
||||||
|
bool google_play_instant_build = p_preset->get("gradle_build/google_play_instant");
|
||||||
|
if (google_play_instant_build) {
|
||||||
|
return gradle_build_directory.path_join(INSTANT_APP_ASSETS_DIRECTORY); // Always use base APK asset format
|
||||||
|
}
|
||||||
|
|
||||||
return gradle_build_directory.path_join(p_export_format == EXPORT_FORMAT_AAB ? AAB_ASSETS_DIRECTORY : APK_ASSETS_DIRECTORY);
|
return gradle_build_directory.path_join(p_export_format == EXPORT_FORMAT_AAB ? AAB_ASSETS_DIRECTORY : APK_ASSETS_DIRECTORY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -994,10 +1001,19 @@ void EditorExportPlatformAndroid::_get_manifest_info(const Ref<EditorExportPrese
|
|||||||
|
|
||||||
void EditorExportPlatformAndroid::_write_tmp_manifest(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, bool p_debug) {
|
void EditorExportPlatformAndroid::_write_tmp_manifest(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, bool p_debug) {
|
||||||
print_verbose("Building temporary manifest...");
|
print_verbose("Building temporary manifest...");
|
||||||
|
|
||||||
|
bool google_play_instant_build = (bool)p_preset->get("gradle_build/google_play_instant");
|
||||||
|
|
||||||
String manifest_text =
|
String manifest_text =
|
||||||
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
|
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
|
||||||
"<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
|
"<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
|
||||||
" xmlns:tools=\"http://schemas.android.com/tools\">\n";
|
" xmlns:tools=\"http://schemas.android.com/tools\"";
|
||||||
|
|
||||||
|
if (google_play_instant_build) {
|
||||||
|
manifest_text += " android:targetSandboxVersion=\"2\" \n xmlns:dist=\"http://schemas.android.com/apk/distribution\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
manifest_text += ">\n";
|
||||||
|
|
||||||
manifest_text += _get_screen_sizes_tag(p_preset);
|
manifest_text += _get_screen_sizes_tag(p_preset);
|
||||||
manifest_text += _get_gles_tag();
|
manifest_text += _get_gles_tag();
|
||||||
@ -1031,6 +1047,7 @@ void EditorExportPlatformAndroid::_write_tmp_manifest(const Ref<EditorExportPres
|
|||||||
}
|
}
|
||||||
|
|
||||||
manifest_text += _get_application_tag(Ref<EditorExportPlatform>(this), p_preset, _has_read_write_storage_permission(perms), p_debug, manifest_metadata);
|
manifest_text += _get_application_tag(Ref<EditorExportPlatform>(this), p_preset, _has_read_write_storage_permission(perms), p_debug, manifest_metadata);
|
||||||
|
|
||||||
manifest_text += "</manifest>\n";
|
manifest_text += "</manifest>\n";
|
||||||
String manifest_path = ExportTemplateManager::get_android_build_directory(p_preset).path_join(vformat("src/%s/AndroidManifest.xml", (p_debug ? "debug" : "release")));
|
String manifest_path = ExportTemplateManager::get_android_build_directory(p_preset).path_join(vformat("src/%s/AndroidManifest.xml", (p_debug ? "debug" : "release")));
|
||||||
|
|
||||||
@ -1038,6 +1055,10 @@ void EditorExportPlatformAndroid::_write_tmp_manifest(const Ref<EditorExportPres
|
|||||||
store_string_at_path(manifest_path, manifest_text);
|
store_string_at_path(manifest_path, manifest_text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool EditorExportPlatformAndroid::_should_be_transparent(const Ref<EditorExportPreset> &p_preset) const {
|
||||||
|
return (bool)get_project_setting(p_preset, "display/window/per_pixel_transparency/allowed");
|
||||||
|
}
|
||||||
|
|
||||||
void EditorExportPlatformAndroid::_fix_themes_xml(const Ref<EditorExportPreset> &p_preset) {
|
void EditorExportPlatformAndroid::_fix_themes_xml(const Ref<EditorExportPreset> &p_preset) {
|
||||||
const String themes_xml_path = ExportTemplateManager::get_android_build_directory(p_preset).path_join("res/values/themes.xml");
|
const String themes_xml_path = ExportTemplateManager::get_android_build_directory(p_preset).path_join("res/values/themes.xml");
|
||||||
|
|
||||||
@ -1046,15 +1067,22 @@ void EditorExportPlatformAndroid::_fix_themes_xml(const Ref<EditorExportPreset>
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool should_be_transparent = _should_be_transparent(p_preset);
|
||||||
|
|
||||||
// Default/Reserved theme attributes.
|
// Default/Reserved theme attributes.
|
||||||
Dictionary main_theme_attributes;
|
Dictionary main_theme_attributes;
|
||||||
main_theme_attributes["android:windowDrawsSystemBarBackgrounds"] = "false";
|
main_theme_attributes["android:windowDrawsSystemBarBackgrounds"] = "false";
|
||||||
main_theme_attributes["android:windowSwipeToDismiss"] = bool_to_string(p_preset->get("gesture/swipe_to_dismiss"));
|
main_theme_attributes["android:windowSwipeToDismiss"] = bool_to_string(p_preset->get("gesture/swipe_to_dismiss"));
|
||||||
|
main_theme_attributes["android:windowIsTranslucent"] = bool_to_string(should_be_transparent);
|
||||||
|
if (should_be_transparent) {
|
||||||
|
main_theme_attributes["android:windowBackground"] = "@android:color/transparent";
|
||||||
|
}
|
||||||
|
|
||||||
Dictionary splash_theme_attributes;
|
Dictionary splash_theme_attributes;
|
||||||
splash_theme_attributes["android:windowSplashScreenBackground"] = "@mipmap/icon_background";
|
splash_theme_attributes["android:windowSplashScreenBackground"] = "@mipmap/icon_background";
|
||||||
splash_theme_attributes["windowSplashScreenAnimatedIcon"] = "@mipmap/icon_foreground";
|
splash_theme_attributes["windowSplashScreenAnimatedIcon"] = "@mipmap/icon_foreground";
|
||||||
splash_theme_attributes["postSplashScreenTheme"] = "@style/GodotAppMainTheme";
|
splash_theme_attributes["postSplashScreenTheme"] = "@style/GodotAppMainTheme";
|
||||||
|
splash_theme_attributes["android:windowIsTranslucent"] = bool_to_string(should_be_transparent);
|
||||||
|
|
||||||
Dictionary custom_theme_attributes = p_preset->get("gradle_build/custom_theme_attributes");
|
Dictionary custom_theme_attributes = p_preset->get("gradle_build/custom_theme_attributes");
|
||||||
|
|
||||||
@ -2008,6 +2036,12 @@ String EditorExportPlatformAndroid::get_export_option_warning(const EditorExport
|
|||||||
if (int(p_preset->get("gradle_build/export_format")) == EXPORT_FORMAT_AAB && !gradle_build_enabled) {
|
if (int(p_preset->get("gradle_build/export_format")) == EXPORT_FORMAT_AAB && !gradle_build_enabled) {
|
||||||
return TTR("\"Export AAB\" is only valid when \"Use Gradle Build\" is enabled.");
|
return TTR("\"Export AAB\" is only valid when \"Use Gradle Build\" is enabled.");
|
||||||
}
|
}
|
||||||
|
} else if (p_name == "gradle_build/google_play_instant") {
|
||||||
|
bool instant_enabled = p_preset->get("gradle_build/google_play_instant");
|
||||||
|
bool gradle_build_enabled = p_preset->get("gradle_build/use_gradle_build");
|
||||||
|
if (instant_enabled && !gradle_build_enabled) {
|
||||||
|
return TTR("\"Instant Build\" is only valid when \"Use Gradle Build\" is enabled.");
|
||||||
|
}
|
||||||
} else if (p_name == "gradle_build/min_sdk") {
|
} else if (p_name == "gradle_build/min_sdk") {
|
||||||
String min_sdk_str = p_preset->get("gradle_build/min_sdk");
|
String min_sdk_str = p_preset->get("gradle_build/min_sdk");
|
||||||
bool gradle_build_enabled = p_preset->get("gradle_build/use_gradle_build");
|
bool gradle_build_enabled = p_preset->get("gradle_build/use_gradle_build");
|
||||||
@ -2087,6 +2121,7 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio
|
|||||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "gradle_build/gradle_build_directory", PROPERTY_HINT_PLACEHOLDER_TEXT, "res://android"), "", false, false));
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "gradle_build/gradle_build_directory", PROPERTY_HINT_PLACEHOLDER_TEXT, "res://android"), "", false, false));
|
||||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "gradle_build/android_source_template", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "gradle_build/android_source_template", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
|
||||||
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "gradle_build/export_format", PROPERTY_HINT_ENUM, "Export APK,Export AAB"), EXPORT_FORMAT_APK, false, true));
|
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "gradle_build/export_format", PROPERTY_HINT_ENUM, "Export APK,Export AAB"), EXPORT_FORMAT_APK, false, true));
|
||||||
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "gradle_build/google_play_instant"), false, true, true));
|
||||||
// Using String instead of int to default to an empty string (no override) with placeholder for instructions (see GH-62465).
|
// Using String instead of int to default to an empty string (no override) with placeholder for instructions (see GH-62465).
|
||||||
// This implies doing validation that the string is a proper int.
|
// This implies doing validation that the string is a proper int.
|
||||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "gradle_build/min_sdk", PROPERTY_HINT_PLACEHOLDER_TEXT, vformat("%d (default)", DEFAULT_MIN_SDK_VERSION)), "", false, true));
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "gradle_build/min_sdk", PROPERTY_HINT_PLACEHOLDER_TEXT, vformat("%d (default)", DEFAULT_MIN_SDK_VERSION)), "", false, true));
|
||||||
@ -2952,7 +2987,8 @@ bool EditorExportPlatformAndroid::has_valid_project_configuration(const Ref<Edit
|
|||||||
valid = false;
|
valid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p_preset->get("gradle_build/use_gradle_build")) {
|
bool gradle_build_enabled = p_preset->get("gradle_build/use_gradle_build");
|
||||||
|
if (gradle_build_enabled) {
|
||||||
String build_version_path = ExportTemplateManager::get_android_build_directory(p_preset).get_base_dir().path_join(".build_version");
|
String build_version_path = ExportTemplateManager::get_android_build_directory(p_preset).get_base_dir().path_join(".build_version");
|
||||||
Ref<FileAccess> f = FileAccess::open(build_version_path, FileAccess::READ);
|
Ref<FileAccess> f = FileAccess::open(build_version_path, FileAccess::READ);
|
||||||
if (f.is_valid()) {
|
if (f.is_valid()) {
|
||||||
@ -2963,6 +2999,12 @@ bool EditorExportPlatformAndroid::has_valid_project_configuration(const Ref<Edit
|
|||||||
err += "\n";
|
err += "\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (_should_be_transparent(p_preset)) {
|
||||||
|
// Warning only, so don't override `valid`.
|
||||||
|
err += vformat(TTR("\"Use Gradle Build\" is required for transparent background on Android"));
|
||||||
|
err += "\n";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String target_sdk_str = p_preset->get("gradle_build/target_sdk");
|
String target_sdk_str = p_preset->get("gradle_build/target_sdk");
|
||||||
@ -3564,6 +3606,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
|
|||||||
String enabled_abi_string = join_abis(enabled_abis, "|", false);
|
String enabled_abi_string = join_abis(enabled_abis, "|", false);
|
||||||
String sign_flag = should_sign ? "true" : "false";
|
String sign_flag = should_sign ? "true" : "false";
|
||||||
String zipalign_flag = "true";
|
String zipalign_flag = "true";
|
||||||
|
String play_instant_flag = bool_to_string(p_preset->get("gradle_build/google_play_instant"));
|
||||||
|
|
||||||
Vector<String> android_libraries;
|
Vector<String> android_libraries;
|
||||||
Vector<String> android_dependencies;
|
Vector<String> android_dependencies;
|
||||||
@ -3638,6 +3681,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
|
|||||||
cmdline.push_back("-Pplugins_maven_repos=" + combined_android_dependencies_maven_repos); // argument to specify the list of maven repos for android dependencies provided by plugins.
|
cmdline.push_back("-Pplugins_maven_repos=" + combined_android_dependencies_maven_repos); // argument to specify the list of maven repos for android dependencies provided by plugins.
|
||||||
cmdline.push_back("-Pperform_zipalign=" + zipalign_flag); // argument to specify whether the build should be zipaligned.
|
cmdline.push_back("-Pperform_zipalign=" + zipalign_flag); // argument to specify whether the build should be zipaligned.
|
||||||
cmdline.push_back("-Pperform_signing=" + sign_flag); // argument to specify whether the build should be signed.
|
cmdline.push_back("-Pperform_signing=" + sign_flag); // argument to specify whether the build should be signed.
|
||||||
|
cmdline.push_back("-Pplay_instant_app=" + play_instant_flag); // argument to specify whether the build is for Google Play Instant.
|
||||||
|
|
||||||
// NOTE: The release keystore is not included in the verbose logging
|
// NOTE: The release keystore is not included in the verbose logging
|
||||||
// to avoid accidentally leaking sensitive information when sharing verbose logs for troubleshooting.
|
// to avoid accidentally leaking sensitive information when sharing verbose logs for troubleshooting.
|
||||||
|
@ -163,6 +163,8 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
|
|||||||
|
|
||||||
void _write_tmp_manifest(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, bool p_debug);
|
void _write_tmp_manifest(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, bool p_debug);
|
||||||
|
|
||||||
|
bool _should_be_transparent(const Ref<EditorExportPreset> &p_preset) const;
|
||||||
|
|
||||||
void _fix_themes_xml(const Ref<EditorExportPreset> &p_preset);
|
void _fix_themes_xml(const Ref<EditorExportPreset> &p_preset);
|
||||||
|
|
||||||
void _fix_manifest(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_manifest, bool p_give_internet);
|
void _fix_manifest(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_manifest, bool p_give_internet);
|
||||||
|
@ -315,6 +315,8 @@ String _get_application_tag(const Ref<EditorExportPlatform> &p_export_platform,
|
|||||||
int app_category_index = (int)(p_preset->get("package/app_category"));
|
int app_category_index = (int)(p_preset->get("package/app_category"));
|
||||||
bool is_game = app_category_index == APP_CATEGORY_GAME;
|
bool is_game = app_category_index == APP_CATEGORY_GAME;
|
||||||
|
|
||||||
|
bool google_play_instant_build = (bool)p_preset->get("gradle_build/google_play_instant");
|
||||||
|
|
||||||
String manifest_application_text = vformat(
|
String manifest_application_text = vformat(
|
||||||
" <application android:label=\"@string/godot_project_name_string\"\n"
|
" <application android:label=\"@string/godot_project_name_string\"\n"
|
||||||
" android:allowBackup=\"%s\"\n"
|
" android:allowBackup=\"%s\"\n"
|
||||||
@ -335,6 +337,10 @@ String _get_application_tag(const Ref<EditorExportPlatform> &p_export_platform,
|
|||||||
}
|
}
|
||||||
manifest_application_text += " tools:ignore=\"GoogleAppIndexingWarning\">\n\n";
|
manifest_application_text += " tools:ignore=\"GoogleAppIndexingWarning\">\n\n";
|
||||||
|
|
||||||
|
if (google_play_instant_build) {
|
||||||
|
manifest_application_text += " <dist:module dist:instant=\"true\" />\n";
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < p_metadata.size(); i++) {
|
for (int i = 0; i < p_metadata.size(); i++) {
|
||||||
manifest_application_text += vformat(" <meta-data tools:node=\"replace\" android:name=\"%s\" android:value=\"%s\" />\n", p_metadata[i].name, p_metadata[i].value);
|
manifest_application_text += vformat(" <meta-data tools:node=\"replace\" android:name=\"%s\" android:value=\"%s\" />\n", p_metadata[i].name, p_metadata[i].value);
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,10 @@ dependencies {
|
|||||||
implementation "androidx.fragment:fragment:$versions.fragmentVersion"
|
implementation "androidx.fragment:fragment:$versions.fragmentVersion"
|
||||||
implementation "androidx.core:core-splashscreen:$versions.splashscreenVersion"
|
implementation "androidx.core:core-splashscreen:$versions.splashscreenVersion"
|
||||||
|
|
||||||
|
if (isInstantApp()) {
|
||||||
|
implementation "com.google.android.gms:play-services-instantapps:$versions.instantAppsVersion"
|
||||||
|
}
|
||||||
|
|
||||||
if (rootProject.findProject(":lib")) {
|
if (rootProject.findProject(":lib")) {
|
||||||
implementation project(":lib")
|
implementation project(":lib")
|
||||||
} else if (rootProject.findProject(":godot:lib")) {
|
} else if (rootProject.findProject(":godot:lib")) {
|
||||||
@ -90,7 +94,7 @@ android {
|
|||||||
jvmTarget = versions.javaVersion
|
jvmTarget = versions.javaVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
assetPacks = [":assetPackInstallTime"]
|
assetPacks = isInstantApp() ? [] : [":assetPackInstallTime"]
|
||||||
|
|
||||||
namespace = 'com.godot.game'
|
namespace = 'com.godot.game'
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user