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++) {
|
||||
uint32_t slen = f->get_32();
|
||||
CharString cs;
|
||||
cs.resize(slen + 1);
|
||||
cs.resize_uninitialized(slen + 1);
|
||||
cs[slen] = 0;
|
||||
f->get_buffer((uint8_t *)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) {
|
||||
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) {
|
||||
|
@ -258,7 +258,12 @@ String FileAccess::fix_path(const String &p_path) const {
|
||||
case ACCESS_RESOURCES: {
|
||||
if (ProjectSettings::get_singleton()) {
|
||||
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://")) {
|
||||
@ -769,7 +774,7 @@ bool FileAccess::store_pascal_string(const String &p_string) {
|
||||
String FileAccess::get_pascal_string() {
|
||||
uint32_t sl = get_32();
|
||||
CharString cs;
|
||||
cs.resize(sl + 1);
|
||||
cs.resize_uninitialized(sl + 1);
|
||||
get_buffer((uint8_t *)cs.ptr(), sl);
|
||||
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++) {
|
||||
uint32_t sl = f->get_32();
|
||||
CharString cs;
|
||||
cs.resize(sl + 1);
|
||||
cs.resize_uninitialized(sl + 1);
|
||||
f->get_buffer((uint8_t *)cs.ptr(), sl);
|
||||
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));
|
||||
}
|
||||
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);
|
||||
} break;
|
||||
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));
|
||||
}
|
||||
Char16String cs16;
|
||||
cs16.resize(marker_size + 1);
|
||||
cs16.resize_uninitialized(marker_size + 1);
|
||||
for (uint64_t i = 0; i < marker_size; i++) {
|
||||
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 base = char_count + ('9' - '0');
|
||||
String id;
|
||||
id.resize(characters + 1);
|
||||
id.resize_uninitialized(characters + 1);
|
||||
char32_t *ptr = id.ptrw();
|
||||
for (uint32_t i = 0; i < characters; i++) {
|
||||
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.
|
||||
String txt;
|
||||
txt.resize(tmp_size + 7);
|
||||
txt.resize_uninitialized(tmp_size + 7);
|
||||
|
||||
char32_t *p = txt.ptrw();
|
||||
p[0] = 'u';
|
||||
@ -273,7 +273,7 @@ Error ResourceUID::load_from_cache(bool p_reset) {
|
||||
int64_t id = f->get_64();
|
||||
int32_t len = f->get_32();
|
||||
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.
|
||||
c.cs[len] = 0;
|
||||
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++) {
|
||||
int64_t id = p_cache_file->get_64();
|
||||
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());
|
||||
cs[len] = 0;
|
||||
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 txt;
|
||||
txt.resize(p_alpha ? 9 : 7);
|
||||
txt.resize_uninitialized(p_alpha ? 9 : 7);
|
||||
char32_t *ptr = txt.ptrw();
|
||||
|
||||
_append_hex(r, ptr + 0);
|
||||
|
@ -823,7 +823,7 @@ Expression::ENode *Expression::_parse_expression() {
|
||||
if (!Variant::is_utility_function_vararg(bifunc->func)) {
|
||||
int expected_args = Variant::get_utility_function_argument_count(bifunc->func);
|
||||
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;
|
||||
}
|
||||
|
||||
operator String() const;
|
||||
explicit operator String() const;
|
||||
bool is_empty() 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) {
|
||||
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());
|
||||
if (ret >= src_s.size()) {
|
||||
//if compressed is larger than original, just use original
|
||||
ps.orig_len = src_s.size();
|
||||
ps.compressed = src_s;
|
||||
} else {
|
||||
dst_s.resize(ret);
|
||||
dst_s.resize_uninitialized(ret);
|
||||
//ps.orig_len=;
|
||||
ps.compressed = dst_s;
|
||||
}
|
||||
} else {
|
||||
ps.orig_len = 1;
|
||||
ps.compressed.resize(1);
|
||||
ps.compressed.resize_uninitialized(1);
|
||||
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);
|
||||
} else {
|
||||
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);
|
||||
return String::utf8(uncomp.get_data());
|
||||
}
|
||||
@ -282,7 +282,7 @@ Vector<String> OptimizedTranslation::get_translated_message_list() const {
|
||||
msgs.push_back(rstr);
|
||||
} else {
|
||||
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);
|
||||
String rstr = String::utf8(uncomp.get_data());
|
||||
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();
|
||||
buffer.resize(next_power_of_2((uint32_t)p_size));
|
||||
buffer.resize_uninitialized(next_power_of_2((uint32_t)p_size));
|
||||
if (need_copy) {
|
||||
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()) {
|
||||
return String(short_buffer);
|
||||
} else {
|
||||
buffer.resize(string_length + 1);
|
||||
buffer.resize_uninitialized(string_length + 1);
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ String StringBuilder::as_string() const {
|
||||
}
|
||||
|
||||
String string;
|
||||
string.resize(string_length + 1);
|
||||
string.resize_uninitialized(string_length + 1);
|
||||
char32_t *buffer = string.ptrw();
|
||||
|
||||
int current_position = 0;
|
||||
|
@ -175,7 +175,7 @@ void String::append_latin1(const Span<char> &p_cstr) {
|
||||
}
|
||||
|
||||
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 *end = src + p_cstr.size();
|
||||
@ -194,7 +194,7 @@ void String::append_utf32(const Span<char32_t> &p_cstr) {
|
||||
}
|
||||
|
||||
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 *end = p_cstr.ptr() + p_cstr.size();
|
||||
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_char is a valid UTF32 string
|
||||
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();
|
||||
memcpy(dst, p_char, p_length * sizeof(char32_t));
|
||||
*(dst + p_length) = _null;
|
||||
@ -1365,7 +1365,7 @@ String String::join(const Vector<String> &parts) const {
|
||||
new_size += 1;
|
||||
|
||||
String ret;
|
||||
ret.resize(new_size);
|
||||
ret.resize_uninitialized(new_size);
|
||||
char32_t *ret_ptrw = ret.ptrw();
|
||||
const char32_t *this_ptr = ptr();
|
||||
|
||||
@ -1404,7 +1404,7 @@ String String::to_upper() const {
|
||||
}
|
||||
|
||||
String upper;
|
||||
upper.resize(size());
|
||||
upper.resize_uninitialized(size());
|
||||
const char32_t *old_ptr = ptr();
|
||||
char32_t *upper_ptrw = upper.ptrw();
|
||||
|
||||
@ -1423,7 +1423,7 @@ String String::to_lower() const {
|
||||
}
|
||||
|
||||
String lower;
|
||||
lower.resize(size());
|
||||
lower.resize_uninitialized(size());
|
||||
const char32_t *old_ptr = ptr();
|
||||
char32_t *lower_ptrw = lower.ptrw();
|
||||
|
||||
@ -1549,7 +1549,7 @@ String String::num_int64(int64_t p_num, int base, bool capitalize_hex) {
|
||||
chars++;
|
||||
}
|
||||
String s;
|
||||
s.resize(chars + 1);
|
||||
s.resize_uninitialized(chars + 1);
|
||||
char32_t *c = s.ptrw();
|
||||
c[chars] = 0;
|
||||
n = p_num;
|
||||
@ -1584,7 +1584,7 @@ String String::num_uint64(uint64_t p_num, int base, bool capitalize_hex) {
|
||||
} while (n);
|
||||
|
||||
String s;
|
||||
s.resize(chars + 1);
|
||||
s.resize_uninitialized(chars + 1);
|
||||
char32_t *c = s.ptrw();
|
||||
c[chars] = 0;
|
||||
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' };
|
||||
|
||||
String ret;
|
||||
ret.resize(p_len * 2 + 1);
|
||||
ret.resize_uninitialized(p_len * 2 + 1);
|
||||
char32_t *ret_ptrw = ret.ptrw();
|
||||
|
||||
for (int i = 0; i < p_len; i++) {
|
||||
@ -1706,7 +1706,7 @@ Vector<uint8_t> String::hex_decode() const {
|
||||
|
||||
Vector<uint8_t> out;
|
||||
int len = length() / 2;
|
||||
out.resize(len);
|
||||
out.resize_uninitialized(len);
|
||||
uint8_t *out_ptrw = out.ptrw();
|
||||
for (int i = 0; i < len; i++) {
|
||||
char32_t c;
|
||||
@ -1732,7 +1732,7 @@ CharString String::ascii(bool p_allow_extended) const {
|
||||
}
|
||||
|
||||
CharString cs;
|
||||
cs.resize(size());
|
||||
cs.resize_uninitialized(size());
|
||||
char *cs_ptrw = cs.ptrw();
|
||||
const char32_t *this_ptr = ptr();
|
||||
|
||||
@ -1755,7 +1755,7 @@ Error String::append_ascii(const Span<char> &p_range) {
|
||||
}
|
||||
|
||||
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 *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();
|
||||
// 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;
|
||||
|
||||
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;
|
||||
resize(prev_length + dst - ptr());
|
||||
resize_uninitialized(prev_length + dst - ptr());
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -1959,7 +1959,7 @@ CharString String::utf8(Vector<uint8_t> *r_ch_length_map) const {
|
||||
|
||||
uint8_t *map_ptr = nullptr;
|
||||
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();
|
||||
}
|
||||
|
||||
@ -1997,7 +1997,7 @@ CharString String::utf8(Vector<uint8_t> *r_ch_length_map) const {
|
||||
return utf8s;
|
||||
}
|
||||
|
||||
utf8s.resize(fl + 1);
|
||||
utf8s.resize_uninitialized(fl + 1);
|
||||
uint8_t *cdst = (uint8_t *)utf8s.get_data();
|
||||
|
||||
#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();
|
||||
resize(prev_length + str_size + 1);
|
||||
resize_uninitialized(prev_length + str_size + 1);
|
||||
char32_t *dst = ptrw() + prev_length;
|
||||
dst[str_size] = 0;
|
||||
|
||||
@ -2194,7 +2194,7 @@ Char16String String::utf16() const {
|
||||
return utf16s;
|
||||
}
|
||||
|
||||
utf16s.resize(fl + 1);
|
||||
utf16s.resize_uninitialized(fl + 1);
|
||||
uint16_t *cdst = (uint16_t *)utf16s.get_data();
|
||||
|
||||
#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);
|
||||
|
||||
Vector<uint8_t> ret;
|
||||
ret.resize(16);
|
||||
ret.resize_uninitialized(16);
|
||||
uint8_t *ret_ptrw = ret.ptrw();
|
||||
for (int i = 0; i < 16; 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);
|
||||
|
||||
Vector<uint8_t> ret;
|
||||
ret.resize(20);
|
||||
ret.resize_uninitialized(20);
|
||||
uint8_t *ret_ptrw = ret.ptrw();
|
||||
for (int i = 0; i < 20; 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);
|
||||
|
||||
Vector<uint8_t> ret;
|
||||
ret.resize(32);
|
||||
ret.resize_uninitialized(32);
|
||||
uint8_t *ret_ptrw = ret.ptrw();
|
||||
for (int i = 0; i < 32; i++) {
|
||||
ret_ptrw[i] = hash[i];
|
||||
@ -2894,7 +2894,7 @@ String String::insert(int p_at_pos, const String &p_string) const {
|
||||
}
|
||||
|
||||
String ret;
|
||||
ret.resize(length() + p_string.length() + 1);
|
||||
ret.resize_uninitialized(length() + p_string.length() + 1);
|
||||
char32_t *ret_ptrw = ret.ptrw();
|
||||
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.
|
||||
String new_string;
|
||||
new_string.resize(len);
|
||||
new_string.resize_uninitialized(len);
|
||||
char32_t *new_ptr = new_string.ptrw();
|
||||
|
||||
// Copy part of input before `char`.
|
||||
@ -2978,7 +2978,7 @@ String String::remove_char(char32_t p_char) const {
|
||||
new_ptr[new_size] = _null;
|
||||
|
||||
// Shrink new string to fit.
|
||||
new_string.resize(new_size + 1);
|
||||
new_string.resize_uninitialized(new_size + 1);
|
||||
|
||||
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.
|
||||
String new_string;
|
||||
new_string.resize(len);
|
||||
new_string.resize_uninitialized(len);
|
||||
char32_t *new_ptr = new_string.ptrw();
|
||||
|
||||
// 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;
|
||||
|
||||
// Shrink new string to fit.
|
||||
new_string.resize(new_size + 1);
|
||||
new_string.resize_uninitialized(new_size + 1);
|
||||
|
||||
return new_string;
|
||||
}
|
||||
@ -3747,7 +3747,7 @@ Vector<String> String::bigrams() const {
|
||||
if (n_pairs <= 0) {
|
||||
return b;
|
||||
}
|
||||
b.resize(n_pairs);
|
||||
b.resize_initialized(n_pairs);
|
||||
String *b_ptrw = b.ptrw();
|
||||
for (int i = 0; i < n_pairs; i++) {
|
||||
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 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();
|
||||
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 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();
|
||||
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();
|
||||
|
||||
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();
|
||||
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);
|
||||
|
||||
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();
|
||||
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.
|
||||
String new_string;
|
||||
new_string.resize(len + 1);
|
||||
new_string.resize_uninitialized(len + 1);
|
||||
char32_t *new_ptr = new_string.ptrw();
|
||||
|
||||
// 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.
|
||||
String new_string;
|
||||
new_string.resize(len + 1);
|
||||
new_string.resize_uninitialized(len + 1);
|
||||
char32_t *new_ptr = new_string.ptrw();
|
||||
|
||||
// Copy part of input before `key`.
|
||||
@ -4196,7 +4196,7 @@ String String::repeat(int p_count) const {
|
||||
|
||||
int len = length();
|
||||
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();
|
||||
int offset = 1;
|
||||
@ -4216,7 +4216,7 @@ String String::reverse() const {
|
||||
return *this;
|
||||
}
|
||||
String new_string;
|
||||
new_string.resize(len + 1);
|
||||
new_string.resize_uninitialized(len + 1);
|
||||
|
||||
const char32_t *src = ptr();
|
||||
char32_t *dst = new_string.ptrw();
|
||||
@ -4909,7 +4909,7 @@ String String::xml_unescape() const {
|
||||
if (len == 0) {
|
||||
return String();
|
||||
}
|
||||
str.resize(len + 1);
|
||||
str.resize_uninitialized(len + 1);
|
||||
char32_t *str_ptrw = str.ptrw();
|
||||
_xml_unescape(get_data(), l, str_ptrw);
|
||||
str_ptrw[len] = 0;
|
||||
@ -5880,7 +5880,7 @@ Vector<uint8_t> String::to_ascii_buffer() const {
|
||||
|
||||
Vector<uint8_t> retval;
|
||||
size_t len = charstr.length();
|
||||
retval.resize(len);
|
||||
retval.resize_uninitialized(len);
|
||||
uint8_t *w = retval.ptrw();
|
||||
memcpy(w, charstr.ptr(), len);
|
||||
|
||||
@ -5896,7 +5896,7 @@ Vector<uint8_t> String::to_utf8_buffer() const {
|
||||
|
||||
Vector<uint8_t> retval;
|
||||
size_t len = charstr.length();
|
||||
retval.resize(len);
|
||||
retval.resize_uninitialized(len);
|
||||
uint8_t *w = retval.ptrw();
|
||||
memcpy(w, charstr.ptr(), len);
|
||||
|
||||
@ -5912,7 +5912,7 @@ Vector<uint8_t> String::to_utf16_buffer() const {
|
||||
|
||||
Vector<uint8_t> retval;
|
||||
size_t len = charstr.length() * sizeof(char16_t);
|
||||
retval.resize(len);
|
||||
retval.resize_uninitialized(len);
|
||||
uint8_t *w = retval.ptrw();
|
||||
memcpy(w, (const void *)charstr.ptr(), len);
|
||||
|
||||
@ -5927,7 +5927,7 @@ Vector<uint8_t> String::to_utf32_buffer() const {
|
||||
|
||||
Vector<uint8_t> retval;
|
||||
size_t len = s->length() * sizeof(char32_t);
|
||||
retval.resize(len);
|
||||
retval.resize_uninitialized(len);
|
||||
uint8_t *w = retval.ptrw();
|
||||
memcpy(w, (const void *)s->ptr(), len);
|
||||
|
||||
|
@ -186,7 +186,9 @@ public:
|
||||
_FORCE_INLINE_ operator Span<T>() 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_ 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) {
|
||||
const int lhs_len = length();
|
||||
resize(lhs_len + 2);
|
||||
resize_uninitialized(lhs_len + 2);
|
||||
|
||||
T *dst = ptrw();
|
||||
dst[lhs_len] = p_char;
|
||||
@ -233,17 +235,17 @@ public:
|
||||
protected:
|
||||
void copy_from(const T *p_cstr) {
|
||||
if (!p_cstr) {
|
||||
resize(0);
|
||||
resize_uninitialized(0);
|
||||
return;
|
||||
}
|
||||
|
||||
size_t len = strlen(p_cstr);
|
||||
if (len == 0) {
|
||||
resize(0);
|
||||
resize_uninitialized(0);
|
||||
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.");
|
||||
|
||||
@ -320,11 +322,14 @@ public:
|
||||
|
||||
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_ 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 {
|
||||
if (unlikely(p_index == _cowdata.size())) {
|
||||
|
@ -51,8 +51,17 @@ public:
|
||||
std::is_same<T, wchar_t>>;
|
||||
|
||||
_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.
|
||||
template <size_t N>
|
||||
|
@ -1642,7 +1642,7 @@ String Variant::stringify(int recursion_count) const {
|
||||
case STRING_NAME:
|
||||
return operator StringName();
|
||||
case NODE_PATH:
|
||||
return operator NodePath();
|
||||
return String(operator NodePath());
|
||||
case COLOR:
|
||||
return String(operator Color());
|
||||
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));
|
||||
}
|
||||
} 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) {
|
||||
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) {
|
||||
err_text = "Method not found";
|
||||
} else if (ce.error == Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL) {
|
||||
|
@ -685,7 +685,7 @@ struct _VariantCall {
|
||||
if (p_instance->size() > 0) {
|
||||
const uint8_t *r = p_instance->ptr();
|
||||
CharString cs;
|
||||
cs.resize(p_instance->size() + 1);
|
||||
cs.resize_uninitialized(p_instance->size() + 1);
|
||||
memcpy(cs.ptrw(), r, p_instance->size());
|
||||
cs[(int)p_instance->size()] = 0;
|
||||
|
||||
@ -1012,6 +1012,62 @@ struct _VariantCall {
|
||||
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) {
|
||||
uint64_t size = p_instance->size();
|
||||
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_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_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, bswap32, _VariantCall::func_PackedByteArray_bswap32, sarray("offset", "count"), varray(0, -1));
|
||||
|
@ -91,7 +91,7 @@
|
||||
<method name="get_length" qualifiers="const">
|
||||
<return type="float" />
|
||||
<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>
|
||||
</method>
|
||||
<method name="instantiate_playback">
|
||||
|
@ -644,7 +644,8 @@
|
||||
The filtering mode used to render this [CanvasItem]'s texture(s).
|
||||
</member>
|
||||
<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 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].
|
||||
@ -754,7 +755,7 @@
|
||||
The [CanvasItem] will inherit the filter from its parent.
|
||||
</constant>
|
||||
<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 name="TEXTURE_REPEAT_ENABLED" value="2" enum="TextureRepeat">
|
||||
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]
|
||||
</constant>
|
||||
<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 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]
|
||||
@ -3073,7 +3073,7 @@
|
||||
<constant name="WINDOW_FLAG_TRANSPARENT" value="3" enum="WindowFlags">
|
||||
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] 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 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.
|
||||
|
@ -905,6 +905,13 @@
|
||||
Returns the travel cost of this [param region].
|
||||
</description>
|
||||
</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">
|
||||
<return type="bool" />
|
||||
<param index="0" name="region" type="RID" />
|
||||
@ -986,6 +993,14 @@
|
||||
Sets the [param travel_cost] for this [param region].
|
||||
</description>
|
||||
</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">
|
||||
<return type="void" />
|
||||
<param index="0" name="region" type="RID" />
|
||||
|
@ -497,6 +497,13 @@
|
||||
Sorts the elements of the array in ascending order.
|
||||
</description>
|
||||
</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">
|
||||
<return type="PackedFloat32Array" />
|
||||
<description>
|
||||
@ -529,6 +536,27 @@
|
||||
If the original data can't be converted to signed 64-bit integers, the resulting data is undefined.
|
||||
</description>
|
||||
</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>
|
||||
<operators>
|
||||
<operator name="operator !=">
|
||||
|
@ -854,7 +854,6 @@
|
||||
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.
|
||||
[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]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>
|
||||
|
@ -155,7 +155,7 @@ Error DirAccessWindows::change_dir(String p_dir) {
|
||||
|
||||
Char16String real_current_dir_name;
|
||||
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());
|
||||
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();
|
||||
if (!base.is_empty()) {
|
||||
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());
|
||||
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)) {
|
||||
@ -175,7 +175,7 @@ Error DirAccessWindows::change_dir(String p_dir) {
|
||||
|
||||
if (worked) {
|
||||
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());
|
||||
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;
|
||||
}
|
||||
Char16String cs;
|
||||
cs.resize(ret + 1);
|
||||
cs.resize_uninitialized(ret + 1);
|
||||
GetFinalPathNameByHandleW(hfile, (LPWSTR)cs.ptrw(), ret, VOLUME_NAME_DOS | FILE_NAME_NORMALIZED);
|
||||
CloseHandle(hfile);
|
||||
|
||||
@ -475,7 +475,7 @@ DirAccessWindows::DirAccessWindows() {
|
||||
|
||||
Char16String real_current_dir_name;
|
||||
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());
|
||||
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()) {
|
||||
Char16String current_dir_name;
|
||||
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());
|
||||
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;
|
||||
}
|
||||
|
||||
String base_path = animation->track_get_path(i);
|
||||
String base_path = String(animation->track_get_path(i));
|
||||
int end = base_path.find_char(':');
|
||||
if (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];
|
||||
|
||||
String path = animation->track_get_path(current_track);
|
||||
String path = String(animation->track_get_path(current_track));
|
||||
path = path.replace_first(base_path, "");
|
||||
|
||||
Color cc = color;
|
||||
@ -763,7 +763,7 @@ bool AnimationBezierTrackEdit::_is_track_displayed(int p_track_index) {
|
||||
}
|
||||
|
||||
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)) {
|
||||
Node *node = root->get_node(path);
|
||||
if (!node) {
|
||||
@ -899,7 +899,7 @@ void AnimationBezierTrackEdit::set_filtered(bool p_filtered) {
|
||||
if (animation.is_null()) {
|
||||
return;
|
||||
}
|
||||
String base_path = animation->track_get_path(selected_track);
|
||||
String base_path = String(animation->track_get_path(selected_track));
|
||||
if (is_filtered) {
|
||||
if (root && root->has_node(base_path)) {
|
||||
Node *node = root->get_node(base_path);
|
||||
@ -909,7 +909,7 @@ void AnimationBezierTrackEdit::set_filtered(bool p_filtered) {
|
||||
continue;
|
||||
}
|
||||
|
||||
base_path = animation->track_get_path(i);
|
||||
base_path = String(animation->track_get_path(i));
|
||||
if (root && root->has_node(base_path)) {
|
||||
node = root->get_node(base_path);
|
||||
if (!node) {
|
||||
|
@ -2191,7 +2191,7 @@ void AnimationTrackEdit::_notification(int p_what) {
|
||||
} else {
|
||||
icon_cache = key_type_icon;
|
||||
|
||||
text = anim_path;
|
||||
text = String(anim_path);
|
||||
}
|
||||
|
||||
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.
|
||||
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)) {
|
||||
@ -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->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();
|
||||
|
||||
moving_selection_attempt = false;
|
||||
@ -3462,7 +3462,7 @@ Variant AnimationTrackEdit::get_drag_data(const Point2 &p_point) {
|
||||
|
||||
Dictionary drag_data;
|
||||
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.
|
||||
drag_data["group"] = base_path;
|
||||
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.
|
||||
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.
|
||||
if (d["group"] != base_path) {
|
||||
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.
|
||||
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.
|
||||
if (d["group"] != base_path) {
|
||||
return;
|
||||
@ -4370,7 +4370,7 @@ void AnimationTrackEditor::insert_transform_key(Node3D *p_node, const String &p_
|
||||
}
|
||||
|
||||
// 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()) {
|
||||
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.
|
||||
String path = root->get_path_to(p_node, true);
|
||||
String path = String(root->get_path_to(p_node, true));
|
||||
if (!p_sub.is_empty()) {
|
||||
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) {
|
||||
String path = p_path;
|
||||
String path = String(p_path);
|
||||
|
||||
// Animation property is a special case, always creates an animation track.
|
||||
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) {
|
||||
// Exists.
|
||||
@ -4460,7 +4460,7 @@ void AnimationTrackEditor::insert_node_value_key(Node *p_node, const String &p_p
|
||||
ERR_FAIL_NULL(root);
|
||||
|
||||
// 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.
|
||||
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;
|
||||
} else if (animation->track_get_type(i) == Animation::TYPE_BEZIER) {
|
||||
Variant actual_value;
|
||||
String track_path = animation->track_get_path(i);
|
||||
if (track_path == np) {
|
||||
String track_path = String(animation->track_get_path(i));
|
||||
if (track_path == String(np)) {
|
||||
actual_value = value; // All good.
|
||||
} else {
|
||||
int sep = track_path.rfind_char(':');
|
||||
if (sep != -1) {
|
||||
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);
|
||||
actual_value = value.get(value_name);
|
||||
} else {
|
||||
@ -5017,7 +5017,7 @@ void AnimationTrackEditor::_update_tracks() {
|
||||
String filter_text = timeline->filter_track->get_text();
|
||||
|
||||
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)) {
|
||||
continue;
|
||||
}
|
||||
@ -5087,7 +5087,7 @@ void AnimationTrackEditor::_update_tracks() {
|
||||
track_edits.push_back(track_edit);
|
||||
|
||||
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.
|
||||
|
||||
if (!group_sort.has(base_path)) {
|
||||
@ -5100,7 +5100,7 @@ void AnimationTrackEditor::_update_tracks() {
|
||||
if (n) {
|
||||
icon = EditorNode::get_singleton()->get_object_icon(n, "Node");
|
||||
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) {
|
||||
last_menu_track_opt = p_option;
|
||||
switch (p_option) {
|
||||
case EDIT_COPY_TRACKS: {
|
||||
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.
|
||||
} else {
|
||||
text = path;
|
||||
text = String(path);
|
||||
int sep = text.find_char(':');
|
||||
if (sep != -1) {
|
||||
text = text.substr(sep + 1);
|
||||
@ -6841,11 +6840,15 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
|
||||
|
||||
undo_redo->commit_action();
|
||||
} 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: {
|
||||
scale_dialog->popup_centered(Size2(200, 100) * EDSCALE);
|
||||
scale->get_line_edit()->grab_focus();
|
||||
scale_from_cursor = true;
|
||||
} break;
|
||||
case EDIT_SCALE_CONFIRM: {
|
||||
if (selection.is_empty()) {
|
||||
@ -6868,9 +6871,8 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
|
||||
}
|
||||
|
||||
len = to_t - from_t;
|
||||
if (last_menu_track_opt == EDIT_SCALE_FROM_CURSOR) {
|
||||
if (scale_from_cursor) {
|
||||
pivot = timeline->get_play_position();
|
||||
|
||||
} else {
|
||||
pivot = from_t;
|
||||
}
|
||||
@ -6912,7 +6914,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
|
||||
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).
|
||||
for (RBMap<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
|
||||
float newpos = NEW_POS(E->get().pos);
|
||||
|
@ -802,7 +802,7 @@ class AnimationTrackEditor : public VBoxContainer {
|
||||
|
||||
void _edit_menu_about_to_popup();
|
||||
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);
|
||||
|
||||
|
@ -413,7 +413,7 @@ Rect2 AnimationTrackEditSpriteFrame::get_key_rect(int p_index, float p_pixels_se
|
||||
animation_name = animations.front()->get();
|
||||
} else {
|
||||
// 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");
|
||||
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);
|
||||
@ -505,7 +505,7 @@ void AnimationTrackEditSpriteFrame::draw_key(int p_index, float p_pixels_sec, in
|
||||
animation_name = animations.front()->get();
|
||||
} else {
|
||||
// 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");
|
||||
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);
|
||||
|
@ -39,9 +39,13 @@
|
||||
#include "editor/plugins/script_editor_plugin.h"
|
||||
#include "editor/themes/editor_scale.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/menu_button.h"
|
||||
#include "scene/gui/rich_text_label.h"
|
||||
#include "scene/gui/separator.h"
|
||||
#include "scene/main/timer.h"
|
||||
#include "scene/resources/font.h"
|
||||
|
||||
void GotoLinePopup::popup_find_line(CodeTextEditor *p_text_editor) {
|
||||
@ -716,10 +720,6 @@ bool FindReplaceBar::is_selection_only() const {
|
||||
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) {
|
||||
if (p_text_editor == base_text_editor) {
|
||||
return;
|
||||
@ -749,8 +749,6 @@ void FindReplaceBar::set_text_edit(CodeTextEditor *p_text_editor) {
|
||||
|
||||
void FindReplaceBar::_bind_methods() {
|
||||
ClassDB::bind_method("_search_current", &FindReplaceBar::search_current);
|
||||
|
||||
ADD_SIGNAL(MethodInfo("error"));
|
||||
}
|
||||
|
||||
FindReplaceBar::FindReplaceBar() {
|
||||
@ -1187,7 +1185,6 @@ void CodeTextEditor::set_find_replace_bar(FindReplaceBar *p_bar) {
|
||||
|
||||
find_replace_bar = p_bar;
|
||||
find_replace_bar->set_text_edit(this);
|
||||
find_replace_bar->connect("error", callable_mp(error, &Label::set_text));
|
||||
}
|
||||
|
||||
void CodeTextEditor::remove_find_replace_bar() {
|
||||
@ -1195,7 +1192,6 @@ void CodeTextEditor::remove_find_replace_bar() {
|
||||
return;
|
||||
}
|
||||
|
||||
find_replace_bar->disconnect("error", callable_mp(error, &Label::set_text));
|
||||
find_replace_bar = nullptr;
|
||||
}
|
||||
|
||||
@ -1516,20 +1512,35 @@ Variant CodeTextEditor::get_navigation_state() {
|
||||
}
|
||||
|
||||
void CodeTextEditor::set_error(const String &p_error) {
|
||||
// Trim the error message if it is more than 2 lines long.
|
||||
if (p_error.count("\n") >= 2) {
|
||||
Vector<String> splits = p_error.split("\n");
|
||||
String trimmed_error = String("\n").join(splits.slice(0, 2));
|
||||
error->set_text(trimmed_error + "...");
|
||||
} else {
|
||||
error->set_text(p_error);
|
||||
|
||||
_update_error_content_height();
|
||||
|
||||
if (p_error.is_empty()) {
|
||||
error->set_default_cursor_shape(CURSOR_ARROW);
|
||||
} else {
|
||||
error->set_default_cursor_shape(CURSOR_POINTING_HAND);
|
||||
}
|
||||
}
|
||||
|
||||
if (!p_error.is_empty()) {
|
||||
error->set_default_cursor_shape(CURSOR_POINTING_HAND);
|
||||
} else {
|
||||
error->set_default_cursor_shape(CURSOR_ARROW);
|
||||
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);
|
||||
}
|
||||
|
||||
const float content_height = margin_height + error->get_content_height();
|
||||
|
||||
float content_max_height = margin_height;
|
||||
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) {
|
||||
@ -1559,28 +1570,36 @@ void CodeTextEditor::goto_error() {
|
||||
void CodeTextEditor::_update_text_editor_theme() {
|
||||
emit_signal(SNAME("load_theme_settings"));
|
||||
|
||||
error_button->set_button_icon(get_editor_theme_icon(SNAME("StatusError")));
|
||||
warning_button->set_button_icon(get_editor_theme_icon(SNAME("NodeWarning")));
|
||||
|
||||
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 Ref<Font> status_bar_font = get_theme_font(SNAME("status_source"), EditorStringName(EditorFonts));
|
||||
const int status_bar_font_size = get_theme_font_size(SNAME("status_source_size"), EditorStringName(EditorFonts));
|
||||
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 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);
|
||||
|
||||
warning_button->set_button_icon(get_editor_theme_icon(SNAME("NodeWarning")));
|
||||
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();
|
||||
}
|
||||
|
||||
@ -1909,19 +1928,16 @@ CodeTextEditor::CodeTextEditor() {
|
||||
toggle_files_button->hide();
|
||||
|
||||
// Error
|
||||
ScrollContainer *scroll = memnew(ScrollContainer);
|
||||
scroll->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
scroll->set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
scroll->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
|
||||
status_bar->add_child(scroll);
|
||||
|
||||
error = memnew(Label);
|
||||
error->set_focus_mode(FOCUS_ACCESSIBILITY);
|
||||
error = memnew(RichTextLabel);
|
||||
error->set_use_bbcode(true);
|
||||
error->set_selection_enabled(true);
|
||||
error->set_context_menu_enabled(true);
|
||||
error->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
|
||||
error->set_v_size_flags(SIZE_EXPAND | SIZE_SHRINK_CENTER);
|
||||
error->set_mouse_filter(MOUSE_FILTER_STOP);
|
||||
scroll->add_child(error);
|
||||
error->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
error->set_v_size_flags(SIZE_SHRINK_CENTER);
|
||||
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
|
||||
error_button = memnew(Button);
|
||||
|
@ -31,17 +31,17 @@
|
||||
#pragma once
|
||||
|
||||
#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/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 Label;
|
||||
class LineEdit;
|
||||
class MenuButton;
|
||||
class RichTextLabel;
|
||||
class Timer;
|
||||
|
||||
class GotoLinePopup : public PopupPanel {
|
||||
GDCLASS(GotoLinePopup, PopupPanel);
|
||||
@ -138,7 +138,6 @@ public:
|
||||
bool is_case_sensitive() const;
|
||||
bool is_whole_words() const;
|
||||
bool is_selection_only() const;
|
||||
void set_error(const String &p_label);
|
||||
|
||||
void set_text_edit(CodeTextEditor *p_text_editor);
|
||||
|
||||
@ -183,7 +182,7 @@ class CodeTextEditor : public VBoxContainer {
|
||||
|
||||
float zoom_factor = 1.0f;
|
||||
|
||||
Label *error = nullptr;
|
||||
RichTextLabel *error = nullptr;
|
||||
int error_line;
|
||||
int error_column;
|
||||
|
||||
@ -211,6 +210,8 @@ class CodeTextEditor : public VBoxContainer {
|
||||
void _zoom_out();
|
||||
void _zoom_to(float p_zoom_factor);
|
||||
|
||||
void _update_error_content_height();
|
||||
|
||||
void _error_button_pressed();
|
||||
void _warning_button_pressed();
|
||||
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.
|
||||
error_title += oe.error_descr.is_empty() ? oe.error : oe.error_descr;
|
||||
error->set_text(1, error_title);
|
||||
error->set_autowrap_mode(1, TextServer::AUTOWRAP_WORD_SMART);
|
||||
tooltip += " " + error_title + "\n";
|
||||
|
||||
// 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) {
|
||||
switch (p_type) {
|
||||
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;
|
||||
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;
|
||||
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);
|
||||
|
||||
_update_reason_content_height();
|
||||
|
||||
const PackedInt32Array boundaries = TS->string_get_word_breaks(p_reason, "", 80);
|
||||
PackedStringArray lines;
|
||||
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));
|
||||
}
|
||||
|
||||
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) {
|
||||
switch (p_what) {
|
||||
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")));
|
||||
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();
|
||||
if (error_root) {
|
||||
@ -1245,6 +1271,7 @@ void ScriptEditorDebugger::stop() {
|
||||
peer.unref();
|
||||
reason->set_text("");
|
||||
reason->set_tooltip_text("");
|
||||
reason->set_custom_minimum_size(Size2(0, 0));
|
||||
}
|
||||
|
||||
node_path_cache.clear();
|
||||
@ -1531,7 +1558,7 @@ void ScriptEditorDebugger::update_live_edit_root() {
|
||||
msg.push_back("");
|
||||
}
|
||||
_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) {
|
||||
@ -1966,14 +1993,14 @@ ScriptEditorDebugger::ScriptEditorDebugger() {
|
||||
HBoxContainer *hbc = memnew(HBoxContainer);
|
||||
vbc->add_child(hbc);
|
||||
|
||||
reason = memnew(Label);
|
||||
reason = memnew(RichTextLabel);
|
||||
reason->set_focus_mode(FOCUS_ACCESSIBILITY);
|
||||
reason->set_text("");
|
||||
hbc->add_child(reason);
|
||||
reason->set_selection_enabled(true);
|
||||
reason->set_context_menu_enabled(true);
|
||||
reason->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
reason->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART);
|
||||
reason->set_max_lines_visible(3);
|
||||
reason->set_mouse_filter(Control::MOUSE_FILTER_PASS);
|
||||
reason->set_v_size_flags(SIZE_SHRINK_CENTER);
|
||||
reason->connect(SceneStringName(resized), callable_mp(this, &ScriptEditorDebugger::_update_reason_content_height));
|
||||
hbc->add_child(reason);
|
||||
|
||||
hbc->add_child(memnew(VSeparator));
|
||||
|
||||
|
@ -118,7 +118,7 @@ private:
|
||||
|
||||
TabContainer *tabs = nullptr;
|
||||
|
||||
Label *reason = nullptr;
|
||||
RichTextLabel *reason = nullptr;
|
||||
|
||||
Button *skip_breakpoints = 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 _set_reason_text(const String &p_reason, MessageType p_type);
|
||||
void _update_reason_content_height();
|
||||
void _update_buttons_state();
|
||||
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);
|
||||
|
@ -6636,7 +6636,7 @@ void EditorNode::reload_instances_with_path_in_edited_scenes() {
|
||||
// it's a multi-level inheritance scene. We should use
|
||||
NodePath scene_path_to_node = current_edited_scene->get_path_to(original_node);
|
||||
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);
|
||||
instantiated_node = root_node->get_node(scene_path_to_node);
|
||||
|
||||
|
@ -2866,7 +2866,7 @@ void EditorPropertyNodePath::_menu_option(int p_idx) {
|
||||
} break;
|
||||
|
||||
case ACTION_COPY: {
|
||||
DisplayServer::get_singleton()->clipboard_set(_get_node_path());
|
||||
DisplayServer::get_singleton()->clipboard_set(String(_get_node_path()));
|
||||
} break;
|
||||
|
||||
case ACTION_EDIT: {
|
||||
@ -2874,7 +2874,7 @@ void EditorPropertyNodePath::_menu_option(int p_idx) {
|
||||
menu->hide();
|
||||
|
||||
const NodePath &np = _get_node_path();
|
||||
edit->set_text(np);
|
||||
edit->set_text(String(np));
|
||||
edit->show();
|
||||
callable_mp((Control *)edit, &Control::grab_focus).call_deferred();
|
||||
} break;
|
||||
@ -2976,7 +2976,7 @@ bool EditorPropertyNodePath::is_drop_valid(const Dictionary &p_drag_data) const
|
||||
void EditorPropertyNodePath::update_property() {
|
||||
const Node *base_node = get_base_node();
|
||||
const NodePath &p = _get_node_path();
|
||||
assign->set_tooltip_text(p);
|
||||
assign->set_tooltip_text(String(p));
|
||||
|
||||
if (p.is_empty()) {
|
||||
assign->set_button_icon(Ref<Texture2D>());
|
||||
@ -2988,7 +2988,7 @@ void EditorPropertyNodePath::update_property() {
|
||||
|
||||
if (!base_node || !base_node->has_node(p)) {
|
||||
assign->set_button_icon(Ref<Texture2D>());
|
||||
assign->set_text(p);
|
||||
assign->set_text(String(p));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2997,7 +2997,7 @@ void EditorPropertyNodePath::update_property() {
|
||||
|
||||
if (String(target_node->get_name()).contains_char('@')) {
|
||||
assign->set_button_icon(Ref<Texture2D>());
|
||||
assign->set_text(p);
|
||||
assign->set_text(String(p));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -430,7 +430,7 @@ void FileSystemDock::_update_tree(const Vector<String> &p_uncollapsed_paths, boo
|
||||
} else if (favorite.ends_with("/")) {
|
||||
text = favorite.substr(0, favorite.length() - 1).get_file();
|
||||
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 {
|
||||
text = favorite.get_file();
|
||||
int index;
|
||||
@ -3951,6 +3951,30 @@ void FileSystemDock::_file_sort_popup(int 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 {
|
||||
return folder_colors;
|
||||
}
|
||||
|
@ -383,6 +383,8 @@ public:
|
||||
static constexpr double ITEM_ALPHA_MAX = 0.15;
|
||||
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;
|
||||
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.
|
||||
void EditorFileDialog::update_file_list() {
|
||||
int thumbnail_size = EDITOR_GET("filesystem/file_dialog/thumbnail_size");
|
||||
@ -1155,7 +1126,7 @@ void EditorFileDialog::update_file_list() {
|
||||
d["bundle"] = bundle;
|
||||
|
||||
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();
|
||||
@ -1840,7 +1811,7 @@ void EditorFileDialog::_update_favorites() {
|
||||
favorites->add_item(favorited_names[i], theme_cache.folder);
|
||||
favorites->set_item_tooltip(-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) {
|
||||
favorite->set_pressed(true);
|
||||
@ -1925,7 +1896,7 @@ void EditorFileDialog::_update_recent() {
|
||||
recent->add_item(recentd_names[i], theme_cache.folder);
|
||||
recent->set_item_tooltip(-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) {
|
||||
|
@ -301,8 +301,6 @@ protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
Color get_dir_icon_color(const String &p_dir_path);
|
||||
|
||||
virtual void set_visible(bool p_visible) override;
|
||||
virtual void popup(const Rect2i &p_rect = Rect2i()) override;
|
||||
|
||||
|
@ -99,10 +99,7 @@ void EditorObjectSelector::_show_popup() {
|
||||
Point2 gp = get_screen_position();
|
||||
gp.y += size.y;
|
||||
|
||||
sub_objects_menu->set_position(gp);
|
||||
sub_objects_menu->set_size(Size2(size.width, 1));
|
||||
|
||||
sub_objects_menu->popup();
|
||||
sub_objects_menu->popup(Rect2(gp, Size2(size.width, 0)));
|
||||
}
|
||||
|
||||
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];
|
||||
String path = scene->get_path_to(nm.node);
|
||||
String path = String(scene->get_path_to(nm.node));
|
||||
|
||||
if (nm.bone >= 0) {
|
||||
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];
|
||||
String path = scene->get_path_to(nm.node);
|
||||
String path = String(scene->get_path_to(nm.node));
|
||||
|
||||
animation->add_track(Animation::TYPE_BLEND_SHAPE);
|
||||
int track = animation->get_track_count() - 1;
|
||||
|
@ -228,9 +228,9 @@ void PostImportPluginSkeletonRenamer::internal_process(InternalImportCategory p_
|
||||
}
|
||||
} else {
|
||||
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 {
|
||||
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;
|
||||
|
@ -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;
|
||||
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;
|
||||
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;
|
||||
|
||||
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;
|
||||
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) {
|
||||
Ref<Animation> anim = tree->get_animation(E);
|
||||
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);
|
||||
|
||||
String track_type_name;
|
||||
@ -890,8 +890,8 @@ bool AnimationNodeBlendTreeEditor::_update_filters(const Ref<AnimationNode> &ano
|
||||
if (ti) {
|
||||
//just a node, not a property track
|
||||
String types_text = "[";
|
||||
if (types.has(path)) {
|
||||
RBSet<String>::Iterator F = types[path].begin();
|
||||
if (types.has(String(path))) {
|
||||
RBSet<String>::Iterator F = types[String(path)].begin();
|
||||
types_text += *F;
|
||||
while (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
|
||||
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_ids;
|
||||
@ -886,7 +886,7 @@ Vector<uint64_t> AnimationLibraryEditor::_load_mixer_libs_folding() {
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
|
@ -1963,7 +1963,7 @@ bool AnimationPlayerEditor::_validate_tracks(const Ref<Animation> p_anim) {
|
||||
for (int j = 0; j < key_len; j++) {
|
||||
Quaternion 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) {
|
||||
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));
|
||||
if (!q.is_normalized()) {
|
||||
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;
|
||||
@ -1985,7 +1985,7 @@ bool AnimationPlayerEditor::_validate_tracks(const Ref<Animation> p_anim) {
|
||||
Transform3D t = Transform3D(p_anim->track_get_key_value(i, j));
|
||||
if (!t.basis.orthonormalized().is_rotation()) {
|
||||
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;
|
||||
|
@ -2436,7 +2436,7 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) {
|
||||
CanvasItem *item = selection_results[i].item;
|
||||
|
||||
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;
|
||||
if (_is_node_locked(item)) {
|
||||
@ -2503,7 +2503,7 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) {
|
||||
String *paths_write = paths.ptrw();
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -546,7 +546,7 @@ void MeshInstance3DEditor::_create_outline_mesh() {
|
||||
Node *skeleton = node->get_node_or_null(node->get_skeleton_path());
|
||||
if (skeleton && node->get_skin().is_valid()) {
|
||||
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();
|
||||
|
@ -222,9 +222,9 @@ void MultiMeshEditor::_browsed(const NodePath &p_path) {
|
||||
NodePath path = node->get_path_to(get_node(p_path));
|
||||
|
||||
if (browsing_source) {
|
||||
mesh_source->set_text(path);
|
||||
mesh_source->set_text(String(path));
|
||||
} 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) {
|
||||
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) {
|
||||
Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_CAPTURED);
|
||||
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());
|
||||
if (mb->is_pressed()) {
|
||||
gizmo_activated = true;
|
||||
original_mouse_pos = get_local_mouse_position();
|
||||
grab_focus();
|
||||
}
|
||||
} 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");
|
||||
|
||||
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;
|
||||
if (_is_node_locked(spat)) {
|
||||
@ -1985,7 +1988,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
|
||||
|
||||
surface->queue_redraw();
|
||||
} else {
|
||||
if (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_RULER) {
|
||||
if (ruler->is_inside_tree()) {
|
||||
EditorNode::get_singleton()->get_scene_root()->remove_child(ruler);
|
||||
ruler_start_point->set_visible(false);
|
||||
ruler_end_point->set_visible(false);
|
||||
|
@ -82,6 +82,7 @@ class ViewportRotationControl : public Control {
|
||||
Vector<Color> axis_colors;
|
||||
Vector<int> axis_menu_options;
|
||||
Vector2i orbiting_mouse_start;
|
||||
Point2 original_mouse_pos;
|
||||
int orbiting_index = -1;
|
||||
int focused_axis = -2;
|
||||
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++) {
|
||||
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.
|
||||
if (node_type.is_empty()) {
|
||||
@ -83,7 +83,7 @@ Error PackedSceneEditorTranslationParserPlugin::parse_file(const String &p_path,
|
||||
auto_translate_mode_found = true;
|
||||
|
||||
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.
|
||||
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_found) {
|
||||
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;
|
||||
} else {
|
||||
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") {
|
||||
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++) {
|
||||
|
@ -166,7 +166,7 @@ void EditorPropertyRootMotion::_node_clear() {
|
||||
|
||||
void EditorPropertyRootMotion::update_property() {
|
||||
NodePath p = get_edited_property_value();
|
||||
assign->set_tooltip_text(p);
|
||||
assign->set_tooltip_text(String(p));
|
||||
if (p == NodePath()) {
|
||||
assign->set_button_icon(Ref<Texture2D>());
|
||||
assign->set_text(TTR("Assign..."));
|
||||
@ -175,7 +175,7 @@ void EditorPropertyRootMotion::update_property() {
|
||||
}
|
||||
|
||||
assign->set_button_icon(Ref<Texture2D>());
|
||||
assign->set_text(p);
|
||||
assign->set_text(String(p));
|
||||
}
|
||||
|
||||
void EditorPropertyRootMotion::setup(const NodePath &p_base_hint) {
|
||||
|
@ -819,10 +819,12 @@ void ScriptTextEditor::_validate_script() {
|
||||
}
|
||||
|
||||
if (errors.size() > 0) {
|
||||
// TRANSLATORS: Script error pointing to a line and column number.
|
||||
String error_text = vformat(TTR("Error at (%d, %d):"), errors.front()->get().line, errors.front()->get().column) + " " + errors.front()->get().message;
|
||||
const int line = errors.front()->get().line;
|
||||
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_pos(errors.front()->get().line - 1, errors.front()->get().column - 1);
|
||||
code_editor->set_error_pos(line - 1, column - 1);
|
||||
}
|
||||
script_is_valid = false;
|
||||
} else {
|
||||
@ -861,8 +863,8 @@ void ScriptTextEditor::_update_warnings() {
|
||||
warnings_panel->push_table(1);
|
||||
for (const Connection &connection : missing_connections) {
|
||||
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 target_path = base == connection.callable.get_object() ? base_path : base_path + "/" + base->get_path_to(Object::cast_to<Node>(connection.callable.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 + "/" + String(base->get_path_to(Object::cast_to<Node>(connection.callable.get_object())));
|
||||
|
||||
warnings_panel->push_cell();
|
||||
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);
|
||||
|
||||
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();
|
||||
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();
|
||||
te->make_insert_queue();
|
||||
|
@ -200,7 +200,7 @@ void TextEditor::_validate_script() {
|
||||
code_editor->set_error("");
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
if (last_compile_result != OK) {
|
||||
//preprocessor error
|
||||
// Preprocessor error.
|
||||
ERR_FAIL_COND(err_positions.is_empty());
|
||||
|
||||
String err_text = error_pp;
|
||||
int err_line = err_positions.front()->get().line;
|
||||
String err_text;
|
||||
const int err_line = err_positions.front()->get().line;
|
||||
if (err_positions.size() == 1) {
|
||||
// Error in main file
|
||||
err_text = "error(" + itos(err_line) + "): " + err_text;
|
||||
// Error in the main file.
|
||||
const String message = error_pp.replace("[", "[lb]");
|
||||
|
||||
err_text = vformat(TTR("Error at line %d:"), err_line) + " " + message;
|
||||
} 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(err_text);
|
||||
set_error_pos(err_line - 1, 0);
|
||||
|
||||
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(err_line - 1, marked_line_color);
|
||||
|
||||
set_warning_count(0);
|
||||
|
||||
} else {
|
||||
ShaderLanguage sl;
|
||||
|
||||
@ -579,21 +586,33 @@ void ShaderTextEditor::_validate_script() {
|
||||
last_compile_result = sl.compile(code, comp_info);
|
||||
|
||||
if (last_compile_result != OK) {
|
||||
Vector<ShaderLanguage::FilePosition> include_positions = sl.get_include_positions();
|
||||
|
||||
String err_text;
|
||||
int err_line;
|
||||
Vector<ShaderLanguage::FilePosition> include_positions = sl.get_include_positions();
|
||||
if (include_positions.size() > 1) {
|
||||
//error is in an include
|
||||
// Error in an included file.
|
||||
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);
|
||||
} else {
|
||||
// Error in the main file.
|
||||
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(err_text);
|
||||
set_error_pos(err_line - 1, 0);
|
||||
|
||||
get_text_editor()->set_line_background_color(err_line - 1, marked_line_color);
|
||||
} else {
|
||||
set_error("");
|
||||
@ -624,9 +643,12 @@ void ShaderTextEditor::_update_warning_panel() {
|
||||
for (const ShaderWarning &w : warnings) {
|
||||
if (warning_count == 0) {
|
||||
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.");
|
||||
set_error_pos(w.get_line() - 1, 0);
|
||||
const String message = (w.get_message() + " " + TTR("Warnings should be fixed to prevent errors.")).replace("[", "[lb]");
|
||||
const String error_text = vformat(TTR("Error at line %d:"), w.get_line()) + " " + message;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -1218,7 +1218,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
|
||||
if (node) {
|
||||
Node *root = EditorNode::get_singleton()->get_edited_scene();
|
||||
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;
|
||||
@ -1998,7 +1998,7 @@ bool SceneTreeDock::_update_node_path(Node *p_root_node, NodePath &r_node_path,
|
||||
if (found_root_path) {
|
||||
NodePath root_path_new = found_root_path->value;
|
||||
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();
|
||||
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);
|
||||
path_renames[node] = fixed_node_path;
|
||||
} 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;
|
||||
Node *root = EditorNode::get_singleton()->get_edited_scene();
|
||||
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);
|
||||
}
|
||||
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;
|
||||
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();
|
||||
transform_node_path = path + ":" + bone;
|
||||
} 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();
|
||||
|
||||
// Assign range args.
|
||||
if (range_from.type == p_from.type) {
|
||||
write_assign(range_from, p_from);
|
||||
} 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) {
|
||||
|
@ -234,6 +234,19 @@ public:
|
||||
|
||||
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) {
|
||||
kind = p_other.kind;
|
||||
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)) + ".";
|
||||
case Callable::CallError::CALL_ERROR_TOO_MANY_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:
|
||||
return "Attempt to call " + p_where + " on a null instance.";
|
||||
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) {
|
||||
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())) {
|
||||
Ref<PackedScene> owner_packed_scene = Ref<PackedScene>(Object::cast_to<PackedScene>(*owner_res));
|
||||
owner_scene_node = owner_packed_scene->instantiate();
|
||||
|
@ -1,7 +1,11 @@
|
||||
# GH-83293
|
||||
|
||||
func test():
|
||||
# GH-83293
|
||||
for x in range(1 << 31, (1 << 31) + 3):
|
||||
print(x)
|
||||
for x in range(1 << 62, (1 << 62) + 3):
|
||||
print(x)
|
||||
|
||||
# GH-107392
|
||||
var n = 1.0
|
||||
for x in range(n):
|
||||
print(x)
|
@ -5,3 +5,4 @@ GDTEST_OK
|
||||
4611686018427387904
|
||||
4611686018427387905
|
||||
4611686018427387906
|
||||
0
|
@ -7051,7 +7051,7 @@ Ref<GLTFObjectModelProperty> GLTFDocument::export_object_model_property(Ref<GLTF
|
||||
Ref<GLTFObjectModelProperty> ret;
|
||||
const Object *target_object = p_godot_node;
|
||||
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;
|
||||
for (StringName subname : subpath) {
|
||||
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;
|
||||
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();
|
||||
transform_node_path = path + ":" + bone;
|
||||
} else {
|
||||
|
@ -51,7 +51,7 @@ void _add_nodes_suggestions(const Node *p_base, const Node *p_node, PackedString
|
||||
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));
|
||||
|
||||
|
@ -180,7 +180,7 @@ namespace Godot
|
||||
/// static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
|
||||
/// {
|
||||
/// 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)(
|
||||
/// VariantConversionCallbacks.GetToManagedCallback<int>()(args[0])
|
||||
|
@ -19,7 +19,7 @@ public readonly partial struct Callable
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"Invalid argument count for invoking callable." +
|
||||
$" Expected {countExpected} arguments, received {countReceived}.",
|
||||
$" Expected {countExpected} argument(s), received {countReceived}.",
|
||||
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_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:
|
||||
return $"Invalid call. Nonexistent {where}.";
|
||||
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;
|
||||
}
|
||||
|
||||
String path = root->get_path_to(node);
|
||||
String path = String(root->get_path_to(node));
|
||||
path += ":" + String(d["property"]);
|
||||
|
||||
_add_sync_property(path);
|
||||
@ -390,7 +390,7 @@ void ReplicationEditor::_add_pressed() {
|
||||
return;
|
||||
}
|
||||
|
||||
_add_sync_property(path);
|
||||
_add_sync_property(String(path));
|
||||
}
|
||||
|
||||
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[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) {
|
||||
|
@ -116,7 +116,7 @@ void SceneCacheInterface::process_simplify_path(int p_from, const uint8_t *p_pac
|
||||
ERR_FAIL_NULL(node);
|
||||
const bool valid_rpc_checksum = multiplayer->get_rpc_md5(node) == methods_md5;
|
||||
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));
|
||||
@ -154,7 +154,7 @@ void SceneCacheInterface::process_confirm_path(int p_from, const uint8_t *p_pack
|
||||
if (valid_rpc_checksum == false) {
|
||||
const Node *node = ObjectDB::get_instance<Node>(*oid);
|
||||
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);
|
||||
|
@ -245,7 +245,7 @@ void SceneRPCInterface::_process_rpc(Node *p_node, const uint16_t p_rpc_method_i
|
||||
} 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;
|
||||
|
||||
|
@ -457,6 +457,19 @@ uint32_t GodotNavigationServer2D::region_get_iteration_id(RID p_region) const {
|
||||
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) {
|
||||
NavRegion2D *region = region_owner.get_or_null(p_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);
|
||||
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 {
|
||||
|
@ -146,6 +146,9 @@ public:
|
||||
virtual RID region_create() 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);
|
||||
virtual bool region_get_enabled(RID p_region) const override;
|
||||
|
||||
|
@ -32,10 +32,13 @@
|
||||
|
||||
#include "../nav_utils_2d.h"
|
||||
|
||||
#include "core/object/ref_counted.h"
|
||||
#include "servers/navigation/navigation_utilities.h"
|
||||
|
||||
struct NavBaseIteration2D {
|
||||
uint32_t id = UINT32_MAX;
|
||||
class NavBaseIteration2D : public RefCounted {
|
||||
GDCLASS(NavBaseIteration2D, RefCounted);
|
||||
|
||||
public:
|
||||
bool enabled = true;
|
||||
uint32_t navigation_layers = 1;
|
||||
real_t enter_cost = 0.0;
|
||||
@ -45,6 +48,7 @@ struct NavBaseIteration2D {
|
||||
RID owner_rid;
|
||||
bool owner_use_edge_connections = false;
|
||||
LocalVector<Nav2D::Polygon> navmesh_polygons;
|
||||
LocalVector<LocalVector<Nav2D::Connection>> internal_connections;
|
||||
|
||||
bool get_enabled() const { return enabled; }
|
||||
NavigationUtilities::PathSegmentType get_type() const { return owner_type; }
|
||||
@ -55,4 +59,10 @@ struct NavBaseIteration2D {
|
||||
real_t get_travel_cost() const { return travel_cost; }
|
||||
bool get_use_edge_connections() const { return owner_use_edge_connections; }
|
||||
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;
|
||||
NavMapIteration2D *map_iteration = r_build.map_iteration;
|
||||
|
||||
LocalVector<NavRegionIteration2D> ®ions = map_iteration->region_iterations;
|
||||
HashMap<uint32_t, LocalVector<Edge::Connection>> ®ion_external_connections = map_iteration->external_region_connections;
|
||||
const LocalVector<Ref<NavRegionIteration2D>> ®ions = map_iteration->region_iterations;
|
||||
HashMap<const NavBaseIteration2D *, LocalVector<Connection>> ®ion_external_connections = map_iteration->external_region_connections;
|
||||
|
||||
map_iteration->navbases_polygons_external_connections.clear();
|
||||
|
||||
// Remove regions connections.
|
||||
region_external_connections.clear();
|
||||
for (const NavRegionIteration2D ®ion : regions) {
|
||||
region_external_connections[region.id] = LocalVector<Edge::Connection>();
|
||||
}
|
||||
|
||||
// Copy all region polygons in the map.
|
||||
int polygon_count = 0;
|
||||
for (NavRegionIteration2D ®ion : regions) {
|
||||
if (!region.get_enabled()) {
|
||||
continue;
|
||||
}
|
||||
LocalVector<Polygon> &polygons_source = region.navmesh_polygons;
|
||||
for (uint32_t n = 0; n < polygons_source.size(); n++) {
|
||||
polygons_source[n].id = polygon_count;
|
||||
polygon_count++;
|
||||
}
|
||||
for (const Ref<NavRegionIteration2D> ®ion : regions) {
|
||||
const uint32_t polygons_size = region->navmesh_polygons.size();
|
||||
polygon_count += polygons_size;
|
||||
|
||||
region_external_connections[region.ptr()] = LocalVector<Connection>();
|
||||
map_iteration->navbases_polygons_external_connections[region.ptr()] = LocalVector<LocalVector<Connection>>();
|
||||
map_iteration->navbases_polygons_external_connections[region.ptr()].resize(polygons_size);
|
||||
}
|
||||
|
||||
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;
|
||||
NavMapIteration2D *map_iteration = r_build.map_iteration;
|
||||
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;
|
||||
|
||||
// Group all edges per key.
|
||||
@ -114,15 +111,9 @@ void NavMapBuilder2D::_build_step_find_edge_connection_pairs(NavMapIterationBuil
|
||||
connection_pairs_map.reserve(polygon_count);
|
||||
int free_edges_count = 0; // How many ConnectionPairs have only one Connection.
|
||||
|
||||
for (NavRegionIteration2D ®ion : map_iteration->region_iterations) {
|
||||
if (!region.get_enabled()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Polygon &poly : region.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_point_key(poly.vertices[p], merge_rasterizer_cell_size), get_point_key(poly.vertices[next_point], merge_rasterizer_cell_size));
|
||||
for (const Ref<NavRegionIteration2D> ®ion : map_iteration->region_iterations) {
|
||||
for (const ConnectableEdge &connectable_edge : region->get_external_edges()) {
|
||||
const EdgeKey &ek = connectable_edge.ek;
|
||||
|
||||
HashMap<EdgeKey, EdgeConnectionPair, EdgeKey>::Iterator pair_it = connection_pairs_map.find(ek);
|
||||
if (!pair_it) {
|
||||
@ -133,11 +124,10 @@ void NavMapBuilder2D::_build_step_find_edge_connection_pairs(NavMapIterationBuil
|
||||
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];
|
||||
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;
|
||||
|
||||
pair.connections[pair.size] = new_connection;
|
||||
++pair.size;
|
||||
@ -151,7 +141,6 @@ void NavMapBuilder2D::_build_step_find_edge_connection_pairs(NavMapIterationBuil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r_build.free_edge_count = free_edges_count;
|
||||
}
|
||||
@ -160,23 +149,28 @@ void NavMapBuilder2D::_build_step_merge_edge_connection_pairs(NavMapIterationBui
|
||||
PerformanceData &performance_data = r_build.performance_data;
|
||||
|
||||
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;
|
||||
bool use_edge_connections = r_build.use_edge_connections;
|
||||
|
||||
free_edges.clear();
|
||||
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) {
|
||||
const EdgeConnectionPair &pair = pair_it.value;
|
||||
if (pair.size == 2) {
|
||||
// Connect edge that are shared in different polygons.
|
||||
const Edge::Connection &c1 = pair.connections[0];
|
||||
const Edge::Connection &c2 = pair.connections[1];
|
||||
c1.polygon->edges[c1.edge].connections.push_back(c2);
|
||||
c2.polygon->edges[c2.edge].connections.push_back(c1);
|
||||
// Note: The pathway_start/end are full for those connection and do not need to be modified.
|
||||
performance_data.pm_edge_merge_count += 1;
|
||||
const Connection &c1 = pair.connections[0];
|
||||
const Connection &c2 = pair.connections[1];
|
||||
|
||||
navbases_polygons_external_connections[c1.polygon->owner][c1.polygon->id].push_back(c2);
|
||||
navbases_polygons_external_connections[c2.polygon->owner][c2.polygon->id].push_back(c1);
|
||||
performance_data.pm_edge_connection_count += 1;
|
||||
|
||||
} else {
|
||||
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()) {
|
||||
@ -191,8 +185,11 @@ void NavMapBuilder2D::_build_step_edge_connection_margin_connections(NavMapItera
|
||||
NavMapIteration2D *map_iteration = r_build.map_iteration;
|
||||
|
||||
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.
|
||||
//
|
||||
@ -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;
|
||||
|
||||
for (uint32_t i = 0; i < free_edges.size(); i++) {
|
||||
const Edge::Connection &free_edge = free_edges[i];
|
||||
Vector2 edge_p1 = free_edge.polygon->vertices[free_edge.edge];
|
||||
Vector2 edge_p2 = free_edge.polygon->vertices[(free_edge.edge + 1) % free_edge.polygon->vertices.size()];
|
||||
const Connection &free_edge = free_edges[i];
|
||||
const Vector2 &edge_p1 = free_edge.pathway_start;
|
||||
const Vector2 &edge_p2 = free_edge.pathway_end;
|
||||
|
||||
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) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Vector2 other_edge_p1 = other_edge.polygon->vertices[other_edge.edge];
|
||||
Vector2 other_edge_p2 = other_edge.polygon->vertices[(other_edge.edge + 1) % other_edge.polygon->vertices.size()];
|
||||
const Vector2 &other_edge_p1 = other_edge.pathway_start;
|
||||
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;
|
||||
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_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());
|
||||
if ((projected_p1_ratio < 0.0 && projected_p2_ratio < 0.0) || (projected_p1_ratio > 1.0 && projected_p2_ratio > 1.0)) {
|
||||
continue;
|
||||
}
|
||||
@ -251,13 +248,14 @@ void NavMapBuilder2D::_build_step_edge_connection_margin_connections(NavMapItera
|
||||
}
|
||||
|
||||
// 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_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.
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -268,18 +266,28 @@ void NavMapBuilder2D::_build_step_navlink_connections(NavMapIterationBuild2D &r_
|
||||
|
||||
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;
|
||||
|
||||
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.
|
||||
for (NavLinkIteration2D &link : links) {
|
||||
if (!link.get_enabled()) {
|
||||
continue;
|
||||
}
|
||||
const Vector2 link_start_pos = link.get_start_position();
|
||||
const Vector2 link_end_pos = link.get_end_position();
|
||||
for (const Ref<NavLinkIteration2D> &link : links) {
|
||||
polygon_count++;
|
||||
Polygon &new_polygon = navlink_polygons[navlink_index++];
|
||||
|
||||
new_polygon.id = 0;
|
||||
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;
|
||||
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;
|
||||
Vector2 closest_end_point;
|
||||
|
||||
for (NavRegionIteration2D ®ion : map_iteration->region_iterations) {
|
||||
if (!region.get_enabled()) {
|
||||
continue;
|
||||
}
|
||||
Rect2 region_bounds = region.get_bounds().grow(link_connection_radius);
|
||||
for (const Ref<NavRegionIteration2D> ®ion : map_iteration->region_iterations) {
|
||||
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)) {
|
||||
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) {
|
||||
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 (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);
|
||||
|
||||
// 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.
|
||||
{
|
||||
Edge::Connection entry_connection;
|
||||
Connection entry_connection;
|
||||
entry_connection.polygon = &new_polygon;
|
||||
entry_connection.edge = -1;
|
||||
entry_connection.pathway_start = new_polygon.vertices[0];
|
||||
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.edge = -1;
|
||||
exit_connection.pathway_start = new_polygon.vertices[2];
|
||||
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 (link.is_bidirectional()) {
|
||||
Edge::Connection entry_connection;
|
||||
if (link->is_bidirectional()) {
|
||||
Connection entry_connection;
|
||||
entry_connection.polygon = &new_polygon;
|
||||
entry_connection.edge = -1;
|
||||
entry_connection.pathway_start = new_polygon.vertices[2];
|
||||
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.edge = -1;
|
||||
exit_connection.pathway_start = new_polygon.vertices[0];
|
||||
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;
|
||||
|
||||
uint32_t navmesh_polygon_count = r_build.polygon_count;
|
||||
uint32_t total_polygon_count = navmesh_polygon_count;
|
||||
|
||||
map_iteration->path_query_slots_mutex.lock();
|
||||
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.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.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();
|
||||
}
|
||||
|
@ -37,9 +37,9 @@
|
||||
#include "core/math/math_defs.h"
|
||||
#include "core/os/semaphore.h"
|
||||
|
||||
struct NavLinkIteration2D;
|
||||
class NavLinkIteration2D;
|
||||
class NavRegion2D;
|
||||
struct NavRegionIteration2D;
|
||||
class NavRegionIteration2D;
|
||||
struct NavMapIteration2D;
|
||||
|
||||
struct NavMapIterationBuild2D {
|
||||
@ -52,7 +52,7 @@ struct NavMapIterationBuild2D {
|
||||
int free_edge_count = 0;
|
||||
|
||||
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;
|
||||
|
||||
@ -74,19 +74,33 @@ struct NavMapIteration2D {
|
||||
mutable SafeNumeric<uint32_t> users;
|
||||
RWLock rwlock;
|
||||
|
||||
LocalVector<NavRegionIteration2D> region_iterations;
|
||||
LocalVector<NavLinkIteration2D> link_iterations;
|
||||
LocalVector<Ref<NavRegionIteration2D>> region_iterations;
|
||||
LocalVector<Ref<NavLinkIteration2D>> link_iterations;
|
||||
|
||||
int navmesh_polygon_count = 0;
|
||||
|
||||
// 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;
|
||||
Mutex path_query_slots_mutex;
|
||||
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 {
|
||||
|
@ -234,22 +234,15 @@ void NavMeshQueries2D::_query_task_find_start_end_positions(NavMeshPathQueryTask
|
||||
real_t begin_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) {
|
||||
if (!region.get_enabled()) {
|
||||
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())) {
|
||||
for (const Ref<NavRegionIteration2D> ®ion : regions) {
|
||||
if (!_query_task_is_connection_owner_usable(p_query_task, region.ptr())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 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.
|
||||
if ((p_query_task.navigation_layers & p.owner->get_navigation_layers()) == 0) {
|
||||
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 Polygon *begin_poly = p_query_task.begin_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.
|
||||
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.entry = 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.traveled_distance = 0.0;
|
||||
begin_navigation_poly.traveled_distance = 0.f;
|
||||
|
||||
// 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;
|
||||
|
||||
const Polygon *reachable_end = nullptr;
|
||||
@ -313,47 +346,27 @@ void NavMeshQueries2D::_query_task_build_path_corridor(NavMeshPathQueryTask2D &p
|
||||
bool is_reachable = true;
|
||||
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) {
|
||||
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.
|
||||
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 *least_cost_navbase = least_cost_poly.poly->owner;
|
||||
|
||||
const NavBaseIteration2D *connection_owner = connection.polygon->owner;
|
||||
const bool owner_is_usable = _query_task_is_connection_owner_usable(p_query_task, connection_owner);
|
||||
if (!owner_is_usable) {
|
||||
continue;
|
||||
}
|
||||
const uint32_t navbase_local_polygon_id = least_cost_poly.poly->id;
|
||||
const LocalVector<LocalVector<Connection>> &navbase_polygons_to_connections = least_cost_poly.poly->owner->get_internal_connections();
|
||||
|
||||
const Vector2 new_entry = Geometry2D::get_closest_point_to_segment(least_cost_poly.entry, connection.pathway_start, connection.pathway_end);
|
||||
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;
|
||||
if (navbase_polygons_to_connections.size() > 0) {
|
||||
const LocalVector<Connection> &polygon_connections = navbase_polygons_to_connections[navbase_local_polygon_id];
|
||||
|
||||
// Check if the neighbor polygon has already been processed.
|
||||
NavigationPoly &neighbor_poly = navigation_polys[connection.polygon->id];
|
||||
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);
|
||||
}
|
||||
for (const Connection &connection : polygon_connections) {
|
||||
_query_task_search_polygon_connections(p_query_task, connection, least_cost_id, least_cost_poly, poly_enter_cost, end_point);
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
@ -371,6 +384,7 @@ void NavMeshQueries2D::_query_task_build_path_corridor(NavMeshPathQueryTask2D &p
|
||||
// Set as end point the furthest reachable point.
|
||||
end_poly = reachable_end;
|
||||
real_t end_d = FLT_MAX;
|
||||
|
||||
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]);
|
||||
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.
|
||||
bool closest_point_on_start_poly = false;
|
||||
|
||||
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]);
|
||||
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.traveled_distance = FLT_MAX;
|
||||
}
|
||||
navigation_polys[begin_poly->id].poly = begin_poly;
|
||||
navigation_polys[begin_poly->id].traveled_distance = 0;
|
||||
least_cost_id = begin_poly->id;
|
||||
uint32_t _bp_id = p_query_task.path_query_slot->poly_to_id[begin_poly];
|
||||
navigation_polys[_bp_id].poly = begin_poly;
|
||||
navigation_polys[_bp_id].traveled_distance = 0;
|
||||
least_cost_id = _bp_id;
|
||||
reachable_end = nullptr;
|
||||
} else {
|
||||
// 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.
|
||||
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()) {
|
||||
ERR_FAIL_NULL(least_cost_poly.poly->owner);
|
||||
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) {
|
||||
real_t end_d = FLT_MAX;
|
||||
// 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++) {
|
||||
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);
|
||||
@ -484,7 +502,7 @@ void NavMeshQueries2D::query_task_map_iteration_get_path(NavMeshPathQueryTask2D
|
||||
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) {
|
||||
return;
|
||||
@ -733,9 +751,9 @@ ClosestPointQueryResult NavMeshQueries2D::map_iteration_get_closest_point_info(c
|
||||
|
||||
// TODO: Check for further 2D improvements.
|
||||
|
||||
const LocalVector<NavRegionIteration2D> ®ions = p_map_iteration.region_iterations;
|
||||
for (const NavRegionIteration2D ®ion : regions) {
|
||||
for (const Polygon &polygon : region.get_navmesh_polygons()) {
|
||||
const LocalVector<Ref<NavRegionIteration2D>> ®ions = p_map_iteration.region_iterations;
|
||||
for (const Ref<NavRegionIteration2D> ®ion : regions) {
|
||||
for (const Polygon &polygon : region->get_navmesh_polygons()) {
|
||||
real_t cross = (polygon.vertices[1] - polygon.vertices[0]).cross(polygon.vertices[2] - polygon.vertices[0]);
|
||||
Vector2 closest_on_polygon;
|
||||
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());
|
||||
|
||||
for (uint32_t i = 0; i < p_map_iteration.region_iterations.size(); i++) {
|
||||
const NavRegionIteration2D ®ion = p_map_iteration.region_iterations[i];
|
||||
if (!region.enabled || (p_navigation_layers & region.navigation_layers) == 0) {
|
||||
const Ref<NavRegionIteration2D> ®ion = p_map_iteration.region_iterations[i];
|
||||
if (!region->get_enabled() || (p_navigation_layers & region->get_navigation_layers()) == 0) {
|
||||
continue;
|
||||
}
|
||||
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;
|
||||
|
||||
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) {
|
||||
continue;
|
||||
@ -843,16 +861,16 @@ Vector2 NavMeshQueries2D::map_iteration_get_random_point(const NavMapIteration2D
|
||||
uint32_t random_region_index = E->value;
|
||||
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 {
|
||||
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) {
|
||||
ERR_FAIL_NULL_V(p_owner, false);
|
||||
|
||||
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) {
|
||||
// Not usable. No matching bit between task filter bitmask and owner bitmask.
|
||||
owner_usable = false;
|
||||
|
@ -32,6 +32,8 @@
|
||||
|
||||
#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_result_2d.h"
|
||||
#include "servers/navigation/navigation_utilities.h"
|
||||
@ -48,6 +50,7 @@ public:
|
||||
Heap<Nav2D::NavigationPoly *, Nav2D::NavPolyTravelCostGreaterThan, Nav2D::NavPolyHeapIndexer> traversable_polys;
|
||||
bool in_use = false;
|
||||
uint32_t slot_index = 0;
|
||||
AHashMap<const Nav2D::Polygon *, uint32_t> poly_to_id;
|
||||
};
|
||||
|
||||
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_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_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_edgecentered(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 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 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_base_iteration_2d.h"
|
||||
#include "scene/resources/2d/navigation_polygon.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;
|
||||
real_t surface_area = 0.0;
|
||||
Rect2 bounds;
|
||||
LocalVector<Nav2D::ConnectableEdge> external_edges;
|
||||
|
||||
const Transform2D &get_transform() const { return transform; }
|
||||
real_t get_surface_area() const { return surface_area; }
|
||||
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;
|
||||
link_dirty = true;
|
||||
iteration_dirty = true;
|
||||
|
||||
if (map) {
|
||||
map->add_link(this);
|
||||
@ -57,9 +57,7 @@ void NavLink2D::set_enabled(bool p_enabled) {
|
||||
return;
|
||||
}
|
||||
enabled = p_enabled;
|
||||
|
||||
// TODO: This should not require a full rebuild as the link has not really changed.
|
||||
link_dirty = true;
|
||||
iteration_dirty = true;
|
||||
|
||||
request_sync();
|
||||
}
|
||||
@ -69,27 +67,27 @@ void NavLink2D::set_bidirectional(bool p_bidirectional) {
|
||||
return;
|
||||
}
|
||||
bidirectional = p_bidirectional;
|
||||
link_dirty = true;
|
||||
iteration_dirty = true;
|
||||
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
start_position = p_position;
|
||||
link_dirty = true;
|
||||
iteration_dirty = true;
|
||||
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
end_position = p_position;
|
||||
link_dirty = true;
|
||||
iteration_dirty = true;
|
||||
|
||||
request_sync();
|
||||
}
|
||||
@ -99,7 +97,7 @@ void NavLink2D::set_navigation_layers(uint32_t p_navigation_layers) {
|
||||
return;
|
||||
}
|
||||
navigation_layers = p_navigation_layers;
|
||||
link_dirty = true;
|
||||
iteration_dirty = true;
|
||||
|
||||
request_sync();
|
||||
}
|
||||
@ -110,7 +108,7 @@ void NavLink2D::set_enter_cost(real_t p_enter_cost) {
|
||||
return;
|
||||
}
|
||||
enter_cost = new_enter_cost;
|
||||
link_dirty = true;
|
||||
iteration_dirty = true;
|
||||
|
||||
request_sync();
|
||||
}
|
||||
@ -121,7 +119,7 @@ void NavLink2D::set_travel_cost(real_t p_travel_cost) {
|
||||
return;
|
||||
}
|
||||
travel_cost = new_travel_cost;
|
||||
link_dirty = true;
|
||||
iteration_dirty = true;
|
||||
|
||||
request_sync();
|
||||
}
|
||||
@ -131,21 +129,59 @@ void NavLink2D::set_owner_id(ObjectID p_owner_id) {
|
||||
return;
|
||||
}
|
||||
owner_id = p_owner_id;
|
||||
link_dirty = true;
|
||||
iteration_dirty = true;
|
||||
|
||||
request_sync();
|
||||
}
|
||||
|
||||
bool NavLink2D::is_dirty() const {
|
||||
return link_dirty;
|
||||
bool NavLink2D::sync() {
|
||||
bool requires_map_update = false;
|
||||
if (!map) {
|
||||
return requires_map_update;
|
||||
}
|
||||
|
||||
void NavLink2D::sync() {
|
||||
if (link_dirty) {
|
||||
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;
|
||||
}
|
||||
|
||||
link_dirty = false;
|
||||
iteration_building = false;
|
||||
iteration_ready = true;
|
||||
}
|
||||
|
||||
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() :
|
||||
sync_dirty_request_list_element(this) {
|
||||
type = NavigationUtilities::PathSegmentType::PATH_SEGMENT_TYPE_LINK;
|
||||
iteration.instantiate();
|
||||
}
|
||||
|
||||
NavLink2D::~NavLink2D() {
|
||||
cancel_sync_request();
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
iteration = Ref<NavLinkIteration2D>();
|
||||
}
|
||||
|
@ -34,7 +34,10 @@
|
||||
#include "nav_base_2d.h"
|
||||
#include "nav_utils_2d.h"
|
||||
|
||||
struct NavLinkIteration2D : NavBaseIteration2D {
|
||||
class NavLinkIteration2D : public NavBaseIteration2D {
|
||||
GDCLASS(NavLinkIteration2D, NavBaseIteration2D);
|
||||
|
||||
public:
|
||||
bool bidirectional = true;
|
||||
Vector2 start_position;
|
||||
Vector2 end_position;
|
||||
@ -42,6 +45,11 @@ struct NavLinkIteration2D : NavBaseIteration2D {
|
||||
Vector2 get_start_position() const { return start_position; }
|
||||
Vector2 get_end_position() const { return end_position; }
|
||||
bool is_bidirectional() const { return bidirectional; }
|
||||
|
||||
virtual ~NavLinkIteration2D() override {
|
||||
navmesh_polygons.clear();
|
||||
internal_connections.clear();
|
||||
}
|
||||
};
|
||||
|
||||
#include "core/templates/self_list.h"
|
||||
@ -53,12 +61,20 @@ class NavLink2D : public NavBase2D {
|
||||
Vector2 end_position;
|
||||
bool enabled = true;
|
||||
|
||||
bool link_dirty = true;
|
||||
|
||||
SelfList<NavLink2D> sync_dirty_request_list_element;
|
||||
|
||||
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:
|
||||
NavLink2D();
|
||||
~NavLink2D();
|
||||
@ -78,12 +94,12 @@ public:
|
||||
return bidirectional;
|
||||
}
|
||||
|
||||
void set_start_position(const Vector2 &p_position);
|
||||
void set_start_position(Vector2 p_position);
|
||||
Vector2 get_start_position() const {
|
||||
return start_position;
|
||||
}
|
||||
|
||||
void set_end_position(const Vector2 &p_position);
|
||||
void set_end_position(Vector2 p_position);
|
||||
Vector2 get_end_position() const {
|
||||
return end_position;
|
||||
}
|
||||
@ -94,10 +110,9 @@ public:
|
||||
virtual void set_travel_cost(real_t p_travel_cost) override;
|
||||
virtual void set_owner_id(ObjectID p_owner_id) override;
|
||||
|
||||
bool is_dirty() const;
|
||||
void sync();
|
||||
bool sync();
|
||||
void request_sync();
|
||||
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;
|
||||
}
|
||||
|
||||
Vector2 NavMap2D::get_merge_rasterizer_cell_size() const {
|
||||
const Vector2 &NavMap2D::get_merge_rasterizer_cell_size() const {
|
||||
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) {
|
||||
DEV_ASSERT(!regions.has(p_region));
|
||||
|
||||
regions.push_back(p_region);
|
||||
iteration_dirty = true;
|
||||
}
|
||||
@ -199,6 +201,8 @@ void NavMap2D::remove_region(NavRegion2D *p_region) {
|
||||
}
|
||||
|
||||
void NavMap2D::add_link(NavLink2D *p_link) {
|
||||
DEV_ASSERT(!links.has(p_link));
|
||||
|
||||
links.push_back(p_link);
|
||||
iteration_dirty = true;
|
||||
}
|
||||
@ -311,49 +315,22 @@ void NavMap2D::_build_iteration() {
|
||||
iteration_build.edge_connection_margin = get_edge_connection_margin();
|
||||
iteration_build.link_connection_radius = get_link_connection_radius();
|
||||
|
||||
uint32_t enabled_region_count = 0;
|
||||
uint32_t enabled_link_count = 0;
|
||||
next_map_iteration.clear();
|
||||
|
||||
for (NavRegion2D *region : regions) {
|
||||
if (!region->get_enabled()) {
|
||||
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);
|
||||
next_map_iteration.region_iterations.resize(regions.size());
|
||||
next_map_iteration.link_iterations.resize(links.size());
|
||||
|
||||
uint32_t region_id_count = 0;
|
||||
uint32_t link_id_count = 0;
|
||||
|
||||
for (NavRegion2D *region : regions) {
|
||||
if (!region->get_enabled()) {
|
||||
continue;
|
||||
}
|
||||
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;
|
||||
const Ref<NavRegionIteration2D> region_iteration = region->get_iteration();
|
||||
next_map_iteration.region_iterations[region_id_count++] = region_iteration;
|
||||
next_map_iteration.region_ptr_to_region_iteration[region] = region_iteration;
|
||||
}
|
||||
for (NavLink2D *link : links) {
|
||||
if (!link->get_enabled()) {
|
||||
continue;
|
||||
}
|
||||
NavLinkIteration2D &link_iteration = next_map_iteration.link_iterations[link_id_count];
|
||||
link_iteration.id = link_id_count++;
|
||||
link->get_iteration_update(link_iteration);
|
||||
const Ref<NavLinkIteration2D> link_iteration = link->get_iteration();
|
||||
next_map_iteration.link_iterations[link_id_count++] = link_iteration;
|
||||
}
|
||||
|
||||
iteration_build.map_iteration = &next_map_iteration;
|
||||
@ -379,9 +356,6 @@ void NavMap2D::_sync_iteration() {
|
||||
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_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_obstacle_count = obstacles.size();
|
||||
|
||||
_sync_async_tasks();
|
||||
|
||||
_sync_dirty_map_update_requests();
|
||||
|
||||
if (iteration_dirty && !iteration_building && !iteration_ready) {
|
||||
@ -426,6 +402,16 @@ void NavMap2D::sync() {
|
||||
map_settings_dirty = false;
|
||||
|
||||
_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() {
|
||||
@ -569,9 +555,9 @@ int NavMap2D::get_region_connections_count(NavRegion2D *p_region) 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) {
|
||||
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) {
|
||||
return found_connections->value.size();
|
||||
}
|
||||
@ -585,9 +571,9 @@ Vector2 NavMap2D::get_region_connection_pathway_start(NavRegion2D *p_region, int
|
||||
|
||||
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) {
|
||||
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) {
|
||||
ERR_FAIL_INDEX_V(p_connection_id, int(found_connections->value.size()), Vector2());
|
||||
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();
|
||||
|
||||
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) {
|
||||
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) {
|
||||
ERR_FAIL_INDEX_V(p_connection_id, int(found_connections->value.size()), Vector2());
|
||||
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()) {
|
||||
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) {
|
||||
if (p_sync_request->in_list()) {
|
||||
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) {
|
||||
if (p_sync_request->in_list()) {
|
||||
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) {
|
||||
if (p_sync_request->in_list()) {
|
||||
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) {
|
||||
if (!p_sync_request->in_list()) {
|
||||
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) {
|
||||
if (!p_sync_request->in_list()) {
|
||||
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) {
|
||||
if (!p_sync_request->in_list()) {
|
||||
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) {
|
||||
if (!p_sync_request->in_list()) {
|
||||
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() {
|
||||
@ -679,41 +669,69 @@ void NavMap2D::_sync_dirty_map_update_requests() {
|
||||
iteration_dirty = true;
|
||||
}
|
||||
|
||||
if (!iteration_dirty) {
|
||||
iteration_dirty = sync_dirty_requests.regions.first() || sync_dirty_requests.links.first();
|
||||
}
|
||||
|
||||
// Sync NavRegions.
|
||||
for (SelfList<NavRegion2D> *element = sync_dirty_requests.regions.first(); element; element = element->next()) {
|
||||
element->self()->sync();
|
||||
RWLockWrite write_lock_regions(sync_dirty_requests.regions.rwlock);
|
||||
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.
|
||||
for (SelfList<NavLink2D> *element = sync_dirty_requests.links.first(); element; element = element->next()) {
|
||||
element->self()->sync();
|
||||
RWLockWrite write_lock_links(sync_dirty_requests.links.rwlock);
|
||||
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() {
|
||||
// Sync NavAgents.
|
||||
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();
|
||||
}
|
||||
sync_dirty_requests.agents.clear();
|
||||
sync_dirty_requests.agents.list.clear();
|
||||
|
||||
// Sync NavObstacles.
|
||||
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();
|
||||
}
|
||||
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) {
|
||||
@ -768,4 +786,9 @@ NavMap2D::~NavMap2D() {
|
||||
WorkerThreadPool::get_singleton()->wait_for_task_completion(iteration_build_thread_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;
|
||||
|
||||
struct {
|
||||
SelfList<NavRegion2D>::List regions;
|
||||
SelfList<NavLink2D>::List links;
|
||||
SelfList<NavAgent2D>::List agents;
|
||||
SelfList<NavObstacle2D>::List obstacles;
|
||||
struct {
|
||||
RWLock rwlock;
|
||||
SelfList<NavRegion2D>::List list;
|
||||
} 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;
|
||||
|
||||
struct {
|
||||
struct {
|
||||
RWLock rwlock;
|
||||
SelfList<NavRegion2D>::List list;
|
||||
} regions;
|
||||
} async_dirty_requests;
|
||||
|
||||
int path_query_slots_max = 4;
|
||||
|
||||
bool use_async_iterations = true;
|
||||
@ -117,7 +136,6 @@ class NavMap2D : public NavRid2D {
|
||||
mutable RWLock iteration_slot_rwlock;
|
||||
|
||||
NavMapIterationBuild2D iteration_build;
|
||||
bool iteration_build_use_threads = false;
|
||||
WorkerThreadPool::TaskID iteration_build_thread_task_id = WorkerThreadPool::INVALID_TASK_ID;
|
||||
static void _build_iteration_threaded(void *p_arg);
|
||||
|
||||
@ -160,7 +178,7 @@ public:
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@ -218,6 +236,9 @@ public:
|
||||
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;
|
||||
|
||||
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_link_sync_dirty_request(SelfList<NavLink2D> *p_sync_request);
|
||||
void add_agent_sync_dirty_request(SelfList<NavAgent2D> *p_sync_request);
|
||||
@ -234,6 +255,7 @@ public:
|
||||
private:
|
||||
void _sync_dirty_map_update_requests();
|
||||
void _sync_dirty_avoidance_update_requests();
|
||||
void _sync_async_tasks();
|
||||
|
||||
void compute_single_step(uint32_t p_index, NavAgent2D **p_agent);
|
||||
|
||||
|
@ -31,11 +31,11 @@
|
||||
#include "nav_region_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_region_builder_2d.h"
|
||||
#include "2d/nav_region_iteration_2d.h"
|
||||
#include "core/config/project_settings.h"
|
||||
|
||||
using namespace Nav2D;
|
||||
|
||||
@ -44,6 +44,7 @@ void NavRegion2D::set_map(NavMap2D *p_map) {
|
||||
return;
|
||||
}
|
||||
|
||||
cancel_async_thread_join();
|
||||
cancel_sync_request();
|
||||
|
||||
if (map) {
|
||||
@ -51,11 +52,14 @@ void NavRegion2D::set_map(NavMap2D *p_map) {
|
||||
}
|
||||
|
||||
map = p_map;
|
||||
polygons_dirty = true;
|
||||
iteration_dirty = true;
|
||||
|
||||
if (map) {
|
||||
map->add_region(this);
|
||||
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;
|
||||
}
|
||||
enabled = p_enabled;
|
||||
|
||||
// TODO: This should not require a full rebuild as the region has not really changed.
|
||||
polygons_dirty = true;
|
||||
iteration_dirty = true;
|
||||
|
||||
request_sync();
|
||||
}
|
||||
@ -74,39 +76,32 @@ void NavRegion2D::set_enabled(bool p_enabled) {
|
||||
void NavRegion2D::set_use_edge_connections(bool p_enabled) {
|
||||
if (use_edge_connections != p_enabled) {
|
||||
use_edge_connections = p_enabled;
|
||||
polygons_dirty = true;
|
||||
iteration_dirty = true;
|
||||
}
|
||||
|
||||
request_sync();
|
||||
}
|
||||
|
||||
void NavRegion2D::set_transform(const Transform2D &p_transform) {
|
||||
void NavRegion2D::set_transform(Transform2D p_transform) {
|
||||
if (transform == p_transform) {
|
||||
return;
|
||||
}
|
||||
transform = p_transform;
|
||||
polygons_dirty = true;
|
||||
iteration_dirty = true;
|
||||
|
||||
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
|
||||
if (map && p_navigation_polygon.is_valid() && !Math::is_equal_approx(double(map->get_cell_size()), double(p_navigation_polygon->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())));
|
||||
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_mesh->get_cell_size()), double(map->get_cell_size())));
|
||||
}
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
RWLockWrite write_lock(navmesh_rwlock);
|
||||
navmesh = p_navigation_mesh;
|
||||
|
||||
pending_navmesh_vertices.clear();
|
||||
pending_navmesh_polygons.clear();
|
||||
|
||||
if (p_navigation_polygon.is_valid()) {
|
||||
p_navigation_polygon->get_data(pending_navmesh_vertices, pending_navmesh_polygons);
|
||||
}
|
||||
|
||||
polygons_dirty = true;
|
||||
iteration_dirty = true;
|
||||
|
||||
request_sync();
|
||||
}
|
||||
@ -132,7 +127,7 @@ void NavRegion2D::set_navigation_layers(uint32_t p_navigation_layers) {
|
||||
return;
|
||||
}
|
||||
navigation_layers = p_navigation_layers;
|
||||
region_dirty = true;
|
||||
iteration_dirty = true;
|
||||
|
||||
request_sync();
|
||||
}
|
||||
@ -143,7 +138,7 @@ void NavRegion2D::set_enter_cost(real_t p_enter_cost) {
|
||||
return;
|
||||
}
|
||||
enter_cost = new_enter_cost;
|
||||
region_dirty = true;
|
||||
iteration_dirty = true;
|
||||
|
||||
request_sync();
|
||||
}
|
||||
@ -154,7 +149,7 @@ void NavRegion2D::set_travel_cost(real_t p_travel_cost) {
|
||||
return;
|
||||
}
|
||||
travel_cost = new_travel_cost;
|
||||
region_dirty = true;
|
||||
iteration_dirty = true;
|
||||
|
||||
request_sync();
|
||||
}
|
||||
@ -164,139 +159,150 @@ void NavRegion2D::set_owner_id(ObjectID p_owner_id) {
|
||||
return;
|
||||
}
|
||||
owner_id = p_owner_id;
|
||||
region_dirty = true;
|
||||
iteration_dirty = true;
|
||||
|
||||
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() {
|
||||
RWLockWrite write_lock(region_rwlock);
|
||||
|
||||
bool something_changed = region_dirty || polygons_dirty;
|
||||
|
||||
region_dirty = false;
|
||||
|
||||
update_polygons();
|
||||
|
||||
if (something_changed) {
|
||||
iteration_id = iteration_id % UINT32_MAX + 1;
|
||||
bool requires_map_update = false;
|
||||
if (!map) {
|
||||
return requires_map_update;
|
||||
}
|
||||
|
||||
return something_changed;
|
||||
if (iteration_dirty && !iteration_building && !iteration_ready) {
|
||||
_build_iteration();
|
||||
}
|
||||
|
||||
void NavRegion2D::update_polygons() {
|
||||
if (!polygons_dirty) {
|
||||
return;
|
||||
if (iteration_ready) {
|
||||
_sync_iteration();
|
||||
requires_map_update = true;
|
||||
}
|
||||
navmesh_polygons.clear();
|
||||
surface_area = 0.0;
|
||||
bounds = Rect2();
|
||||
polygons_dirty = false;
|
||||
|
||||
if (map == nullptr) {
|
||||
return requires_map_update;
|
||||
}
|
||||
|
||||
void NavRegion2D::sync_async_tasks() {
|
||||
if (iteration_build_thread_task_id != WorkerThreadPool::INVALID_TASK_ID) {
|
||||
if (WorkerThreadPool::get_singleton()->is_task_completed(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_building = false;
|
||||
iteration_ready = true;
|
||||
request_sync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NavRegion2D::_build_iteration() {
|
||||
if (!iteration_dirty || iteration_building || iteration_ready) {
|
||||
return;
|
||||
}
|
||||
|
||||
RWLockRead read_lock(navmesh_rwlock);
|
||||
iteration_dirty = false;
|
||||
iteration_building = true;
|
||||
iteration_ready = false;
|
||||
|
||||
if (pending_navmesh_vertices.is_empty() || pending_navmesh_polygons.is_empty()) {
|
||||
return;
|
||||
iteration_build.reset();
|
||||
|
||||
if (navmesh.is_valid()) {
|
||||
navmesh->get_data(iteration_build.navmesh_data.vertices, iteration_build.navmesh_data.polygons);
|
||||
}
|
||||
|
||||
int len = pending_navmesh_vertices.size();
|
||||
if (len == 0) {
|
||||
return;
|
||||
}
|
||||
iteration_build.map_cell_size = map->get_merge_rasterizer_cell_size();
|
||||
|
||||
const Vector2 *vertices_r = pending_navmesh_vertices.ptr();
|
||||
Ref<NavRegionIteration2D> new_iteration;
|
||||
new_iteration.instantiate();
|
||||
|
||||
navmesh_polygons.resize(pending_navmesh_polygons.size());
|
||||
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();
|
||||
|
||||
real_t _new_region_surface_area = 0.0;
|
||||
Rect2 _new_bounds;
|
||||
iteration_build.region_iteration = new_iteration;
|
||||
|
||||
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;
|
||||
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 {
|
||||
_new_bounds.expand_to(point_position);
|
||||
NavRegionBuilder2D::build_iteration(iteration_build);
|
||||
|
||||
iteration_building = false;
|
||||
iteration_ready = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!valid) {
|
||||
ERR_BREAK_MSG(!valid, "The navigation polygon set in this region is not valid!");
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
surface_area = _new_region_surface_area;
|
||||
bounds = _new_bounds;
|
||||
}
|
||||
|
||||
void NavRegion2D::get_iteration_update(NavRegionIteration2D &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.transform = get_transform();
|
||||
r_iteration.owner_use_edge_connections = get_use_edge_connections();
|
||||
r_iteration.bounds = get_bounds();
|
||||
r_iteration.surface_area = get_surface_area();
|
||||
|
||||
r_iteration.navmesh_polygons.clear();
|
||||
r_iteration.navmesh_polygons.resize(navmesh_polygons.size());
|
||||
for (uint32_t i = 0; i < navmesh_polygons.size(); i++) {
|
||||
Polygon &navmesh_polygon = navmesh_polygons[i];
|
||||
navmesh_polygon.owner = &r_iteration;
|
||||
r_iteration.navmesh_polygons[i] = navmesh_polygon;
|
||||
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() :
|
||||
sync_dirty_request_list_element(this) {
|
||||
sync_dirty_request_list_element(this), async_list_element(this) {
|
||||
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() {
|
||||
cancel_async_thread_join();
|
||||
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 "scene/resources/2d/navigation_polygon.h"
|
||||
|
||||
struct NavRegionIteration2D;
|
||||
#include "2d/nav_region_iteration_2d.h"
|
||||
|
||||
class NavRegion2D : public NavBase2D {
|
||||
RWLock region_rwlock;
|
||||
@ -47,21 +47,30 @@ class NavRegion2D : public NavBase2D {
|
||||
|
||||
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;
|
||||
|
||||
RWLock navmesh_rwlock;
|
||||
Vector<Vector2> pending_navmesh_vertices;
|
||||
Vector<Vector<int>> pending_navmesh_polygons;
|
||||
Ref<NavigationPolygon> navmesh;
|
||||
|
||||
Nav2D::PerformanceData performance_data;
|
||||
|
||||
uint32_t iteration_id = 0;
|
||||
|
||||
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:
|
||||
NavRegion2D();
|
||||
@ -69,9 +78,7 @@ public:
|
||||
|
||||
uint32_t get_iteration_id() const { return iteration_id; }
|
||||
|
||||
void scratch_polygons() {
|
||||
polygons_dirty = true;
|
||||
}
|
||||
void scratch_polygons();
|
||||
|
||||
void set_enabled(bool p_enabled);
|
||||
bool get_enabled() const { return enabled; }
|
||||
@ -84,22 +91,21 @@ public:
|
||||
virtual void set_use_edge_connections(bool p_enabled) override;
|
||||
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 {
|
||||
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 {
|
||||
return navmesh_polygons;
|
||||
}
|
||||
LocalVector<Nav2D::Polygon> const &get_polygons() 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;
|
||||
|
||||
real_t get_surface_area() const { return surface_area; }
|
||||
Rect2 get_bounds() const { return bounds; }
|
||||
real_t get_surface_area() const;
|
||||
Rect2 get_bounds() const;
|
||||
|
||||
// NavBase properties.
|
||||
virtual void set_navigation_layers(uint32_t p_navigation_layers) override;
|
||||
@ -111,8 +117,17 @@ public:
|
||||
void request_sync();
|
||||
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:
|
||||
void update_polygons();
|
||||
Ref<NavRegionIteration2D> get_iteration();
|
||||
|
||||
// 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
|
||||
|
||||
#include "core/math/vector3.h"
|
||||
#include "core/object/ref_counted.h"
|
||||
#include "core/templates/hash_map.h"
|
||||
#include "core/templates/hashfuncs.h"
|
||||
#include "servers/navigation/nav_heap.h"
|
||||
#include "servers/navigation/navigation_utilities.h"
|
||||
|
||||
struct NavBaseIteration2D;
|
||||
class NavBaseIteration2D;
|
||||
|
||||
namespace Nav2D {
|
||||
struct Polygon;
|
||||
@ -71,8 +72,13 @@ struct EdgeKey {
|
||||
}
|
||||
};
|
||||
|
||||
struct Edge {
|
||||
/// The gateway in the edge, as, in some case, the whole edge might not be navigable.
|
||||
struct ConnectableEdge {
|
||||
EdgeKey ek;
|
||||
uint32_t polygon_index;
|
||||
Vector2 pathway_start;
|
||||
Vector2 pathway_end;
|
||||
};
|
||||
|
||||
struct Connection {
|
||||
/// Polygon that this connection leads to.
|
||||
Polygon *polygon = nullptr;
|
||||
@ -87,12 +93,7 @@ struct Edge {
|
||||
Vector2 pathway_end;
|
||||
};
|
||||
|
||||
/// Connections from this edge to other polygons.
|
||||
LocalVector<Connection> connections;
|
||||
};
|
||||
|
||||
struct Polygon {
|
||||
/// Id of the polygon in the map.
|
||||
uint32_t id = UINT32_MAX;
|
||||
|
||||
/// Navigation region or link that contains this polygon.
|
||||
@ -100,9 +101,6 @@ struct Polygon {
|
||||
|
||||
LocalVector<Vector2> vertices;
|
||||
|
||||
/// The edges of this `Polygon`
|
||||
LocalVector<Edge> edges;
|
||||
|
||||
real_t surface_area = 0.0;
|
||||
};
|
||||
|
||||
@ -177,7 +175,7 @@ struct ClosestPointQueryResult {
|
||||
};
|
||||
|
||||
struct EdgeConnectionPair {
|
||||
Edge::Connection connections[2];
|
||||
Connection connections[2];
|
||||
int size = 0;
|
||||
};
|
||||
|
||||
|
@ -1504,7 +1504,7 @@ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_font_data, const
|
||||
continue;
|
||||
}
|
||||
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());
|
||||
}
|
||||
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;
|
||||
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());
|
||||
if (!text.is_empty()) {
|
||||
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_MIME:
|
||||
//case FEATURE_NATIVE_ICON:
|
||||
//case FEATURE_WINDOW_TRANSPARENCY:
|
||||
case FEATURE_WINDOW_TRANSPARENCY:
|
||||
case FEATURE_CLIPBOARD:
|
||||
case FEATURE_KEEP_SCREEN_ON:
|
||||
case FEATURE_ORIENTATION:
|
||||
@ -592,8 +592,14 @@ void DisplayServerAndroid::window_set_flag(DisplayServer::WindowFlags p_flag, bo
|
||||
}
|
||||
|
||||
bool DisplayServerAndroid::window_get_flag(DisplayServer::WindowFlags p_flag, DisplayServer::WindowID p_window) const {
|
||||
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) {
|
||||
// Not supported on Android.
|
||||
@ -961,3 +967,7 @@ void DisplayServerAndroid::set_native_icon(const String &p_filename) {
|
||||
void DisplayServerAndroid::set_icon(const Ref<Image> &p_icon) {
|
||||
// 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_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();
|
||||
};
|
||||
|
@ -61,6 +61,11 @@
|
||||
<member name="gradle_build/export_format" type="int" setter="" getter="">
|
||||
Application export format (*.apk or *.aab).
|
||||
</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="">
|
||||
Path to the Gradle build directory. If left empty, then [code]res://android[/code] will be used.
|
||||
</member>
|
||||
|
@ -280,6 +280,7 @@ static const int EXPORT_FORMAT_AAB = 1;
|
||||
|
||||
static const char *APK_ASSETS_DIRECTORY = "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_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 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);
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
print_verbose("Building temporary manifest...");
|
||||
|
||||
bool google_play_instant_build = (bool)p_preset->get("gradle_build/google_play_instant");
|
||||
|
||||
String manifest_text =
|
||||
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\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_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 += "</manifest>\n";
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
bool should_be_transparent = _should_be_transparent(p_preset);
|
||||
|
||||
// Default/Reserved theme attributes.
|
||||
Dictionary main_theme_attributes;
|
||||
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:windowIsTranslucent"] = bool_to_string(should_be_transparent);
|
||||
if (should_be_transparent) {
|
||||
main_theme_attributes["android:windowBackground"] = "@android:color/transparent";
|
||||
}
|
||||
|
||||
Dictionary splash_theme_attributes;
|
||||
splash_theme_attributes["android:windowSplashScreenBackground"] = "@mipmap/icon_background";
|
||||
splash_theme_attributes["windowSplashScreenAnimatedIcon"] = "@mipmap/icon_foreground";
|
||||
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");
|
||||
|
||||
@ -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) {
|
||||
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") {
|
||||
String min_sdk_str = p_preset->get("gradle_build/min_sdk");
|
||||
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/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::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).
|
||||
// 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));
|
||||
@ -2952,7 +2987,8 @@ bool EditorExportPlatformAndroid::has_valid_project_configuration(const Ref<Edit
|
||||
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");
|
||||
Ref<FileAccess> f = FileAccess::open(build_version_path, FileAccess::READ);
|
||||
if (f.is_valid()) {
|
||||
@ -2963,6 +2999,12 @@ bool EditorExportPlatformAndroid::has_valid_project_configuration(const Ref<Edit
|
||||
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");
|
||||
@ -3564,6 +3606,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
|
||||
String enabled_abi_string = join_abis(enabled_abis, "|", false);
|
||||
String sign_flag = should_sign ? "true" : "false";
|
||||
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_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("-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("-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
|
||||
// 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);
|
||||
|
||||
bool _should_be_transparent(const Ref<EditorExportPreset> &p_preset) const;
|
||||
|
||||
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);
|
||||
|
@ -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"));
|
||||
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(
|
||||
" <application android:label=\"@string/godot_project_name_string\"\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";
|
||||
|
||||
if (google_play_instant_build) {
|
||||
manifest_application_text += " <dist:module dist:instant=\"true\" />\n";
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -37,6 +37,10 @@ dependencies {
|
||||
implementation "androidx.fragment:fragment:$versions.fragmentVersion"
|
||||
implementation "androidx.core:core-splashscreen:$versions.splashscreenVersion"
|
||||
|
||||
if (isInstantApp()) {
|
||||
implementation "com.google.android.gms:play-services-instantapps:$versions.instantAppsVersion"
|
||||
}
|
||||
|
||||
if (rootProject.findProject(":lib")) {
|
||||
implementation project(":lib")
|
||||
} else if (rootProject.findProject(":godot:lib")) {
|
||||
@ -90,7 +94,7 @@ android {
|
||||
jvmTarget = versions.javaVersion
|
||||
}
|
||||
|
||||
assetPacks = [":assetPackInstallTime"]
|
||||
assetPacks = isInstantApp() ? [] : [":assetPackInstallTime"]
|
||||
|
||||
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