Merge pull request #87514 from YuriSizov/4.1-cherrypicks

Cherry-picks for the 4.1 branch (future 4.1.4) - 1st batch
This commit is contained in:
Yuri Sizov 2024-01-24 11:42:59 +01:00 committed by GitHub
commit b915d7d9f2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
117 changed files with 852 additions and 444 deletions

View File

@ -15,3 +15,39 @@
# Style: clang-format: Disable AllowShortIfStatementsOnASingleLine
e956e80c1fa1cc8aefcb1533e5acf5cf3c8ffdd9
# One Copyright Update to rule them all
d95794ec8a7c362b06a9cf080e2554ef77adb667
# Update copyright statements to 2022
fe52458154c64fb1b741df4f7bd10106395f7cbd
# Update copyright statements to 2021
b5334d14f7a471f94bcbd64d5bae2ad853d0b7f1
# Update copyright statements to 2020
a7f49ac9a107820a62677ee3fb49d38982a25165
# Update copyright statements to 2019
b16c309f82c77d606472c3c721a1857e323a09e7
# Update copyright statements to 2018
b50a9114b105dafafdda8248a38653bca314a6f3
# Welcome in 2017, dear changelog reader!
c7bc44d5ad9aae4902280012f7654e2318cd910e
# Update copyright to 2016 in headers
5be9ff7b6715a661e85f99b108f96340de7ef435
# Updated copyright year in all headers
fdaa2920eb21fff3320a17e9239e04dfadecdb00
# Add missing copyright headers and fix formatting
e4213e66b2dd8f5a87d8cf5015ac83ba3143279d
# Use HTTPS URL for Godot's website in the headers
bd282ff43f23fe845f29a3e25c8efc01bd65ffb0
# Add "Godot Engine contributors" copyright line
df61dc4b2bd54a5a40c515493c76f5a458e5b541

3
.github/CODEOWNERS vendored
View File

@ -139,10 +139,9 @@ doc_classes/* @godotengine/documentation
/platform/android/ @godotengine/android
/platform/ios/ @godotengine/ios
/platform/javascript/ @godotengine/html5
/platform/linuxbsd/ @godotengine/linux-bsd
/platform/macos/ @godotengine/macos
/platform/uwp/ @godotengine/uwp
/platform/web/ @godotengine/web
/platform/windows/ @godotengine/windows
# Scene

View File

@ -150,7 +150,7 @@ jobs:
run: |
${{ matrix.bin }} --version
${{ matrix.bin }} --help
${{ matrix.bin }} --test --headless
${{ matrix.bin }} --headless --test --force-colors
# Check class reference
- name: Check for class reference updates

View File

@ -72,4 +72,4 @@ jobs:
run: |
${{ matrix.bin }} --version
${{ matrix.bin }} --help
${{ matrix.bin }} --test
${{ matrix.bin }} --test --force-colors

View File

@ -7,7 +7,7 @@ env:
# Used for the cache key. Add version suffix to force clean build.
GODOT_BASE_BRANCH: '4.1'
SCONSFLAGS: verbose=yes warnings=extra werror=yes debug_symbols=no
EM_VERSION: 3.1.45
EM_VERSION: 3.1.39
EM_CACHE_FOLDER: "emsdk-cache"
concurrency:

View File

@ -75,4 +75,4 @@ jobs:
run: |
${{ matrix.bin }} --version
${{ matrix.bin }} --help
${{ matrix.bin }} --test
${{ matrix.bin }} --test --force-colors

18
.gitignore vendored
View File

@ -132,23 +132,9 @@ cppcheck-cppcheck-build-dir/
*.pydevproject
*.launch
# Gcov and Lcov code coverage
*.gcno
# GCOV code coverage
*.gcda
*.gcov.html
*.func.html
*.func-sort-c.html
*index-sort-f.html
*index-sort-l.html
*index.html
godot.info
amber.png
emerald.png
glass.png
ruby.png
snow.png
updown.png
gcov.css
*.gcno
# Geany
*.geany

View File

@ -1060,7 +1060,8 @@ void Input::joy_axis(int p_device, JoyAxis p_axis, float p_value) {
return;
}
JoyEvent map = _get_mapped_axis_event(map_db[joy.mapping], p_axis, p_value);
JoyAxisRange range;
JoyEvent map = _get_mapped_axis_event(map_db[joy.mapping], p_axis, p_value, range);
if (map.type == TYPE_BUTTON) {
bool pressed = map.value > 0.5;
@ -1100,7 +1101,7 @@ void Input::joy_axis(int p_device, JoyAxis p_axis, float p_value) {
if (map.type == TYPE_AXIS) {
JoyAxis axis = JoyAxis(map.index);
float value = map.value;
if (axis == JoyAxis::TRIGGER_LEFT || axis == JoyAxis::TRIGGER_RIGHT) {
if (range == FULL_AXIS && (axis == JoyAxis::TRIGGER_LEFT || axis == JoyAxis::TRIGGER_RIGHT)) {
// Convert to a value between 0.0f and 1.0f.
value = 0.5f + value / 2.0f;
}
@ -1206,7 +1207,7 @@ Input::JoyEvent Input::_get_mapped_button_event(const JoyDeviceMapping &mapping,
return event;
}
Input::JoyEvent Input::_get_mapped_axis_event(const JoyDeviceMapping &mapping, JoyAxis p_axis, float p_value) {
Input::JoyEvent Input::_get_mapped_axis_event(const JoyDeviceMapping &mapping, JoyAxis p_axis, float p_value, JoyAxisRange &r_range) {
JoyEvent event;
for (int i = 0; i < mapping.bindings.size(); i++) {
@ -1252,6 +1253,7 @@ Input::JoyEvent Input::_get_mapped_axis_event(const JoyDeviceMapping &mapping, J
case TYPE_AXIS:
event.index = (int)binding.output.axis.axis;
event.value = value;
r_range = binding.output.axis.range;
if (binding.output.axis.range != binding.input.axis.range) {
switch (binding.output.axis.range) {
case POSITIVE_HALF_AXIS:

View File

@ -219,7 +219,7 @@ private:
Vector<JoyDeviceMapping> map_db;
JoyEvent _get_mapped_button_event(const JoyDeviceMapping &mapping, JoyButton p_button);
JoyEvent _get_mapped_axis_event(const JoyDeviceMapping &mapping, JoyAxis p_axis, float p_value);
JoyEvent _get_mapped_axis_event(const JoyDeviceMapping &mapping, JoyAxis p_axis, float p_value, JoyAxisRange &r_range);
void _get_mapped_hat_events(const JoyDeviceMapping &mapping, HatDir p_hat, JoyEvent r_events[(size_t)HatDir::MAX]);
JoyButton _get_output_button(String output);
JoyAxis _get_output_axis(String output);

View File

@ -1619,8 +1619,10 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
encode_uint32(datalen, buf);
buf += 4;
const uint8_t *r = data.ptr();
memcpy(buf, &r[0], datalen * datasize);
buf += datalen * datasize;
if (r) {
memcpy(buf, &r[0], datalen * datasize);
buf += datalen * datasize;
}
}
r_len += 4 + datalen * datasize;

View File

@ -917,7 +917,7 @@ String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_rem
}
// Fallback to p_path if new_path does not exist.
if (!FileAccess::exists(new_path)) {
if (!FileAccess::exists(new_path + ".import") && !FileAccess::exists(new_path)) {
WARN_PRINT(vformat("Translation remap '%s' does not exist. Falling back to '%s'.", new_path, p_path));
new_path = p_path;
}

View File

@ -81,35 +81,27 @@ template <class T, class... P>
class CallableCustomMethodPointer : public CallableCustomMethodPointerBase {
struct Data {
T *instance;
#ifdef DEBUG_ENABLED
uint64_t object_id;
#endif
void (T::*method)(P...);
} data;
public:
virtual ObjectID get_object() const {
#ifdef DEBUG_ENABLED
if (ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr) {
return ObjectID();
}
#endif
return data.instance->get_instance_id();
}
virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
#ifdef DEBUG_ENABLED
ERR_FAIL_COND_MSG(ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr, "Invalid Object id '" + uitos(data.object_id) + "', can't call method.");
#endif
ERR_FAIL_NULL_MSG(ObjectDB::get_instance(ObjectID(data.object_id)), "Invalid Object id '" + uitos(data.object_id) + "', can't call method.");
call_with_variant_args(data.instance, data.method, p_arguments, p_argcount, r_call_error);
}
CallableCustomMethodPointer(T *p_instance, void (T::*p_method)(P...)) {
memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes.
data.instance = p_instance;
#ifdef DEBUG_ENABLED
data.object_id = p_instance->get_instance_id();
#endif
data.method = p_method;
_setup((uint32_t *)&data, sizeof(Data));
}
@ -135,36 +127,28 @@ template <class T, class R, class... P>
class CallableCustomMethodPointerRet : public CallableCustomMethodPointerBase {
struct Data {
T *instance;
#ifdef DEBUG_ENABLED
uint64_t object_id;
#endif
R(T::*method)
(P...);
} data;
public:
virtual ObjectID get_object() const {
#ifdef DEBUG_ENABLED
if (ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr) {
return ObjectID();
}
#endif
return data.instance->get_instance_id();
}
virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
#ifdef DEBUG_ENABLED
ERR_FAIL_COND_MSG(ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr, "Invalid Object id '" + uitos(data.object_id) + "', can't call method.");
#endif
ERR_FAIL_NULL_MSG(ObjectDB::get_instance(ObjectID(data.object_id)), "Invalid Object id '" + uitos(data.object_id) + "', can't call method.");
call_with_variant_args_ret(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error);
}
CallableCustomMethodPointerRet(T *p_instance, R (T::*p_method)(P...)) {
memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes.
data.instance = p_instance;
#ifdef DEBUG_ENABLED
data.object_id = p_instance->get_instance_id();
#endif
data.method = p_method;
_setup((uint32_t *)&data, sizeof(Data));
}
@ -190,36 +174,28 @@ template <class T, class R, class... P>
class CallableCustomMethodPointerRetC : public CallableCustomMethodPointerBase {
struct Data {
T *instance;
#ifdef DEBUG_ENABLED
uint64_t object_id;
#endif
R(T::*method)
(P...) const;
} data;
public:
virtual ObjectID get_object() const override {
#ifdef DEBUG_ENABLED
if (ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr) {
return ObjectID();
}
#endif
return data.instance->get_instance_id();
}
virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override {
#ifdef DEBUG_ENABLED
ERR_FAIL_COND_MSG(ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr, "Invalid Object id '" + uitos(data.object_id) + "', can't call method.");
#endif
ERR_FAIL_NULL_MSG(ObjectDB::get_instance(ObjectID(data.object_id)), "Invalid Object id '" + uitos(data.object_id) + "', can't call method.");
call_with_variant_args_retc(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error);
}
CallableCustomMethodPointerRetC(T *p_instance, R (T::*p_method)(P...) const) {
memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes.
data.instance = p_instance;
#ifdef DEBUG_ENABLED
data.object_id = p_instance->get_instance_id();
#endif
data.method = p_method;
_setup((uint32_t *)&data, sizeof(Data));
}

View File

@ -35,6 +35,8 @@
#include "core/object/class_db.h"
#include "core/object/script_language.h"
#include <stdio.h>
#ifdef DEV_ENABLED
// Includes sanity checks to ensure that a queue set as a thread singleton override
// is only ever called from the thread it was set for.
@ -93,7 +95,7 @@ Error CallQueue::push_callablep(const Callable &p_callable, const Variant **p_ar
if ((page_bytes[pages_used - 1] + room_needed) > uint32_t(PAGE_SIZE_BYTES)) {
if (pages_used == max_pages) {
ERR_PRINT("Failed method: " + p_callable + ". Message queue out of memory. " + error_text);
fprintf(stderr, "Failed method: %s. Message queue out of memory. %s\n", String(p_callable).utf8().get_data(), error_text.utf8().get_data());
statistics();
UNLOCK_MUTEX;
return ERR_OUT_OF_MEMORY;
@ -144,7 +146,7 @@ Error CallQueue::push_set(ObjectID p_id, const StringName &p_prop, const Variant
if (ObjectDB::get_instance(p_id)) {
type = ObjectDB::get_instance(p_id)->get_class();
}
ERR_PRINT("Failed set: " + type + ":" + p_prop + " target ID: " + itos(p_id) + ". Message queue out of memory. " + error_text);
fprintf(stderr, "Failed set: %s: %s target ID: %s. Message queue out of memory. %s\n", type.utf8().get_data(), String(p_prop).utf8().get_data(), itos(p_id).utf8().get_data(), error_text.utf8().get_data());
statistics();
UNLOCK_MUTEX;
@ -181,7 +183,7 @@ Error CallQueue::push_notification(ObjectID p_id, int p_notification) {
if ((page_bytes[pages_used - 1] + room_needed) > uint32_t(PAGE_SIZE_BYTES)) {
if (pages_used == max_pages) {
ERR_PRINT("Failed notification: " + itos(p_notification) + " target ID: " + itos(p_id) + ". Message queue out of memory. " + error_text);
fprintf(stderr, "Failed notification: %s target ID: %s. Message queue out of memory. %s\n", itos(p_notification).utf8().get_data(), itos(p_id).utf8().get_data(), error_text.utf8().get_data());
statistics();
UNLOCK_MUTEX;
return ERR_OUT_OF_MEMORY;

View File

@ -520,11 +520,11 @@ String TranslationServer::get_country_name(const String &p_country) const {
void TranslationServer::set_locale(const String &p_locale) {
locale = standardize_locale(p_locale);
ResourceLoader::reload_translation_remaps();
if (OS::get_singleton()->get_main_loop()) {
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED);
}
ResourceLoader::reload_translation_remaps();
}
String TranslationServer::get_locale() const {
@ -816,10 +816,11 @@ bool TranslationServer::is_pseudolocalization_enabled() const {
void TranslationServer::set_pseudolocalization_enabled(bool p_enabled) {
pseudolocalization_enabled = p_enabled;
ResourceLoader::reload_translation_remaps();
if (OS::get_singleton()->get_main_loop()) {
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED);
}
ResourceLoader::reload_translation_remaps();
}
void TranslationServer::set_editor_pseudolocalization(bool p_enabled) {
@ -836,10 +837,11 @@ void TranslationServer::reload_pseudolocalization() {
pseudolocalization_suffix = GLOBAL_GET("internationalization/pseudolocalization/suffix");
pseudolocalization_skip_placeholders_enabled = GLOBAL_GET("internationalization/pseudolocalization/skip_placeholders");
ResourceLoader::reload_translation_remaps();
if (OS::get_singleton()->get_main_loop()) {
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED);
}
ResourceLoader::reload_translation_remaps();
}
StringName TranslationServer::pseudolocalize(const StringName &p_message) const {

View File

@ -305,7 +305,11 @@ void String::copy_from(const char *p_cstr) {
char32_t *dst = this->ptrw();
for (size_t i = 0; i <= len; i++) {
#if CHAR_MIN == 0
uint8_t c = p_cstr[i];
#else
uint8_t c = p_cstr[i] >= 0 ? p_cstr[i] : uint8_t(256 + p_cstr[i]);
#endif
if (c == 0 && i < len) {
print_unicode_error("NUL character", true);
dst[i] = _replacement_char;
@ -338,7 +342,11 @@ void String::copy_from(const char *p_cstr, const int p_clip_to) {
char32_t *dst = this->ptrw();
for (int i = 0; i < len; i++) {
#if CHAR_MIN == 0
uint8_t c = p_cstr[i];
#else
uint8_t c = p_cstr[i] >= 0 ? p_cstr[i] : uint8_t(256 + p_cstr[i]);
#endif
if (c == 0) {
print_unicode_error("NUL character", true);
dst[i] = _replacement_char;
@ -544,7 +552,11 @@ String &String::operator+=(const char *p_str) {
char32_t *dst = ptrw() + lhs_len;
for (size_t i = 0; i <= rhs_len; i++) {
#if CHAR_MIN == 0
uint8_t c = p_str[i];
#else
uint8_t c = p_str[i] >= 0 ? p_str[i] : uint8_t(256 + p_str[i]);
#endif
if (c == 0 && i < rhs_len) {
print_unicode_error("NUL character", true);
dst[i] = _replacement_char;
@ -1814,7 +1826,11 @@ Error String::parse_utf8(const char *p_utf8, int p_len, bool p_skip_cr) {
int skip = 0;
uint8_t c_start = 0;
while (ptrtmp != ptrtmp_limit && *ptrtmp) {
#if CHAR_MIN == 0
uint8_t c = *ptrtmp;
#else
uint8_t c = *ptrtmp >= 0 ? *ptrtmp : uint8_t(256 + *ptrtmp);
#endif
if (skip == 0) {
if (p_skip_cr && c == '\r') {
@ -1882,7 +1898,11 @@ Error String::parse_utf8(const char *p_utf8, int p_len, bool p_skip_cr) {
int skip = 0;
uint32_t unichar = 0;
while (cstr_size) {
#if CHAR_MIN == 0
uint8_t c = *p_utf8;
#else
uint8_t c = *p_utf8 >= 0 ? *p_utf8 : uint8_t(256 + *p_utf8);
#endif
if (skip == 0) {
if (p_skip_cr && c == '\r') {
@ -3945,24 +3965,22 @@ bool String::is_absolute_path() const {
}
}
static _FORCE_INLINE_ bool _is_valid_identifier_bit(int p_index, char32_t p_char) {
if (p_index == 0 && is_digit(p_char)) {
return false; // No start with number plz.
}
return is_ascii_identifier_char(p_char);
}
String String::validate_identifier() const {
if (is_empty()) {
return "_"; // Empty string is not a valid identifier;
}
String result = *this;
String result;
if (is_digit(operator[](0))) {
result = "_" + *this;
} else {
result = *this;
}
int len = result.length();
char32_t *buffer = result.ptrw();
for (int i = 0; i < len; i++) {
if (!_is_valid_identifier_bit(i, buffer[i])) {
if (!is_ascii_identifier_char(buffer[i])) {
buffer[i] = '_';
}
}
@ -3977,10 +3995,14 @@ bool String::is_valid_identifier() const {
return false;
}
if (is_digit(operator[](0))) {
return false;
}
const char32_t *str = &operator[](0);
for (int i = 0; i < len; i++) {
if (!_is_valid_identifier_bit(i, str[i])) {
if (!is_ascii_identifier_char(str[i])) {
return false;
}
}

View File

@ -90,6 +90,10 @@ private:
}
_FORCE_INLINE_ bool _get_alloc_size_checked(size_t p_elements, size_t *out) const {
if (unlikely(p_elements == 0)) {
*out = 0;
return true;
}
#if defined(__GNUC__)
size_t o;
size_t p;
@ -101,13 +105,12 @@ private:
if (__builtin_add_overflow(o, static_cast<size_t>(32), &p)) {
return false; // No longer allocated here.
}
return true;
#else
// Speed is more important than correctness here, do the operations unchecked
// and hope for the best.
*out = _get_alloc_size(p_elements);
return true;
#endif
return *out;
}
void _unref(void *p_data);

View File

@ -310,7 +310,7 @@ struct HashMapHasherDefault {
static _FORCE_INLINE_ uint32_t hash(const char16_t p_uchar) { return hash_fmix32(p_uchar); }
static _FORCE_INLINE_ uint32_t hash(const char32_t p_uchar) { return hash_fmix32(p_uchar); }
static _FORCE_INLINE_ uint32_t hash(const RID &p_rid) { return hash_one_uint64(p_rid.get_id()); }
static _FORCE_INLINE_ uint32_t hash(const CharString &p_char_string) { return hash_djb2(p_char_string.ptr()); }
static _FORCE_INLINE_ uint32_t hash(const CharString &p_char_string) { return hash_djb2(p_char_string.get_data()); }
static _FORCE_INLINE_ uint32_t hash(const StringName &p_string_name) { return p_string_name.hash(); }
static _FORCE_INLINE_ uint32_t hash(const NodePath &p_path) { return p_path.hash(); }
static _FORCE_INLINE_ uint32_t hash(const ObjectID &p_id) { return hash_one_uint64(p_id); }

View File

@ -257,7 +257,7 @@
<param index="0" name="track_idx" type="int" />
<param index="1" name="to_animation" type="Animation" />
<description>
Adds a new track that is a copy of the given track from [param to_animation].
Adds a new track to [param to_animation] that is a copy of the given track from this animation.
</description>
</method>
<method name="find_track" qualifiers="const">

View File

@ -498,22 +498,22 @@
Represents the size of the [enum TextureParam] enum.
</constant>
<constant name="TEXTURE_FILTER_NEAREST" value="0" enum="TextureFilter">
The texture filter reads from the nearest pixel only. The simplest and fastest method of filtering, but the texture will look pixelized.
The texture filter reads from the nearest pixel only. This makes the texture look pixelated from up close, and grainy from a distance (due to mipmaps not being sampled).
</constant>
<constant name="TEXTURE_FILTER_LINEAR" value="1" enum="TextureFilter">
The texture filter blends between the nearest 4 pixels. Use this when you want to avoid a pixelated style, but do not want mipmaps.
The texture filter blends between the nearest 4 pixels. This makes the texture look smooth from up close, and grainy from a distance (due to mipmaps not being sampled).
</constant>
<constant name="TEXTURE_FILTER_NEAREST_WITH_MIPMAPS" value="2" enum="TextureFilter">
The texture filter reads from the nearest pixel in the nearest mipmap. The fastest way to read from textures with mipmaps.
The texture filter reads from the nearest pixel and blends between the nearest 2 mipmaps (or uses the nearest mipmap if [member ProjectSettings.rendering/textures/default_filters/use_nearest_mipmap_filter] is [code]true[/code]). This makes the texture look pixelated from up close, and smooth from a distance.
</constant>
<constant name="TEXTURE_FILTER_LINEAR_WITH_MIPMAPS" value="3" enum="TextureFilter">
The texture filter blends between the nearest 4 pixels and between the nearest 2 mipmaps. Use this for most cases as mipmaps are important to smooth out pixels that are far from the camera.
The texture filter blends between the nearest 4 pixels and between the nearest 2 mipmaps (or uses the nearest mipmap if [member ProjectSettings.rendering/textures/default_filters/use_nearest_mipmap_filter] is [code]true[/code]). This makes the texture look smooth from up close, and smooth from a distance.
</constant>
<constant name="TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC" value="4" enum="TextureFilter">
The texture filter reads from the nearest pixel, but selects a mipmap based on the angle between the surface and the camera view. This reduces artifacts on surfaces that are almost in line with the camera. The anisotropic filtering level can be changed by adjusting [member ProjectSettings.rendering/textures/default_filters/anisotropic_filtering_level].
The texture filter reads from the nearest pixel and blends between 2 mipmaps (or uses the nearest mipmap if [member ProjectSettings.rendering/textures/default_filters/use_nearest_mipmap_filter] is [code]true[/code]) based on the angle between the surface and the camera view. This makes the texture look pixelated from up close, and smooth from a distance. Anisotropic filtering improves texture quality on surfaces that are almost in line with the camera, but is slightly slower. The anisotropic filtering level can be changed by adjusting [member ProjectSettings.rendering/textures/default_filters/anisotropic_filtering_level].
</constant>
<constant name="TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC" value="5" enum="TextureFilter">
The texture filter blends between the nearest 4 pixels and selects a mipmap based on the angle between the surface and the camera view. This reduces artifacts on surfaces that are almost in line with the camera. This is the slowest of the filtering options, but results in the highest quality texturing. The anisotropic filtering level can be changed by adjusting [member ProjectSettings.rendering/textures/default_filters/anisotropic_filtering_level].
The texture filter blends between the nearest 4 pixels and blends between 2 mipmaps (or uses the nearest mipmap if [member ProjectSettings.rendering/textures/default_filters/use_nearest_mipmap_filter] is [code]true[/code]) based on the angle between the surface and the camera view. This makes the texture look smooth from up close, and smooth from a distance. Anisotropic filtering improves texture quality on surfaces that are almost in line with the camera, but is slightly slower. The anisotropic filtering level can be changed by adjusting [member ProjectSettings.rendering/textures/default_filters/anisotropic_filtering_level].
</constant>
<constant name="TEXTURE_FILTER_MAX" value="6" enum="TextureFilter">
Represents the size of the [enum TextureFilter] enum.

View File

@ -290,7 +290,7 @@
Particles are drawn in the order emitted.
</constant>
<constant name="DRAW_ORDER_LIFETIME" value="1" enum="DrawOrder">
Particles are drawn in order of remaining lifetime.
Particles are drawn in order of remaining lifetime. In other words, the particle with the highest lifetime is drawn at the front.
</constant>
<constant name="PARAM_INITIAL_LINEAR_VELOCITY" value="0" enum="Parameter">
Use with [method set_param_min], [method set_param_max], and [method set_param_curve] to set initial velocity properties.

View File

@ -315,7 +315,7 @@
Particles are drawn in the order emitted.
</constant>
<constant name="DRAW_ORDER_LIFETIME" value="1" enum="DrawOrder">
Particles are drawn in order of remaining lifetime.
Particles are drawn in order of remaining lifetime. In other words, the particle with the highest lifetime is drawn at the front.
</constant>
<constant name="DRAW_ORDER_VIEW_DEPTH" value="2" enum="DrawOrder">
Particles are drawn in order of depth.

View File

@ -32,7 +32,7 @@
Only available when [member ProjectSettings.rendering/lights_and_shadows/use_physical_light_units] is enabled.
</member>
<member name="exposure_shutter_speed" type="float" setter="set_shutter_speed" getter="get_shutter_speed" default="100.0">
Time for shutter to open and close, measured in seconds. A higher value will let in more light leading to a brighter image, while a lower amount will let in less light leading to a darker image.
Time for shutter to open and close, evaluated as [code]1 / shutter_speed[/code] seconds. A higher value will allow less light (leading to a darker image), while a lower value will allow more light (leading to a brighter image).
Only available when [member ProjectSettings.rendering/lights_and_shadows/use_physical_light_units] is enabled.
</member>
<member name="frustum_far" type="float" setter="set_far" getter="get_far" default="4000.0">

View File

@ -660,24 +660,26 @@
The [CanvasItem] will inherit the filter from its parent.
</constant>
<constant name="TEXTURE_FILTER_NEAREST" value="1" enum="TextureFilter">
The texture filter reads from the nearest pixel only. The simplest and fastest method of filtering. Useful for pixel art.
The texture filter reads from the nearest pixel only. This makes the texture look pixelated from up close, and grainy from a distance (due to mipmaps not being sampled).
</constant>
<constant name="TEXTURE_FILTER_LINEAR" value="2" enum="TextureFilter">
The texture filter blends between the nearest four pixels. Use this for most cases where you want to avoid a pixelated style.
The texture filter blends between the nearest 4 pixels. This makes the texture look smooth from up close, and grainy from a distance (due to mipmaps not being sampled).
</constant>
<constant name="TEXTURE_FILTER_NEAREST_WITH_MIPMAPS" value="3" enum="TextureFilter">
The texture filter reads from the nearest pixel in the nearest mipmap. This is the fastest way to read from textures with mipmaps.
The texture filter reads from the nearest pixel and blends between the nearest 2 mipmaps (or uses the nearest mipmap if [member ProjectSettings.rendering/textures/default_filters/use_nearest_mipmap_filter] is [code]true[/code]). This makes the texture look pixelated from up close, and smooth from a distance.
Use this for non-pixel art textures that may be viewed at a low scale (e.g. due to [Camera2D] zoom or sprite scaling), as mipmaps are important to smooth out pixels that are smaller than on-screen pixels.
</constant>
<constant name="TEXTURE_FILTER_LINEAR_WITH_MIPMAPS" value="4" enum="TextureFilter">
The texture filter blends between the nearest 4 pixels and between the nearest 2 mipmaps. Use this for non-pixel art textures that may be viewed at a low scale (e.g. due to [Camera2D] zoom), as mipmaps are important to smooth out pixels that are smaller than on-screen pixels.
The texture filter blends between the nearest 4 pixels and between the nearest 2 mipmaps (or uses the nearest mipmap if [member ProjectSettings.rendering/textures/default_filters/use_nearest_mipmap_filter] is [code]true[/code]). This makes the texture look smooth from up close, and smooth from a distance.
Use this for non-pixel art textures that may be viewed at a low scale (e.g. due to [Camera2D] zoom or sprite scaling), as mipmaps are important to smooth out pixels that are smaller than on-screen pixels.
</constant>
<constant name="TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC" value="5" enum="TextureFilter">
The texture filter reads from the nearest pixel, but selects a mipmap based on the angle between the surface and the camera view. This reduces artifacts on surfaces that are almost in line with the camera. The anisotropic filtering level can be changed by adjusting [member ProjectSettings.rendering/textures/default_filters/anisotropic_filtering_level].
[b]Note:[/b] This texture filter is rarely useful in 2D projects. [constant TEXTURE_FILTER_NEAREST_WITH_MIPMAPS] is usually more appropriate.
The texture filter reads from the nearest pixel and blends between 2 mipmaps (or uses the nearest mipmap if [member ProjectSettings.rendering/textures/default_filters/use_nearest_mipmap_filter] is [code]true[/code]) based on the angle between the surface and the camera view. This makes the texture look pixelated from up close, and smooth from a distance. Anisotropic filtering improves texture quality on surfaces that are almost in line with the camera, but is slightly slower. The anisotropic filtering level can be changed by adjusting [member ProjectSettings.rendering/textures/default_filters/anisotropic_filtering_level].
[b]Note:[/b] This texture filter is rarely useful in 2D projects. [constant TEXTURE_FILTER_NEAREST_WITH_MIPMAPS] is usually more appropriate in this case.
</constant>
<constant name="TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC" value="6" enum="TextureFilter">
The texture filter blends between the nearest 4 pixels and selects a mipmap based on the angle between the surface and the camera view. This reduces artifacts on surfaces that are almost in line with the camera. This is the slowest of the filtering options, but results in the highest quality texturing. The anisotropic filtering level can be changed by adjusting [member ProjectSettings.rendering/textures/default_filters/anisotropic_filtering_level].
[b]Note:[/b] This texture filter is rarely useful in 2D projects. [constant TEXTURE_FILTER_LINEAR_WITH_MIPMAPS] is usually more appropriate.
The texture filter blends between the nearest 4 pixels and blends between 2 mipmaps (or uses the nearest mipmap if [member ProjectSettings.rendering/textures/default_filters/use_nearest_mipmap_filter] is [code]true[/code]) based on the angle between the surface and the camera view. This makes the texture look smooth from up close, and smooth from a distance. Anisotropic filtering improves texture quality on surfaces that are almost in line with the camera, but is slightly slower. The anisotropic filtering level can be changed by adjusting [member ProjectSettings.rendering/textures/default_filters/anisotropic_filtering_level].
[b]Note:[/b] This texture filter is rarely useful in 2D projects. [constant TEXTURE_FILTER_LINEAR_WITH_MIPMAPS] is usually more appropriate in this case.
</constant>
<constant name="TEXTURE_FILTER_MAX" value="7" enum="TextureFilter">
Represents the size of the [enum TextureFilter] enum.

View File

@ -5,7 +5,7 @@
</brief_description>
<description>
A 2D polyline shape, intended for use in physics. Used internally in [CollisionPolygon2D] when it's in [code]BUILD_SEGMENTS[/code] mode.
Being just a collection of interconnected line segments, [ConcavePolygonShape2D] is the most freely configurable single 2D shape. It can be used to form polygons of any nature, or even shapes that don't enclose an area. However, [ConvexPolygonShape2D] is [i]hollow[/i] even if the interconnected line segments do enclose an area, which often makes it unsuitable for physics or detection.
Being just a collection of interconnected line segments, [ConcavePolygonShape2D] is the most freely configurable single 2D shape. It can be used to form polygons of any nature, or even shapes that don't enclose an area. However, [ConcavePolygonShape2D] is [i]hollow[/i] even if the interconnected line segments do enclose an area, which often makes it unsuitable for physics or detection.
[b]Note:[/b] When used for collision, [ConcavePolygonShape2D] is intended to work with static [CollisionShape2D] nodes like [StaticBody2D] and will likely not behave well for [CharacterBody2D]s or [RigidBody2D]s in a mode other than Static.
[b]Warning:[/b] Physics bodies that are small have a chance to clip through this shape when moving fast. This happens because on one frame, the physics body may be on the "outside" of the shape, and on the next frame it may be "inside" it. [ConcavePolygonShape2D] is hollow, so it won't detect a collision.
[b]Performance:[/b] Due to its complexity, [ConcavePolygonShape2D] is the slowest 2D collision shape to check collisions against. Its use should generally be limited to level geometry. If the polyline is closed, [CollisionPolygon2D]'s [code]BUILD_SOLIDS[/code] mode can be used, which decomposes the polygon into convex ones; see [ConvexPolygonShape2D]'s documentation for instructions.

View File

@ -5,7 +5,7 @@
</brief_description>
<description>
A 3D trimesh shape, intended for use in physics. Usually used to provide a shape for a [CollisionShape3D].
Being just a collection of interconnected triangles, [ConcavePolygonShape3D] is the most freely configurable single 3D shape. It can be used to form polyhedra of any nature, or even shapes that don't enclose a volume. However, [ConvexPolygonShape3D] is [i]hollow[/i] even if the interconnected triangles do enclose a volume, which often makes it unsuitable for physics or detection.
Being just a collection of interconnected triangles, [ConcavePolygonShape3D] is the most freely configurable single 3D shape. It can be used to form polyhedra of any nature, or even shapes that don't enclose a volume. However, [ConcavePolygonShape3D] is [i]hollow[/i] even if the interconnected triangles do enclose a volume, which often makes it unsuitable for physics or detection.
[b]Note:[/b] When used for collision, [ConcavePolygonShape3D] is intended to work with static [CollisionShape3D] nodes like [StaticBody3D] and will likely not behave well for [CharacterBody3D]s or [RigidBody3D]s in a mode other than Static.
[b]Warning:[/b] Physics bodies that are small have a chance to clip through this shape when moving fast. This happens because on one frame, the physics body may be on the "outside" of the shape, and on the next frame it may be "inside" it. [ConcavePolygonShape3D] is hollow, so it won't detect a collision.
[b]Performance:[/b] Due to its complexity, [ConcavePolygonShape3D] is the slowest 3D collision shape to check collisions against. Its use should generally be limited to level geometry. For convex geometry, [ConvexPolygonShape3D] should be used. For dynamic physics bodies that need concave collision, several [ConvexPolygonShape3D]s can be used to represent its collision by using convex decomposition; see [ConvexPolygonShape3D]'s documentation for instructions.

View File

@ -1161,10 +1161,12 @@
[b]Note:[/b] As an optimization, this notification won't be sent from changes that occur while this node is outside of the scene tree. Instead, all of the theme item updates can be applied at once when the node enters the scene tree.
</constant>
<constant name="NOTIFICATION_SCROLL_BEGIN" value="47">
Sent when this node is inside a [ScrollContainer] which has begun being scrolled.
Sent when this node is inside a [ScrollContainer] which has begun being scrolled when dragging the scrollable area [i]with a touch event[/i]. This notification is [i]not[/i] sent when scrolling by dragging the scrollbar, scrolling with the mouse wheel or scrolling with keyboard/gamepad events.
[b]Note:[/b] This signal is only emitted on Android or iOS, or on desktop/web platforms when [member ProjectSettings.input_devices/pointing/emulate_touch_from_mouse] is enabled.
</constant>
<constant name="NOTIFICATION_SCROLL_END" value="48">
Sent when this node is inside a [ScrollContainer] which has stopped being scrolled.
Sent when this node is inside a [ScrollContainer] which has stopped being scrolled when dragging the scrollable area [i]with a touch event[/i]. This notification is [i]not[/i] sent when scrolling by dragging the scrollbar, scrolling with the mouse wheel or scrolling with keyboard/gamepad events.
[b]Note:[/b] This signal is only emitted on Android or iOS, or on desktop/web platforms when [member ProjectSettings.input_devices/pointing/emulate_touch_from_mouse] is enabled.
</constant>
<constant name="NOTIFICATION_LAYOUT_DIRECTION_CHANGED" value="49">
Sent when control layout direction is changed.

View File

@ -294,6 +294,42 @@
<param index="1" name="overwrite" type="bool" default="false" />
<description>
Adds entries from [param dictionary] to this dictionary. By default, duplicate keys are not copied over, unless [param overwrite] is [code]true[/code].
[codeblocks]
[gdscript]
var dict = { "item": "sword", "quantity": 2 }
var other_dict = { "quantity": 15, "color": "silver" }
# Overwriting of existing keys is disabled by default.
dict.merge(other_dict)
print(dict) # { "item": "sword", "quantity": 2, "color": "silver" }
# With overwriting of existing keys enabled.
dict.merge(other_dict, true)
print(dict) # { "item": "sword", "quantity": 15, "color": "silver" }
[/gdscript]
[csharp]
var dict = new Godot.Collections.Dictionary
{
["item"] = "sword",
["quantity"] = 2,
};
var otherDict = new Godot.Collections.Dictionary
{
["quantity"] = 15,
["color"] = "silver",
};
// Overwriting of existing keys is disabled by default.
dict.Merge(otherDict);
GD.Print(dict); // { "item": "sword", "quantity": 2, "color": "silver" }
// With overwriting of existing keys enabled.
dict.Merge(otherDict, true);
GD.Print(dict); // { "item": "sword", "quantity": 15, "color": "silver" }
[/csharp]
[/codeblocks]
[b]Note:[/b] [method merge] is [i]not[/i] recursive. Nested dictionaries are considered as keys that can be overwritten or not depending on the value of [param overwrite], but they will never be merged together.
</description>
</method>
<method name="size" qualifiers="const">

View File

@ -5,6 +5,7 @@
</brief_description>
<description>
This [Control] node is used in the editor's Inspector dock to allow editing of numeric values. Can be used with [EditorInspectorPlugin] to recreate the same behavior.
If [member step] is [code]1[/code], the [EditorSpinSlider] will display up/down arrows, similar to [SpinBox]. If the [member step] is not [code]1[/code], a slider will be displayed instead.
</description>
<tutorials>
</tutorials>
@ -14,7 +15,7 @@
</member>
<member name="focus_mode" type="int" setter="set_focus_mode" getter="get_focus_mode" overrides="Control" enum="Control.FocusMode" default="2" />
<member name="hide_slider" type="bool" setter="set_hide_slider" getter="is_hiding_slider" default="false">
If [code]true[/code], the slider is hidden.
If [code]true[/code], the slider and up/down arrows are hidden.
</member>
<member name="label" type="String" setter="set_label" getter="get_label" default="&quot;&quot;">
The text that displays to the left of the value.

View File

@ -113,9 +113,10 @@
Particles are drawn in the order emitted.
</constant>
<constant name="DRAW_ORDER_LIFETIME" value="1" enum="DrawOrder">
Particles are drawn in order of remaining lifetime.
Particles are drawn in order of remaining lifetime. In other words, the particle with the highest lifetime is drawn at the front.
</constant>
<constant name="DRAW_ORDER_REVERSE_LIFETIME" value="2" enum="DrawOrder">
Particles are drawn in reverse order of remaining lifetime. In other words, the particle with the lowest lifetime is drawn at the front.
</constant>
<constant name="EMIT_FLAG_POSITION" value="1" enum="EmitFlags">
Particle starts at the specified position.

View File

@ -137,9 +137,10 @@
Particles are drawn in the order emitted.
</constant>
<constant name="DRAW_ORDER_LIFETIME" value="1" enum="DrawOrder">
Particles are drawn in order of remaining lifetime.
Particles are drawn in order of remaining lifetime. In other words, the particle with the highest lifetime is drawn at the front.
</constant>
<constant name="DRAW_ORDER_REVERSE_LIFETIME" value="2" enum="DrawOrder">
Particles are drawn in reverse order of remaining lifetime. In other words, the particle with the lowest lifetime is drawn at the front.
</constant>
<constant name="DRAW_ORDER_VIEW_DEPTH" value="3" enum="DrawOrder">
Particles are drawn in order of depth.

View File

@ -1,10 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="Material" inherits="Resource" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
Abstract base [Resource] for coloring and shading geometry.
Virtual base class for applying visual properties to an object, such as color and roughness.
</brief_description>
<description>
Material is a base [Resource] used for coloring and shading geometry. All materials inherit from it and almost all [VisualInstance3D] derived nodes carry a Material. A few flags and parameters are shared between all material types and are configured here.
[Material] is a base resource used for coloring and shading geometry. All materials inherit from it and almost all [VisualInstance3D] derived nodes carry a [Material]. A few flags and parameters are shared between all material types and are configured here.
Importantly, you can inherit from [Material] to create your own custom material type in script or in GDExtension.
</description>
<tutorials>
<link title="3D Material Testers Demo">https://godotengine.org/asset-library/asset/123</link>
@ -14,21 +15,25 @@
<method name="_can_do_next_pass" qualifiers="virtual const">
<return type="bool" />
<description>
Only exposed for the purpose of overriding. You cannot call this function directly. Used internally to determine if [member next_pass] should be shown in the editor or not.
</description>
</method>
<method name="_can_use_render_priority" qualifiers="virtual const">
<return type="bool" />
<description>
Only exposed for the purpose of overriding. You cannot call this function directly. Used internally to determine if [member render_priority] should be shown in the editor or not.
</description>
</method>
<method name="_get_shader_mode" qualifiers="virtual const">
<return type="int" enum="Shader.Mode" />
<description>
Only exposed for the purpose of overriding. You cannot call this function directly. Used internally by various editor tools.
</description>
</method>
<method name="_get_shader_rid" qualifiers="virtual const">
<return type="RID" />
<description>
Only exposed for the purpose of overriding. You cannot call this function directly. Used internally by various editor tools. Used to access the RID of the [Material]'s [Shader].
</description>
</method>
<method name="create_placeholder" qualifiers="const">
@ -40,18 +45,20 @@
<method name="inspect_native_shader_code">
<return type="void" />
<description>
Only available when running in the editor. Opens a popup that visualizes the generated shader code, including all variants and internal shader code.
</description>
</method>
</methods>
<members>
<member name="next_pass" type="Material" setter="set_next_pass" getter="get_next_pass">
Sets the [Material] to be used for the next pass. This renders the object again using a different material.
[b]Note:[/b] [member next_pass] materials are not necessarily drawn immediately after the source [Material]. Draw order is determined by material properties, [member render_priority], and distance to camera.
[b]Note:[/b] This only applies to [StandardMaterial3D]s and [ShaderMaterial]s with type "Spatial".
</member>
<member name="render_priority" type="int" setter="set_render_priority" getter="get_render_priority">
Sets the render priority for transparent objects in 3D scenes. Higher priority objects will be sorted in front of lower priority objects.
Sets the render priority for objects in 3D scenes. Higher priority objects will be sorted in front of lower priority objects. In other words, all objects with [member render_priority] [code]1[/code] will render before all objects with [member render_priority] [code]0[/code]).
[b]Note:[/b] This only applies to [StandardMaterial3D]s and [ShaderMaterial]s with type "Spatial".
[b]Note:[/b] This only applies to sorting of transparent objects. This will not impact how transparent objects are sorted relative to opaque objects. This is because opaque objects are not sorted, while transparent objects are sorted from back to front (subject to priority).
[b]Note:[/b] This will not impact how transparent objects are sorted relative to opaque objects or how dynamic meshes will be sorted relative to other opaque meshes. This is because all transparent objects are drawn after all opaque objects and all dynamic opaque meshes are drawn before other opaque meshes.
</member>
</members>
<constants>

View File

@ -445,7 +445,7 @@
<method name="get_tree" qualifiers="const">
<return type="SceneTree" />
<description>
Returns the [SceneTree] that contains this node.
Returns the [SceneTree] that contains this node. Returns [code]null[/code] and prints an error if this node is not inside the scene tree. See also [method is_inside_tree].
</description>
</method>
<method name="get_viewport" qualifiers="const">

View File

@ -172,7 +172,7 @@
<param index="1" name="submenu" type="String" />
<param index="2" name="id" type="int" default="-1" />
<description>
Adds an item that will act as a submenu of the parent [PopupMenu] node when clicked. The [param submenu] argument is the name of the child [PopupMenu] node that will be shown when the item is clicked.
Adds an item that will act as a submenu of the parent [PopupMenu] node when clicked. The [param submenu] argument must be the name of an existing [PopupMenu] that has been added as a child to this node. This submenu will be shown when the item is clicked, hovered for long enough, or activated using the [code]ui_select[/code] or [code]ui_right[/code] input actions.
An [param id] can optionally be provided. If no [param id] is provided, one will be created from the index.
</description>
</method>

View File

@ -324,7 +324,8 @@
Changes to this setting will only be applied upon restarting the application.
</member>
<member name="application/run/frame_delay_msec" type="int" setter="" getter="" default="0">
Forces a delay between frames in the main loop (in milliseconds). This may be useful if you plan to disable vertical synchronization.
Forces a [i]constant[/i] delay between frames in the main loop (in milliseconds). In most situations, [member application/run/max_fps] should be preferred as an FPS limiter as it's more precise.
This setting can be overridden using the [code]--frame-delay &lt;ms;&gt;[/code] command line argument.
</member>
<member name="application/run/low_processor_mode" type="bool" setter="" getter="" default="false">
If [code]true[/code], enables low-processor usage mode. This setting only works on desktop platforms. The screen is not redrawn if nothing changes visually. This is meant for writing applications and editors, but is pretty useless (and can hurt performance) in most games.

View File

@ -4416,9 +4416,10 @@
Draw particles in the order that they appear in the particles array.
</constant>
<constant name="PARTICLES_DRAW_ORDER_LIFETIME" value="1" enum="ParticlesDrawOrder">
Sort particles based on their lifetime.
Sort particles based on their lifetime. In other words, the particle with the highest lifetime is drawn at the front.
</constant>
<constant name="PARTICLES_DRAW_ORDER_REVERSE_LIFETIME" value="2" enum="ParticlesDrawOrder">
Sort particles based on the inverse of their lifetime. In other words, the particle with the lowest lifetime is drawn at the front.
</constant>
<constant name="PARTICLES_DRAW_ORDER_VIEW_DEPTH" value="3" enum="ParticlesDrawOrder">
Sort particles based on their distance to the camera.
@ -5032,22 +5033,26 @@
Uses the default filter mode for this [Viewport].
</constant>
<constant name="CANVAS_ITEM_TEXTURE_FILTER_NEAREST" value="1" enum="CanvasItemTextureFilter">
The texture filter reads from the nearest pixel only. The simplest and fastest method of filtering, but the texture will look pixelized.
The texture filter reads from the nearest pixel only. This makes the texture look pixelated from up close, and grainy from a distance (due to mipmaps not being sampled).
</constant>
<constant name="CANVAS_ITEM_TEXTURE_FILTER_LINEAR" value="2" enum="CanvasItemTextureFilter">
The texture filter blends between the nearest 4 pixels. Use this when you want to avoid a pixelated style, but do not want mipmaps.
The texture filter blends between the nearest 4 pixels. This makes the texture look smooth from up close, and grainy from a distance (due to mipmaps not being sampled).
</constant>
<constant name="CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS" value="3" enum="CanvasItemTextureFilter">
The texture filter reads from the nearest pixel in the nearest mipmap. The fastest way to read from textures with mipmaps.
The texture filter reads from the nearest pixel and blends between the nearest 2 mipmaps (or uses the nearest mipmap if [member ProjectSettings.rendering/textures/default_filters/use_nearest_mipmap_filter] is [code]true[/code]). This makes the texture look pixelated from up close, and smooth from a distance.
Use this for non-pixel art textures that may be viewed at a low scale (e.g. due to [Camera2D] zoom or sprite scaling), as mipmaps are important to smooth out pixels that are smaller than on-screen pixels.
</constant>
<constant name="CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS" value="4" enum="CanvasItemTextureFilter">
The texture filter blends between the nearest 4 pixels and between the nearest 2 mipmaps.
The texture filter blends between the nearest 4 pixels and between the nearest 2 mipmaps (or uses the nearest mipmap if [member ProjectSettings.rendering/textures/default_filters/use_nearest_mipmap_filter] is [code]true[/code]). This makes the texture look smooth from up close, and smooth from a distance.
Use this for non-pixel art textures that may be viewed at a low scale (e.g. due to [Camera2D] zoom or sprite scaling), as mipmaps are important to smooth out pixels that are smaller than on-screen pixels.
</constant>
<constant name="CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC" value="5" enum="CanvasItemTextureFilter">
The texture filter reads from the nearest pixel, but selects a mipmap based on the angle between the surface and the camera view. This reduces artifacts on surfaces that are almost in line with the camera.
The texture filter reads from the nearest pixel and blends between 2 mipmaps (or uses the nearest mipmap if [member ProjectSettings.rendering/textures/default_filters/use_nearest_mipmap_filter] is [code]true[/code]) based on the angle between the surface and the camera view. This makes the texture look pixelated from up close, and smooth from a distance. Anisotropic filtering improves texture quality on surfaces that are almost in line with the camera, but is slightly slower. The anisotropic filtering level can be changed by adjusting [member ProjectSettings.rendering/textures/default_filters/anisotropic_filtering_level].
[b]Note:[/b] This texture filter is rarely useful in 2D projects. [constant CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS] is usually more appropriate in this case.
</constant>
<constant name="CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC" value="6" enum="CanvasItemTextureFilter">
The texture filter blends between the nearest 4 pixels and selects a mipmap based on the angle between the surface and the camera view. This reduces artifacts on surfaces that are almost in line with the camera. This is the slowest of the filtering options, but results in the highest quality texturing.
The texture filter blends between the nearest 4 pixels and blends between 2 mipmaps (or uses the nearest mipmap if [member ProjectSettings.rendering/textures/default_filters/use_nearest_mipmap_filter] is [code]true[/code]) based on the angle between the surface and the camera view. This makes the texture look smooth from up close, and smooth from a distance. Anisotropic filtering improves texture quality on surfaces that are almost in line with the camera, but is slightly slower. The anisotropic filtering level can be changed by adjusting [member ProjectSettings.rendering/textures/default_filters/anisotropic_filtering_level].
[b]Note:[/b] This texture filter is rarely useful in 2D projects. [constant CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS] is usually more appropriate in this case.
</constant>
<constant name="CANVAS_ITEM_TEXTURE_FILTER_MAX" value="7" enum="CanvasItemTextureFilter">
Max value for [enum CanvasItemTextureFilter] enum.

View File

@ -6,7 +6,6 @@
<description>
A control used for playback of [VideoStream] resources.
Supported video formats are [url=https://www.theora.org/]Ogg Theora[/url] ([code].ogv[/code], [VideoStreamTheora]) and any format exposed via a GDExtension plugin.
[b]Note:[/b] Due to a bug, VideoStreamPlayer does not support localization remapping yet.
[b]Warning:[/b] On Web, video playback [i]will[/i] perform poorly due to missing architecture-specific assembly optimizations.
</description>
<tutorials>

View File

@ -543,16 +543,18 @@
<constant name="DEBUG_DRAW_MOTION_VECTORS" value="25" enum="DebugDraw">
</constant>
<constant name="DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_NEAREST" value="0" enum="DefaultCanvasItemTextureFilter">
The texture filter reads from the nearest pixel only. The simplest and fastest method of filtering, but the texture will look pixelized.
The texture filter reads from the nearest pixel only. This makes the texture look pixelated from up close, and grainy from a distance (due to mipmaps not being sampled).
</constant>
<constant name="DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_LINEAR" value="1" enum="DefaultCanvasItemTextureFilter">
The texture filter blends between the nearest 4 pixels. Use this when you want to avoid a pixelated style, but do not want mipmaps.
The texture filter blends between the nearest 4 pixels. This makes the texture look smooth from up close, and grainy from a distance (due to mipmaps not being sampled).
</constant>
<constant name="DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS" value="2" enum="DefaultCanvasItemTextureFilter">
The texture filter reads from the nearest pixel in the nearest mipmap. The fastest way to read from textures with mipmaps.
The texture filter blends between the nearest 4 pixels and between the nearest 2 mipmaps (or uses the nearest mipmap if [member ProjectSettings.rendering/textures/default_filters/use_nearest_mipmap_filter] is [code]true[/code]). This makes the texture look smooth from up close, and smooth from a distance.
Use this for non-pixel art textures that may be viewed at a low scale (e.g. due to [Camera2D] zoom or sprite scaling), as mipmaps are important to smooth out pixels that are smaller than on-screen pixels.
</constant>
<constant name="DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS" value="3" enum="DefaultCanvasItemTextureFilter">
The texture filter blends between the nearest 4 pixels and between the nearest 2 mipmaps.
The texture filter reads from the nearest pixel and blends between the nearest 2 mipmaps (or uses the nearest mipmap if [member ProjectSettings.rendering/textures/default_filters/use_nearest_mipmap_filter] is [code]true[/code]). This makes the texture look pixelated from up close, and smooth from a distance.
Use this for non-pixel art textures that may be viewed at a low scale (e.g. due to [Camera2D] zoom or sprite scaling), as mipmaps are important to smooth out pixels that are smaller than on-screen pixels.
</constant>
<constant name="DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_MAX" value="4" enum="DefaultCanvasItemTextureFilter">
Max value for [enum DefaultCanvasItemTextureFilter] enum.

View File

@ -57,24 +57,26 @@
Sample the texture using the filter determined by the node this shader is attached to.
</constant>
<constant name="FILTER_NEAREST" value="1" enum="TextureFilter">
The texture filter reads from the nearest pixel only. The simplest and fastest method of filtering, but the texture will look pixelized.
The texture filter reads from the nearest pixel only. This makes the texture look pixelated from up close, and grainy from a distance (due to mipmaps not being sampled).
</constant>
<constant name="FILTER_LINEAR" value="2" enum="TextureFilter">
The texture filter blends between the nearest four pixels. Use this for most cases where you want to avoid a pixelated style.
The texture filter blends between the nearest 4 pixels. This makes the texture look smooth from up close, and grainy from a distance (due to mipmaps not being sampled).
</constant>
<constant name="FILTER_NEAREST_MIPMAP" value="3" enum="TextureFilter">
The texture filter reads from the nearest pixel in the nearest mipmap. This is the fastest way to read from textures with mipmaps.
The texture filter reads from the nearest pixel and blends between the nearest 2 mipmaps (or uses the nearest mipmap if [member ProjectSettings.rendering/textures/default_filters/use_nearest_mipmap_filter] is [code]true[/code]). This makes the texture look pixelated from up close, and smooth from a distance.
Use this for non-pixel art textures that may be viewed at a low scale (e.g. due to [Camera2D] zoom or sprite scaling), as mipmaps are important to smooth out pixels that are smaller than on-screen pixels.
</constant>
<constant name="FILTER_LINEAR_MIPMAP" value="4" enum="TextureFilter">
The texture filter blends between the nearest 4 pixels and between the nearest 2 mipmaps. Use this for non-pixel art textures that may be viewed at a low scale (e.g. due to [Camera2D] zoom), as mipmaps are important to smooth out pixels that are smaller than on-screen pixels.
The texture filter blends between the nearest 4 pixels and between the nearest 2 mipmaps (or uses the nearest mipmap if [member ProjectSettings.rendering/textures/default_filters/use_nearest_mipmap_filter] is [code]true[/code]). This makes the texture look smooth from up close, and smooth from a distance.
Use this for non-pixel art textures that may be viewed at a low scale (e.g. due to [Camera2D] zoom or sprite scaling), as mipmaps are important to smooth out pixels that are smaller than on-screen pixels.
</constant>
<constant name="FILTER_NEAREST_MIPMAP_ANISOTROPIC" value="5" enum="TextureFilter">
The texture filter reads from the nearest pixel, but selects a mipmap based on the angle between the surface and the camera view. This reduces artifacts on surfaces that are almost in line with the camera. The anisotropic filtering level can be changed by adjusting [member ProjectSettings.rendering/textures/default_filters/anisotropic_filtering_level].
[b]Note:[/b] This texture filter is rarely useful in 2D projects. [constant FILTER_LINEAR_MIPMAP] is usually more appropriate.
The texture filter reads from the nearest pixel and blends between 2 mipmaps (or uses the nearest mipmap if [member ProjectSettings.rendering/textures/default_filters/use_nearest_mipmap_filter] is [code]true[/code]) based on the angle between the surface and the camera view. This makes the texture look pixelated from up close, and smooth from a distance. Anisotropic filtering improves texture quality on surfaces that are almost in line with the camera, but is slightly slower. The anisotropic filtering level can be changed by adjusting [member ProjectSettings.rendering/textures/default_filters/anisotropic_filtering_level].
[b]Note:[/b] This texture filter is rarely useful in 2D projects. [constant FILTER_NEAREST_MIPMAP] is usually more appropriate in this case.
</constant>
<constant name="FILTER_LINEAR_MIPMAP_ANISOTROPIC" value="6" enum="TextureFilter">
The texture filter blends between the nearest 4 pixels and selects a mipmap based on the angle between the surface and the camera view. This reduces artifacts on surfaces that are almost in line with the camera. This is the slowest of the filtering options, but results in the highest quality texturing. The anisotropic filtering level can be changed by adjusting [member ProjectSettings.rendering/textures/default_filters/anisotropic_filtering_level].
[b]Note:[/b] This texture filter is rarely useful in 2D projects. [constant FILTER_LINEAR_MIPMAP] is usually more appropriate.
The texture filter blends between the nearest 4 pixels and blends between 2 mipmaps (or uses the nearest mipmap if [member ProjectSettings.rendering/textures/default_filters/use_nearest_mipmap_filter] is [code]true[/code]) based on the angle between the surface and the camera view. This makes the texture look smooth from up close, and smooth from a distance. Anisotropic filtering improves texture quality on surfaces that are almost in line with the camera, but is slightly slower. The anisotropic filtering level can be changed by adjusting [member ProjectSettings.rendering/textures/default_filters/anisotropic_filtering_level].
[b]Note:[/b] This texture filter is rarely useful in 2D projects. [constant FILTER_LINEAR_MIPMAP] is usually more appropriate in this case.
</constant>
<constant name="FILTER_MAX" value="7" enum="TextureFilter">
Represents the size of the [enum TextureFilter] enum.

View File

@ -660,6 +660,11 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou
state.current_tex = RID();
for (uint32_t i = 0; i <= state.current_batch_index; i++) {
// Skipping when there is no instances.
if (state.canvas_instance_batches[i].instance_count == 0) {
continue;
}
//setup clip
if (current_clip != state.canvas_instance_batches[i].clip) {
current_clip = state.canvas_instance_batches[i].clip;

View File

@ -1734,7 +1734,7 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
RenderDataGLES3 render_data;
{
render_data.render_buffers = rb;
render_data.transparent_bg = rb.is_valid() ? rb->is_transparent : false;
render_data.transparent_bg = rb.is_valid() ? rt->is_transparent : false;
// Our first camera is used by default
render_data.cam_transform = p_camera_data->main_transform;
render_data.inv_cam_transform = render_data.cam_transform.affine_inverse();
@ -1980,6 +1980,7 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
}
if (!keep_color) {
clear_color.a = render_data.transparent_bg ? 0.0f : 1.0f;
glClearBufferfv(GL_COLOR, 0, clear_color.components);
}
RENDER_TIMESTAMP("Render Opaque Pass");

View File

@ -38,8 +38,6 @@ RenderSceneBuffersGLES3::~RenderSceneBuffersGLES3() {
}
void RenderSceneBuffersGLES3::configure(RID p_render_target, const Size2i p_internal_size, const Size2i p_target_size, RS::ViewportScaling3DMode p_scaling_3d_mode, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count) {
GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
//internal_size.x = p_internal_size.x; // ignore for now
//internal_size.y = p_internal_size.y;
width = p_target_size.x;
@ -54,10 +52,6 @@ void RenderSceneBuffersGLES3::configure(RID p_render_target, const Size2i p_inte
view_count = p_view_count;
free_render_buffer_data();
GLES3::RenderTarget *rt = texture_storage->get_render_target(p_render_target);
is_transparent = rt->is_transparent;
}
void RenderSceneBuffersGLES3::free_render_buffer_data() {

View File

@ -58,8 +58,6 @@ public:
//bool use_debanding = false;
uint32_t view_count = 1;
bool is_transparent = false;
RID render_target;
//built-in textures used for ping pong image processing and blurring

View File

@ -59,8 +59,6 @@ static const GLenum _cube_side_enum[6] = {
TextureStorage::TextureStorage() {
singleton = this;
system_fbo = 0;
{ //create default textures
{ // White Textures
@ -709,18 +707,20 @@ void TextureStorage::texture_free(RID p_texture) {
memdelete(t->canvas_texture);
}
if (t->tex_id != 0) {
if (!t->is_external) {
GLES3::Utilities::get_singleton()->texture_free_data(t->tex_id);
bool must_free_data = false;
if (t->is_proxy) {
if (t->proxy_to.is_valid()) {
Texture *proxy_to = texture_owner.get_or_null(t->proxy_to);
if (proxy_to) {
proxy_to->proxies.erase(p_texture);
}
}
t->tex_id = 0;
} else {
must_free_data = t->tex_id != 0 && !t->is_external;
}
if (t->is_proxy && t->proxy_to.is_valid()) {
Texture *proxy_to = texture_owner.get_or_null(t->proxy_to);
if (proxy_to) {
proxy_to->proxies.erase(p_texture);
}
if (must_free_data) {
GLES3::Utilities::get_singleton()->texture_free_data(t->tex_id);
t->tex_id = 0;
}
texture_atlas_remove_texture(p_texture);

View File

@ -2694,14 +2694,21 @@ void AnimationTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
AnimationPlayer *ap = ape->get_player();
if (ap) {
NodePath npath = animation->track_get_path(track);
Node *nd = ap->get_node(ap->get_root())->get_node(NodePath(npath.get_concatenated_names()));
StringName prop = npath.get_concatenated_subnames();
PropertyInfo prop_info;
ClassDB::get_property_info(nd->get_class(), prop, &prop_info);
bool is_angle = prop_info.type == Variant::FLOAT && prop_info.hint_string.find("radians") != -1;
if (is_angle) {
menu->add_icon_item(get_theme_icon(SNAME("InterpLinearAngle"), SNAME("EditorIcons")), TTR("Linear Angle"), MENU_INTERPOLATION_LINEAR_ANGLE);
menu->add_icon_item(get_theme_icon(SNAME("InterpCubicAngle"), SNAME("EditorIcons")), TTR("Cubic Angle"), MENU_INTERPOLATION_CUBIC_ANGLE);
Node *a_ap_root_node = ap->get_node(ap->get_root());
Node *nd = nullptr;
// We must test that we have a valid a_ap_root_node before trying to access its content to init the nd Node.
if (a_ap_root_node) {
nd = a_ap_root_node->get_node(NodePath(npath.get_concatenated_names()));
}
if (nd) {
StringName prop = npath.get_concatenated_subnames();
PropertyInfo prop_info;
ClassDB::get_property_info(nd->get_class(), prop, &prop_info);
bool is_angle = prop_info.type == Variant::FLOAT && prop_info.hint_string.find("radians") != -1;
if (is_angle) {
menu->add_icon_item(get_theme_icon(SNAME("InterpLinearAngle"), SNAME("EditorIcons")), TTR("Linear Angle"), MENU_INTERPOLATION_LINEAR_ANGLE);
menu->add_icon_item(get_theme_icon(SNAME("InterpCubicAngle"), SNAME("EditorIcons")), TTR("Cubic Angle"), MENU_INTERPOLATION_CUBIC_ANGLE);
}
}
}
}

View File

@ -367,6 +367,9 @@ void FindReplaceBar::_update_results_count() {
int col_pos = 0;
bool searched_start_is_symbol = is_symbol(searched[0]);
bool searched_end_is_symbol = is_symbol(searched[searched.length() - 1]);
while (true) {
col_pos = is_case_sensitive() ? line_text.find(searched, col_pos) : line_text.findn(searched, col_pos);
@ -375,11 +378,11 @@ void FindReplaceBar::_update_results_count() {
}
if (is_whole_words()) {
if (col_pos > 0 && !is_symbol(line_text[col_pos - 1])) {
if (!searched_start_is_symbol && col_pos > 0 && !is_symbol(line_text[col_pos - 1])) {
col_pos += searched.length();
continue;
}
if (col_pos + searched.length() < line_text.length() && !is_symbol(line_text[col_pos + searched.length()])) {
if (!searched_end_is_symbol && col_pos + searched.length() < line_text.length() && !is_symbol(line_text[col_pos + searched.length()])) {
col_pos += searched.length();
continue;
}

View File

@ -338,8 +338,7 @@ void EditorAutoloadSettings::_autoload_button_pressed(Object *p_item, int p_colu
undo_redo->add_do_property(ProjectSettings::get_singleton(), name, Variant());
undo_redo->add_undo_property(ProjectSettings::get_singleton(), name, GLOBAL_GET(name));
undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set_persisting", name, true);
undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set_order", order);
undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set_order", name, order);
undo_redo->add_do_method(this, "update_autoload");
undo_redo->add_undo_method(this, "update_autoload");
@ -795,8 +794,7 @@ void EditorAutoloadSettings::autoload_remove(const String &p_name) {
undo_redo->add_do_property(ProjectSettings::get_singleton(), name, Variant());
undo_redo->add_undo_property(ProjectSettings::get_singleton(), name, GLOBAL_GET(name));
undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set_persisting", name, true);
undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set_order", order);
undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set_order", name, order);
undo_redo->add_do_method(this, "update_autoload");
undo_redo->add_undo_method(this, "update_autoload");

View File

@ -854,12 +854,12 @@ void EditorNode::_on_plugin_ready(Object *p_script, const String &p_activate_nam
if (scr.is_null()) {
return;
}
if (p_activate_name.length()) {
set_addon_plugin_enabled(p_activate_name, true);
}
project_settings_editor->update_plugins();
project_settings_editor->hide();
push_item(scr.operator->());
if (p_activate_name.length()) {
set_addon_plugin_enabled(p_activate_name, true);
}
}
void EditorNode::_remove_plugin_from_enabled(const String &p_name) {
@ -6690,7 +6690,7 @@ static void _execute_thread(void *p_ud) {
eta->done.set();
}
int EditorNode::execute_and_show_output(const String &p_title, const String &p_path, const List<String> &p_arguments, bool p_close_on_ok, bool p_close_on_errors) {
int EditorNode::execute_and_show_output(const String &p_title, const String &p_path, const List<String> &p_arguments, bool p_close_on_ok, bool p_close_on_errors, String *r_output) {
if (execute_output_dialog) {
execute_output_dialog->set_title(p_title);
execute_output_dialog->get_ok_button()->set_disabled(true);
@ -6736,6 +6736,9 @@ int EditorNode::execute_and_show_output(const String &p_title, const String &p_p
execute_output_dialog->get_ok_button()->set_disabled(false);
}
if (r_output) {
*r_output = eta.output;
}
return eta.exitcode;
}
@ -7443,10 +7446,6 @@ EditorNode::EditorNode() {
project_menu->add_item(TTR("Customize Engine Build Configuration..."), TOOLS_BUILD_PROFILE_MANAGER);
project_menu->add_separator();
plugin_config_dialog = memnew(PluginConfigDialog);
plugin_config_dialog->connect("plugin_ready", callable_mp(this, &EditorNode::_on_plugin_ready));
gui_base->add_child(plugin_config_dialog);
tool_menu = memnew(PopupMenu);
tool_menu->set_name("Tools");
tool_menu->connect("index_pressed", callable_mp(this, &EditorNode::_tool_menu_option));

View File

@ -357,8 +357,6 @@ private:
Timer *screenshot_timer = nullptr;
PluginConfigDialog *plugin_config_dialog = nullptr;
RichTextLabel *load_errors = nullptr;
AcceptDialog *load_error_dialog = nullptr;
@ -934,7 +932,7 @@ public:
bool has_scenes_in_session();
int execute_and_show_output(const String &p_title, const String &p_path, const List<String> &p_arguments, bool p_close_on_ok = true, bool p_close_on_errors = false);
int execute_and_show_output(const String &p_title, const String &p_path, const List<String> &p_arguments, bool p_close_on_ok = true, bool p_close_on_errors = false, String *r_output = nullptr);
EditorNode();
~EditorNode();

View File

@ -3664,7 +3664,7 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
case Variant::VECTOR2I: {
EditorPropertyVector2i *editor = memnew(EditorPropertyVector2i(p_wide));
EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1, true);
editor->setup(hint.min, hint.max, 1, true, p_hint == PROPERTY_HINT_LINK, hint.suffix);
editor->setup(hint.min, hint.max, 1, false, p_hint == PROPERTY_HINT_LINK, hint.suffix);
return editor;
} break;
@ -3691,7 +3691,7 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
case Variant::VECTOR3I: {
EditorPropertyVector3i *editor = memnew(EditorPropertyVector3i(p_wide));
EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1, true);
editor->setup(hint.min, hint.max, 1, true, p_hint == PROPERTY_HINT_LINK, hint.suffix);
editor->setup(hint.min, hint.max, 1, false, p_hint == PROPERTY_HINT_LINK, hint.suffix);
return editor;
} break;
@ -3705,7 +3705,7 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
case Variant::VECTOR4I: {
EditorPropertyVector4i *editor = memnew(EditorPropertyVector4i);
EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1, true);
editor->setup(hint.min, hint.max, 1, true, p_hint == PROPERTY_HINT_LINK, hint.suffix);
editor->setup(hint.min, hint.max, 1, false, p_hint == PROPERTY_HINT_LINK, hint.suffix);
return editor;
} break;

View File

@ -1631,7 +1631,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
if (increase_scrollbar_touch_area) {
theme->set_stylebox("scroll", "HScrollBar", make_line_stylebox(separator_color, 50));
} else {
theme->set_stylebox("scroll", "HScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollBg"), SNAME("EditorIcons")), 5, 5, 5, 5, 1, 1, 1, 1));
theme->set_stylebox("scroll", "HScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollBg"), SNAME("EditorIcons")), 5, 5, 5, 5, -5, 1, -5, 1));
}
theme->set_stylebox("scroll_focus", "HScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollBg"), SNAME("EditorIcons")), 5, 5, 5, 5, 1, 1, 1, 1));
theme->set_stylebox("grabber", "HScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabber"), SNAME("EditorIcons")), 6, 6, 6, 6, 1, 1, 1, 1));
@ -1649,7 +1649,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
if (increase_scrollbar_touch_area) {
theme->set_stylebox("scroll", "VScrollBar", make_line_stylebox(separator_color, 50, 1, 1, true));
} else {
theme->set_stylebox("scroll", "VScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollBg"), SNAME("EditorIcons")), 5, 5, 5, 5, 1, 1, 1, 1));
theme->set_stylebox("scroll", "VScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollBg"), SNAME("EditorIcons")), 5, 5, 5, 5, 1, -5, 1, -5));
}
theme->set_stylebox("scroll_focus", "VScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollBg"), SNAME("EditorIcons")), 5, 5, 5, 5, 1, 1, 1, 1));
theme->set_stylebox("grabber", "VScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabber"), SNAME("EditorIcons")), 6, 6, 6, 6, 1, 1, 1, 1));

View File

@ -1421,7 +1421,7 @@ void EditorExportPlatform::zip_folder_recursive(zipFile &p_zip, const String &p_
nullptr,
0,
0x0314, // "version made by", 0x03 - Unix, 0x14 - ZIP specification version 2.0, required to store Unix file permissions
0);
1 << 11); // Bit 11 is the language encoding flag. When set, filename and comment fields must be encoded using UTF-8.
String target = da->read_link(f);
zipWriteInFileInZip(p_zip, target.utf8().get_data(), target.utf8().size());
@ -1465,7 +1465,7 @@ void EditorExportPlatform::zip_folder_recursive(zipFile &p_zip, const String &p_
nullptr,
0,
0x0314, // "version made by", 0x03 - Unix, 0x14 - ZIP specification version 2.0, required to store Unix file permissions
0);
1 << 11); // Bit 11 is the language encoding flag. When set, filename and comment fields must be encoded using UTF-8.
Ref<FileAccess> fa = FileAccess::open(dir.path_join(f), FileAccess::READ);
if (fa.is_null()) {

View File

@ -242,7 +242,7 @@ void ProjectExportDialog::_edit_preset(int p_index) {
export_filter->select(current->get_export_filter());
include_filters->set_text(current->get_include_filter());
include_label->set_text(current->get_export_filter() == EditorExportPreset::EXCLUDE_SELECTED_RESOURCES ? TTR("Resources to exclude:") : TTR("Resources to export:"));
include_label->set_text(_get_resource_export_header(current->get_export_filter()));
exclude_filters->set_text(current->get_exclude_filter());
server_strip_message->set_visible(current->get_export_filter() == EditorExportPreset::EXPORT_CUSTOMIZED);
@ -703,13 +703,24 @@ void ProjectExportDialog::_export_type_changed(int p_which) {
if (filter_type == EditorExportPreset::EXPORT_CUSTOMIZED && current->get_customized_files_count() == 0) {
current->set_file_export_mode("res://", EditorExportPreset::MODE_FILE_STRIP);
}
include_label->set_text(current->get_export_filter() == EditorExportPreset::EXCLUDE_SELECTED_RESOURCES ? TTR("Resources to exclude:") : TTR("Resources to export:"));
include_label->set_text(_get_resource_export_header(current->get_export_filter()));
updating = true;
_fill_resource_tree();
updating = false;
}
String ProjectExportDialog::_get_resource_export_header(EditorExportPreset::ExportFilter p_filter) const {
switch (p_filter) {
case EditorExportPreset::EXCLUDE_SELECTED_RESOURCES:
return TTR("Resources to exclude:");
case EditorExportPreset::EXPORT_CUSTOMIZED:
return TTR("Resources to override export behavior:");
default:
return TTR("Resources to export:");
}
}
void ProjectExportDialog::_filter_changed(const String &p_filter) {
if (updating) {
return;

View File

@ -115,6 +115,7 @@ private:
void _export_type_changed(int p_which);
void _filter_changed(const String &p_filter);
String _get_resource_export_header(EditorExportPreset::ExportFilter p_filter) const;
void _fill_resource_tree();
void _setup_item_for_file_mode(TreeItem *p_item, EditorExportPreset::FileExportMode p_mode);
bool _fill_tree(EditorFileSystemDirectory *p_dir, TreeItem *p_item, Ref<EditorExportPreset> &current, EditorExportPreset::ExportFilter p_export_filter);

View File

@ -1507,6 +1507,10 @@ void AnimationNodeStateMachineEditor::_name_edited(const String &p_text) {
int base = 1;
String name = base_name;
while (state_machine->has_node(name)) {
if (name == prev_name) {
name_edit_popup->hide(); // The old name wins, the name doesn't change, just hide the popup.
return;
}
base++;
name = base_name + " " + itos(base);
}

View File

@ -533,6 +533,11 @@ void BoneMapper::_clear_mapping_current_group() {
}
#ifdef MODULE_REGEX_ENABLED
bool BoneMapper::is_match_with_bone_name(String p_bone_name, String p_word) {
RegEx re = RegEx(p_word);
return !re.search(p_bone_name.to_lower()).is_null();
}
int BoneMapper::search_bone_by_name(Skeleton3D *p_skeleton, Vector<String> p_picklist, BoneSegregation p_segregation, int p_parent, int p_child, int p_children_count) {
// There may be multiple candidates hit by existing the subsidiary bone.
// The one with the shortest name is probably the original.
@ -540,7 +545,6 @@ int BoneMapper::search_bone_by_name(Skeleton3D *p_skeleton, Vector<String> p_pic
String shortest = "";
for (int word_idx = 0; word_idx < p_picklist.size(); word_idx++) {
RegEx re = RegEx(p_picklist[word_idx]);
if (p_child == -1) {
Vector<int> bones_to_process = p_parent == -1 ? p_skeleton->get_parentless_bones() : p_skeleton->get_bone_children(p_parent);
while (bones_to_process.size() > 0) {
@ -559,7 +563,7 @@ int BoneMapper::search_bone_by_name(Skeleton3D *p_skeleton, Vector<String> p_pic
}
String bn = skeleton->get_bone_name(idx);
if (!re.search(bn.to_lower()).is_null() && guess_bone_segregation(bn) == p_segregation) {
if (is_match_with_bone_name(bn, p_picklist[word_idx]) && guess_bone_segregation(bn) == p_segregation) {
hit_list.push_back(bn);
}
}
@ -584,7 +588,7 @@ int BoneMapper::search_bone_by_name(Skeleton3D *p_skeleton, Vector<String> p_pic
}
String bn = skeleton->get_bone_name(idx);
if (!re.search(bn.to_lower()).is_null() && guess_bone_segregation(bn) == p_segregation) {
if (is_match_with_bone_name(bn, p_picklist[word_idx]) && guess_bone_segregation(bn) == p_segregation) {
hit_list.push_back(bn);
}
idx = skeleton->get_bone_parent(idx);
@ -654,6 +658,7 @@ void BoneMapper::auto_mapping_process(Ref<BoneMap> &p_bone_map) {
picklist.push_back("pelvis");
picklist.push_back("waist");
picklist.push_back("torso");
picklist.push_back("spine");
int hips = search_bone_by_name(skeleton, picklist);
if (hips == -1) {
WARN_PRINT("Auto Mapping couldn't guess Hips. Abort auto mapping.");
@ -704,70 +709,7 @@ void BoneMapper::auto_mapping_process(Ref<BoneMap> &p_bone_map) {
bone_idx = -1;
search_path.clear();
// 3. Guess Neck
picklist.push_back("neck");
picklist.push_back("head"); // For no neck model.
picklist.push_back("face"); // Same above.
int neck = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_NONE, hips);
picklist.clear();
// 4. Guess Head
picklist.push_back("head");
picklist.push_back("face");
int head = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_NONE, neck);
if (head == -1) {
search_path = skeleton->get_bone_children(neck);
if (search_path.size() == 1) {
head = search_path[0]; // Maybe only one child of the Neck is Head.
}
}
if (head == -1) {
if (neck != -1) {
head = neck; // The head animation should have more movement.
neck = -1;
p_bone_map->_set_skeleton_bone_name("Head", skeleton->get_bone_name(head));
} else {
WARN_PRINT("Auto Mapping couldn't guess Neck or Head."); // Continued for guessing on the other bones. But abort when guessing spines step.
}
} else {
p_bone_map->_set_skeleton_bone_name("Neck", skeleton->get_bone_name(neck));
p_bone_map->_set_skeleton_bone_name("Head", skeleton->get_bone_name(head));
}
picklist.clear();
search_path.clear();
int neck_or_head = neck != -1 ? neck : (head != -1 ? head : -1);
if (neck_or_head != -1) {
// 4-1. Guess Eyes
picklist.push_back("eye(?!.*(brow|lash|lid))");
bone_idx = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_LEFT, neck_or_head);
if (bone_idx == -1) {
WARN_PRINT("Auto Mapping couldn't guess LeftEye.");
} else {
p_bone_map->_set_skeleton_bone_name("LeftEye", skeleton->get_bone_name(bone_idx));
}
bone_idx = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_RIGHT, neck_or_head);
if (bone_idx == -1) {
WARN_PRINT("Auto Mapping couldn't guess RightEye.");
} else {
p_bone_map->_set_skeleton_bone_name("RightEye", skeleton->get_bone_name(bone_idx));
}
picklist.clear();
// 4-2. Guess Jaw
picklist.push_back("jaw");
bone_idx = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_NONE, neck_or_head);
if (bone_idx == -1) {
WARN_PRINT("Auto Mapping couldn't guess Jaw.");
} else {
p_bone_map->_set_skeleton_bone_name("Jaw", skeleton->get_bone_name(bone_idx));
}
bone_idx = -1;
picklist.clear();
}
// 5. Guess Foots
// 3. Guess Foots
picklist.push_back("foot");
picklist.push_back("ankle");
int left_foot = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_LEFT, hips);
@ -784,7 +726,7 @@ void BoneMapper::auto_mapping_process(Ref<BoneMap> &p_bone_map) {
}
picklist.clear();
// 5-1. Guess LowerLegs
// 3-1. Guess LowerLegs
picklist.push_back("(low|under).*leg");
picklist.push_back("knee");
picklist.push_back("shin");
@ -810,7 +752,7 @@ void BoneMapper::auto_mapping_process(Ref<BoneMap> &p_bone_map) {
}
picklist.clear();
// 5-2. Guess UpperLegs
// 3-2. Guess UpperLegs
picklist.push_back("up.*leg");
picklist.push_back("thigh");
picklist.push_back("leg");
@ -834,7 +776,7 @@ void BoneMapper::auto_mapping_process(Ref<BoneMap> &p_bone_map) {
bone_idx = -1;
picklist.clear();
// 5-3. Guess Toes
// 3-3. Guess Toes
picklist.push_back("toe");
picklist.push_back("ball");
if (left_foot != -1) {
@ -871,7 +813,7 @@ void BoneMapper::auto_mapping_process(Ref<BoneMap> &p_bone_map) {
bone_idx = -1;
picklist.clear();
// 6. Guess Hands
// 4. Guess Hands
picklist.push_back("hand");
picklist.push_back("wrist");
picklist.push_back("palm");
@ -916,7 +858,7 @@ void BoneMapper::auto_mapping_process(Ref<BoneMap> &p_bone_map) {
bone_idx = -1;
picklist.clear();
// 6-1. Guess Finger
// 4-1. Guess Finger
bool named_finger_is_found = false;
LocalVector<String> fingers;
fingers.push_back("thumb|pollex");
@ -1106,7 +1048,7 @@ void BoneMapper::auto_mapping_process(Ref<BoneMap> &p_bone_map) {
}
}
// 7. Guess Arms
// 5. Guess Arms
picklist.push_back("shoulder");
picklist.push_back("clavicle");
picklist.push_back("collar");
@ -1124,7 +1066,7 @@ void BoneMapper::auto_mapping_process(Ref<BoneMap> &p_bone_map) {
}
picklist.clear();
// 7-1. Guess LowerArms
// 5-1. Guess LowerArms
picklist.push_back("(low|fore).*arm");
picklist.push_back("elbow");
picklist.push_back("arm");
@ -1148,7 +1090,7 @@ void BoneMapper::auto_mapping_process(Ref<BoneMap> &p_bone_map) {
}
picklist.clear();
// 7-2. Guess UpperArms
// 5-2. Guess UpperArms
picklist.push_back("up.*arm");
picklist.push_back("arm");
if (left_shoulder != -1 && left_lower_arm != -1) {
@ -1171,6 +1113,99 @@ void BoneMapper::auto_mapping_process(Ref<BoneMap> &p_bone_map) {
bone_idx = -1;
picklist.clear();
// 6. Guess Neck
picklist.push_back("neck");
picklist.push_back("head"); // For no neck model.
picklist.push_back("face"); // Same above.
int neck = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_NONE, hips);
picklist.clear();
if (neck == -1) {
// If it can't expect by name, search child spine of where the right and left shoulders (or hands) cross.
int ls_idx = left_shoulder != -1 ? left_shoulder : (left_hand_or_palm != -1 ? left_hand_or_palm : -1);
int rs_idx = right_shoulder != -1 ? right_shoulder : (right_hand_or_palm != -1 ? right_hand_or_palm : -1);
if (ls_idx != -1 && rs_idx != -1) {
bool detect = false;
while (ls_idx != hips && ls_idx >= 0 && rs_idx != hips && rs_idx >= 0) {
ls_idx = skeleton->get_bone_parent(ls_idx);
rs_idx = skeleton->get_bone_parent(rs_idx);
if (ls_idx == rs_idx) {
detect = true;
break;
}
}
if (detect) {
Vector<int> children = skeleton->get_bone_children(ls_idx);
children.erase(ls_idx);
children.erase(rs_idx);
String word = "spine"; // It would be better to limit the search with "spine" because it could be mistaken with breast, wing and etc...
for (int i = 0; children.size(); i++) {
bone_idx = children[i];
if (is_match_with_bone_name(skeleton->get_bone_name(bone_idx), word)) {
neck = bone_idx;
break;
};
}
bone_idx = -1;
}
}
}
// 7. Guess Head
picklist.push_back("head");
picklist.push_back("face");
int head = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_NONE, neck);
if (head == -1) {
search_path = skeleton->get_bone_children(neck);
if (search_path.size() == 1) {
head = search_path[0]; // Maybe only one child of the Neck is Head.
}
}
if (head == -1) {
if (neck != -1) {
head = neck; // The head animation should have more movement.
neck = -1;
p_bone_map->_set_skeleton_bone_name("Head", skeleton->get_bone_name(head));
} else {
WARN_PRINT("Auto Mapping couldn't guess Neck or Head."); // Continued for guessing on the other bones. But abort when guessing spines step.
}
} else {
p_bone_map->_set_skeleton_bone_name("Neck", skeleton->get_bone_name(neck));
p_bone_map->_set_skeleton_bone_name("Head", skeleton->get_bone_name(head));
}
picklist.clear();
search_path.clear();
int neck_or_head = neck != -1 ? neck : (head != -1 ? head : -1);
if (neck_or_head != -1) {
// 7-1. Guess Eyes
picklist.push_back("eye(?!.*(brow|lash|lid))");
bone_idx = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_LEFT, neck_or_head);
if (bone_idx == -1) {
WARN_PRINT("Auto Mapping couldn't guess LeftEye.");
} else {
p_bone_map->_set_skeleton_bone_name("LeftEye", skeleton->get_bone_name(bone_idx));
}
bone_idx = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_RIGHT, neck_or_head);
if (bone_idx == -1) {
WARN_PRINT("Auto Mapping couldn't guess RightEye.");
} else {
p_bone_map->_set_skeleton_bone_name("RightEye", skeleton->get_bone_name(bone_idx));
}
picklist.clear();
// 7-2. Guess Jaw
picklist.push_back("jaw");
bone_idx = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_NONE, neck_or_head);
if (bone_idx == -1) {
WARN_PRINT("Auto Mapping couldn't guess Jaw.");
} else {
p_bone_map->_set_skeleton_bone_name("Jaw", skeleton->get_bone_name(bone_idx));
}
bone_idx = -1;
picklist.clear();
}
// 8. Guess UpperChest or Chest
if (neck_or_head == -1) {
return; // Abort.

View File

@ -179,6 +179,7 @@ class BoneMapper : public VBoxContainer {
BONE_SEGREGATION_LEFT,
BONE_SEGREGATION_RIGHT
};
bool is_match_with_bone_name(String p_bone_name, String p_word);
int search_bone_by_name(Skeleton3D *p_skeleton, Vector<String> p_picklist, BoneSegregation p_segregation = BONE_SEGREGATION_NONE, int p_parent = -1, int p_child = -1, int p_children_count = -1);
BoneSegregation guess_bone_segregation(String p_bone_name);
void auto_mapping_process(Ref<BoneMap> &p_bone_map);

View File

@ -456,6 +456,7 @@ void Polygon2DEditor::_uv_mode(int p_mode) {
for (int i = 0; i < UV_MODE_MAX; i++) {
uv_button[i]->set_pressed(p_mode == i);
}
uv_edit_draw->queue_redraw();
}
void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
@ -980,9 +981,36 @@ void Polygon2DEditor::_uv_draw() {
mtx.columns[2] = -uv_draw_ofs;
mtx.scale_basis(Vector2(uv_draw_zoom, uv_draw_zoom));
RS::get_singleton()->canvas_item_add_set_transform(uv_edit_draw->get_canvas_item(), mtx);
uv_edit_draw->draw_texture(base_tex, Point2());
RS::get_singleton()->canvas_item_add_set_transform(uv_edit_draw->get_canvas_item(), Transform2D());
// Draw texture as a background if editing uvs or no uv mapping exist.
if (uv_edit_mode[0]->is_pressed() || uv_mode == UV_MODE_CREATE || node->get_polygon().is_empty() || node->get_uv().size() != node->get_polygon().size()) {
Transform2D texture_transform = Transform2D(node->get_texture_rotation(), node->get_texture_offset());
texture_transform.scale(node->get_texture_scale());
texture_transform.affine_invert();
RS::get_singleton()->canvas_item_add_set_transform(uv_edit_draw->get_canvas_item(), mtx * texture_transform);
uv_edit_draw->draw_texture(base_tex, Point2());
RS::get_singleton()->canvas_item_add_set_transform(uv_edit_draw->get_canvas_item(), Transform2D());
preview_polygon->hide();
} else {
preview_polygon->set_transform(mtx);
// Keep in sync with newly added Polygon2D properties (when relevant).
preview_polygon->set_texture(node->get_texture());
preview_polygon->set_texture_offset(node->get_texture_offset());
preview_polygon->set_texture_rotation(node->get_texture_rotation());
preview_polygon->set_texture_scale(node->get_texture_scale());
preview_polygon->set_texture_filter(node->get_texture_filter_in_tree());
preview_polygon->set_texture_repeat(node->get_texture_repeat_in_tree());
preview_polygon->set_polygon(node->get_polygon());
preview_polygon->set_uv(node->get_uv());
preview_polygon->set_invert(node->get_invert());
preview_polygon->set_invert_border(node->get_invert_border());
preview_polygon->set_internal_vertex_count(node->get_internal_vertex_count());
if (uv_mode == UV_MODE_ADD_POLYGON) {
preview_polygon->set_polygons(Array());
} else {
preview_polygon->set_polygons(node->get_polygons());
}
preview_polygon->show();
}
if (snap_show_grid) {
Color grid_color = Color(1.0, 1.0, 1.0, 0.15);
@ -1347,10 +1375,19 @@ Polygon2DEditor::Polygon2DEditor() {
HSplitContainer *uv_main_hsc = memnew(HSplitContainer);
uv_main_vb->add_child(uv_main_hsc);
uv_main_hsc->set_v_size_flags(SIZE_EXPAND_FILL);
uv_edit_draw = memnew(Panel);
uv_main_hsc->add_child(uv_edit_draw);
uv_edit_draw->set_h_size_flags(SIZE_EXPAND_FILL);
uv_edit_draw->set_custom_minimum_size(Size2(200, 200) * EDSCALE);
uv_edit_background = memnew(Panel);
uv_main_hsc->add_child(uv_edit_background);
uv_edit_background->set_h_size_flags(SIZE_EXPAND_FILL);
uv_edit_background->set_custom_minimum_size(Size2(200, 200) * EDSCALE);
uv_edit_background->set_clip_contents(true);
preview_polygon = memnew(Polygon2D);
uv_edit_background->add_child(preview_polygon);
uv_edit_draw = memnew(Control);
uv_edit_background->add_child(uv_edit_draw);
uv_edit_draw->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
Control *space = memnew(Control);
uv_mode_hb->add_child(space);
@ -1491,8 +1528,6 @@ Polygon2DEditor::Polygon2DEditor() {
error = memnew(AcceptDialog);
add_child(error);
uv_edit_draw->set_clip_contents(true);
}
Polygon2DEditorPlugin::Polygon2DEditorPlugin() :

View File

@ -82,7 +82,9 @@ class Polygon2DEditor : public AbstractPolygon2DEditor {
Button *uv_button[UV_MODE_MAX];
Button *b_snap_enable = nullptr;
Button *b_snap_grid = nullptr;
Panel *uv_edit_draw = nullptr;
Panel *uv_edit_background = nullptr;
Polygon2D *preview_polygon = nullptr;
Control *uv_edit_draw = nullptr;
HSlider *uv_zoom = nullptr;
SpinBox *uv_zoom_value = nullptr;
HScrollBar *uv_hscroll = nullptr;

View File

@ -421,7 +421,11 @@ ScriptEditor *ScriptEditor::script_editor = nullptr;
String ScriptEditor::_get_debug_tooltip(const String &p_text, Node *_se) {
String val = EditorDebuggerNode::get_singleton()->get_var_value(p_text);
const int display_limit = 300;
if (!val.is_empty()) {
if (val.size() > display_limit) {
val = val.left(display_limit) + " [...] truncated!";
}
return p_text + ": " + val;
} else {
return String();

View File

@ -948,13 +948,16 @@ void SpriteFramesEditor::_animation_name_edited() {
String name = new_name;
int counter = 0;
while (frames->has_animation(name)) {
if (name == String(edited_anim)) {
edited->set_text(0, name); // The name didn't change, just updated the column text to name.
return;
}
counter++;
name = new_name + "_" + itos(counter);
}
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Rename Animation"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene());
_rename_node_animation(undo_redo, false, edited_anim, "", "");
undo_redo->add_do_method(frames.ptr(), "rename_animation", edited_anim, name);
undo_redo->add_undo_method(frames.ptr(), "rename_animation", name, edited_anim);
_rename_node_animation(undo_redo, false, edited_anim, name, name);

View File

@ -1099,7 +1099,7 @@ TextureRegionEditor::TextureRegionEditor() {
snap_mode_button->add_item(TTR("Pixel Snap"), 1);
snap_mode_button->add_item(TTR("Grid Snap"), 2);
snap_mode_button->add_item(TTR("Auto Slice"), 3);
snap_mode_button->select(0);
snap_mode_button->select(snap_mode);
snap_mode_button->connect("item_selected", callable_mp(this, &TextureRegionEditor::_set_snap_mode));
hb_grid = memnew(HBoxContainer);

View File

@ -1451,7 +1451,7 @@ const char *RenamesMap3To4::shaders_renames[][2] = {
{ "NORMALMAP_DEPTH", "NORMAL_MAP_DEPTH" },
{ "TRANSMISSION", "BACKLIGHT" },
{ "WORLD_MATRIX", "MODEL_MATRIX" },
{ "depth_draw_alpha_prepass", "depth_draw_opaque" },
{ "depth_draw_alpha_prepass", "depth_prepass_alpha" },
{ "hint_albedo", "source_color" },
{ "hint_aniso", "hint_anisotropy" },
{ "hint_black", "hint_default_black" },

View File

@ -1962,6 +1962,9 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
if (frame_delay == 0) {
frame_delay = GLOBAL_DEF(PropertyInfo(Variant::INT, "application/run/frame_delay_msec", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), 0);
if (Engine::get_singleton()->is_editor_hint()) {
frame_delay = 0;
}
}
OS::get_singleton()->set_low_processor_usage_mode(GLOBAL_DEF("application/run/low_processor_mode", false));

View File

@ -907,9 +907,16 @@ def generate_vs_project(env, original_args, project_name="godot"):
defines=mono_defines,
)
env["MSVSBUILDCOM"] = module_configs.build_commandline("scons")
env["MSVSREBUILDCOM"] = module_configs.build_commandline("scons vsproj=yes")
env["MSVSCLEANCOM"] = module_configs.build_commandline("scons --clean")
scons_cmd = "scons"
path_to_venv = os.getenv("VIRTUAL_ENV")
path_to_scons_exe = Path(str(path_to_venv)) / "Scripts" / "scons.exe"
if path_to_venv and path_to_scons_exe.exists():
scons_cmd = str(path_to_scons_exe)
env["MSVSBUILDCOM"] = module_configs.build_commandline(scons_cmd)
env["MSVSREBUILDCOM"] = module_configs.build_commandline(f"{scons_cmd} vsproj=yes")
env["MSVSCLEANCOM"] = module_configs.build_commandline(f"{scons_cmd} --clean")
if not env.get("MSVS"):
env["MSVS"]["PROJECTSUFFIX"] = ".vcxproj"
env["MSVS"]["SOLUTIONSUFFIX"] = ".sln"

View File

@ -573,6 +573,11 @@ void CSGShape3D::_notification(int p_what) {
// Update this node's parent only if its own visibility has changed, not the visibility of parent nodes
parent_shape->_make_dirty();
}
if (is_visible()) {
_update_debug_collision_shape();
} else {
_clear_debug_collision_shape();
}
last_visible = is_visible();
} break;

View File

@ -190,6 +190,9 @@ namespace GodotTools
case ExternalEditorId.CustomEditor:
{
string file = ProjectSettings.GlobalizePath(script.ResourcePath);
string project = ProjectSettings.GlobalizePath("res://");
// Since ProjectSettings.GlobalizePath replaces only "res:/", leaving a trailing slash, it is removed here.
project = project[..^1];
var execCommand = _editorSettings.GetSetting(Settings.CustomExecPath).As<string>();
var execArgs = _editorSettings.GetSetting(Settings.CustomExecPathArgs).As<string>();
var args = new List<string>();
@ -226,6 +229,7 @@ namespace GodotTools
hasFileFlag = true;
}
arg = arg.ReplaceN("{project}", project);
arg = arg.ReplaceN("{file}", file);
args.Add(arg);

View File

@ -1116,6 +1116,7 @@ void NavMap::_update_rvo_obstacles_tree_2d() {
rvo_2d_vertices.reserve(_obstacle_vertices.size());
uint32_t _obstacle_avoidance_layers = obstacle->get_avoidance_layers();
real_t _obstacle_height = obstacle->get_height();
for (const Vector3 &_obstacle_vertex : _obstacle_vertices) {
rvo_2d_vertices.push_back(RVO2D::Vector2(_obstacle_vertex.x + _obstacle_position.x, _obstacle_vertex.z + _obstacle_position.z));
@ -1126,6 +1127,9 @@ void NavMap::_update_rvo_obstacles_tree_2d() {
for (size_t i = 0; i < rvo_2d_vertices.size(); i++) {
RVO2D::Obstacle2D *rvo_2d_obstacle = new RVO2D::Obstacle2D();
rvo_2d_obstacle->point_ = rvo_2d_vertices[i];
rvo_2d_obstacle->height_ = _obstacle_height;
rvo_2d_obstacle->elevation_ = _obstacle_position.y;
rvo_2d_obstacle->avoidance_layers_ = _obstacle_avoidance_layers;
if (i != 0) {

View File

@ -28,6 +28,11 @@ elif env["platform"] == "linuxbsd":
env_openxr.AppendUnique(CPPDEFINES=["HAVE_SECURE_GETENV"])
elif env["platform"] == "windows":
env_openxr.AppendUnique(CPPDEFINES=["XR_OS_WINDOWS", "NOMINMAX", "XR_USE_PLATFORM_WIN32"])
elif env["platform"] == "macos":
env_openxr.AppendUnique(CPPDEFINES=["XR_OS_APPLE"])
# There does not seem to be a XR_USE_PLATFORM_XYZ for Apple
# may need to check and set:
# - XR_USE_TIMESPEC
@ -96,7 +101,7 @@ if env["platform"] == "android":
env_openxr.add_source_files(module_obj, "extensions/openxr_android_extension.cpp")
if env["vulkan"]:
env_openxr.add_source_files(module_obj, "extensions/openxr_vulkan_extension.cpp")
if env["opengl3"]:
if env["opengl3"] and env["platform"] != "macos":
env_openxr.add_source_files(module_obj, "extensions/openxr_opengl_extension.cpp")
env_openxr.add_source_files(module_obj, "extensions/openxr_palm_pose_extension.cpp")

View File

@ -1,5 +1,5 @@
def can_build(env, platform):
if platform in ("linuxbsd", "windows", "android"):
if platform in ("linuxbsd", "windows", "android", "macos"):
return env["openxr"] and not env["disable_3d"]
else:
# not supported on these platforms

View File

@ -47,7 +47,7 @@
#ifdef VULKAN_ENABLED
#define XR_USE_GRAPHICS_API_VULKAN
#endif
#ifdef GLES3_ENABLED
#if defined(GLES3_ENABLED) && !defined(MACOS_ENABLED)
#ifdef ANDROID_ENABLED
#define XR_USE_GRAPHICS_API_OPENGL_ES
#include <EGL/egl.h>
@ -72,7 +72,7 @@
#include "extensions/openxr_vulkan_extension.h"
#endif
#ifdef GLES3_ENABLED
#if defined(GLES3_ENABLED) && !defined(MACOS_ENABLED)
#include "extensions/openxr_opengl_extension.h"
#endif
@ -1306,7 +1306,7 @@ bool OpenXRAPI::initialize(const String &p_rendering_driver) {
ERR_FAIL_V(false);
#endif
} else if (p_rendering_driver == "opengl3") {
#ifdef GLES3_ENABLED
#if defined(GLES3_ENABLED) && !defined(MACOS_ENABLED)
graphics_extension = memnew(OpenXROpenGLExtension);
register_extension_wrapper(graphics_extension);
#else

View File

@ -327,9 +327,9 @@ void AudioStreamPlaybackOggVorbis::seek(double p_time) {
int64_t samples_to_burn = samples_in_page - (granule_pos - desired_sample);
if (samples_to_burn > samples_in_page) {
WARN_PRINT("Burning more samples than we have in this page. Check seek algorithm.");
WARN_PRINT_ONCE("Burning more samples than we have in this page. Check seek algorithm.");
} else if (samples_to_burn < 0) {
WARN_PRINT("Burning negative samples doesn't make sense. Check seek algorithm.");
WARN_PRINT_ONCE("Burning negative samples doesn't make sense. Check seek algorithm.");
}
// Seek again, this time we'll burn a specific number of samples instead of all of them.

View File

@ -72,7 +72,24 @@ Error ZIPPacker::start_file(String p_path) {
zipfi.internal_fa = 0;
zipfi.external_fa = 0;
int err = zipOpenNewFileInZip(zf, p_path.utf8().get_data(), &zipfi, nullptr, 0, nullptr, 0, nullptr, Z_DEFLATED, Z_DEFAULT_COMPRESSION);
int err = zipOpenNewFileInZip4(zf,
p_path.utf8().get_data(),
&zipfi,
nullptr,
0,
nullptr,
0,
nullptr,
Z_DEFLATED,
Z_DEFAULT_COMPRESSION,
0,
-MAX_WBITS,
DEF_MEM_LEVEL,
Z_DEFAULT_STRATEGY,
nullptr,
0,
0x0314, // "version made by", 0x03 - Unix, 0x14 - ZIP specification version 2.0, required to store Unix file permissions.
1 << 11); // Bit 11 is the language encoding flag. When set, filename and comment fields must be encoded using UTF-8.
return err == ZIP_OK ? OK : FAILED;
}

View File

@ -1531,18 +1531,32 @@ void EditorExportPlatformAndroid::_process_launcher_icons(const String &p_file_n
String EditorExportPlatformAndroid::load_splash_refs(Ref<Image> &splash_image, Ref<Image> &splash_bg_color_image) {
bool scale_splash = GLOBAL_GET("application/boot_splash/fullsize");
bool apply_filter = GLOBAL_GET("application/boot_splash/use_filter");
bool show_splash_image = GLOBAL_GET("application/boot_splash/show_image");
String project_splash_path = GLOBAL_GET("application/boot_splash/image");
if (!project_splash_path.is_empty()) {
splash_image.instantiate();
print_verbose("Loading splash image: " + project_splash_path);
const Error err = ImageLoader::load_image(project_splash_path, splash_image);
if (err) {
if (OS::get_singleton()->is_stdout_verbose()) {
print_error("- unable to load splash image from " + project_splash_path + " (" + itos(err) + ")");
// Setup the splash bg color.
bool bg_color_valid = false;
Color bg_color = ProjectSettings::get_singleton()->get("application/boot_splash/bg_color", &bg_color_valid);
if (!bg_color_valid) {
bg_color = boot_splash_bg_color;
}
if (show_splash_image) {
if (!project_splash_path.is_empty()) {
splash_image.instantiate();
print_verbose("Loading splash image: " + project_splash_path);
const Error err = ImageLoader::load_image(project_splash_path, splash_image);
if (err) {
if (OS::get_singleton()->is_stdout_verbose()) {
print_error("- unable to load splash image from " + project_splash_path + " (" + itos(err) + ")");
}
splash_image.unref();
}
splash_image.unref();
}
} else {
splash_image.instantiate();
splash_image->initialize_data(1, 1, false, Image::FORMAT_RGBA8);
splash_image->set_pixel(0, 0, bg_color);
}
if (splash_image.is_null()) {
@ -1566,13 +1580,6 @@ String EditorExportPlatformAndroid::load_splash_refs(Ref<Image> &splash_image, R
splash_image->resize(width, height);
}
// Setup the splash bg color
bool bg_color_valid;
Color bg_color = ProjectSettings::get_singleton()->get("application/boot_splash/bg_color", &bg_color_valid);
if (!bg_color_valid) {
bg_color = boot_splash_bg_color;
}
print_verbose("Creating splash background color image.");
splash_bg_color_image.instantiate();
splash_bg_color_image->initialize_data(splash_image->get_width(), splash_image->get_height(), false, splash_image->get_format());
@ -1699,7 +1706,6 @@ void EditorExportPlatformAndroid::get_preset_features(const Ref<EditorExportPres
Vector<ABI> abis = get_enabled_abis(p_preset);
for (int i = 0; i < abis.size(); ++i) {
r_features->push_back(abis[i].arch);
r_features->push_back(abis[i].abi);
}
}
@ -2933,10 +2939,13 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
}
}
int result = EditorNode::get_singleton()->execute_and_show_output(TTR("Building Android Project (gradle)"), build_command, cmdline);
String build_project_output;
int result = EditorNode::get_singleton()->execute_and_show_output(TTR("Building Android Project (gradle)"), build_command, cmdline, true, false, &build_project_output);
if (result != 0) {
add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Building of Android project failed, check output for the error. Alternatively visit docs.godotengine.org for Android build documentation."));
add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Building of Android project failed, check output for the error:") + "\n\n" + build_project_output);
return ERR_CANT_CREATE;
} else {
print_verbose(build_project_output);
}
List<String> copy_args;
@ -2963,10 +2972,13 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
copy_args.push_back("-Pexport_filename=" + export_filename);
print_verbose("Copying Android binary using gradle command: " + String("\n") + build_command + " " + join_list(copy_args, String(" ")));
int copy_result = EditorNode::get_singleton()->execute_and_show_output(TTR("Moving output"), build_command, copy_args);
String copy_binary_output;
int copy_result = EditorNode::get_singleton()->execute_and_show_output(TTR("Moving output"), build_command, copy_args, true, false, &copy_binary_output);
if (copy_result != 0) {
add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Unable to copy and rename export file, check gradle project directory for outputs."));
add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Unable to copy and rename export file:") + "\n\n" + copy_binary_output);
return ERR_CANT_CREATE;
} else {
print_verbose(copy_binary_output);
}
print_verbose("Successfully completed Android gradle build.");

View File

@ -0,0 +1 @@
!/debug

View File

@ -35,7 +35,7 @@
<activity
android:name=".GodotProjectManager"
android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
android:launchMode="singleInstance"
android:launchMode="singleTask"
android:screenOrientation="userLandscape"
android:exported="true"
android:process=":GodotProjectManager">
@ -53,7 +53,7 @@
android:name=".GodotEditor"
android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
android:process=":GodotEditor"
android:launchMode="singleInstance"
android:launchMode="singleTask"
android:screenOrientation="userLandscape"
android:exported="false">
<layout android:defaultHeight="@dimen/editor_default_window_height"
@ -65,7 +65,7 @@
android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
android:label="@string/godot_project_name_string"
android:process=":GodotGame"
android:launchMode="singleInstance"
android:launchMode="singleTask"
android:exported="false"
android:screenOrientation="userLandscape">
<layout android:defaultHeight="@dimen/editor_default_window_height"

View File

@ -82,11 +82,11 @@ void TTS_Linux::speech_init_thread_func(void *p_userdata) {
void TTS_Linux::speech_event_index_mark(size_t p_msg_id, size_t p_client_id, SPDNotificationType p_type, char *p_index_mark) {
TTS_Linux *tts = TTS_Linux::get_singleton();
if (tts) {
callable_mp(tts, &TTS_Linux::_speech_index_mark).call_deferred(p_msg_id, p_client_id, (int)p_type, String::utf8(p_index_mark));
callable_mp(tts, &TTS_Linux::_speech_index_mark).call_deferred((int)p_msg_id, (int)p_type, String::utf8(p_index_mark));
}
}
void TTS_Linux::_speech_index_mark(size_t p_msg_id, size_t p_client_id, int p_type, const String &p_index_mark) {
void TTS_Linux::_speech_index_mark(int p_msg_id, int p_type, const String &p_index_mark) {
_THREAD_SAFE_METHOD_
if (ids.has(p_msg_id)) {
@ -97,7 +97,7 @@ void TTS_Linux::_speech_index_mark(size_t p_msg_id, size_t p_client_id, int p_ty
void TTS_Linux::speech_event_callback(size_t p_msg_id, size_t p_client_id, SPDNotificationType p_type) {
TTS_Linux *tts = TTS_Linux::get_singleton();
if (tts) {
callable_mp(tts, &TTS_Linux::_speech_event).call_deferred(p_msg_id, p_client_id, (int)p_type);
callable_mp(tts, &TTS_Linux::_speech_event).call_deferred((int)p_msg_id, (int)p_type);
}
}
@ -119,7 +119,7 @@ void TTS_Linux::_load_voices() {
}
}
void TTS_Linux::_speech_event(size_t p_msg_id, size_t p_client_id, int p_type) {
void TTS_Linux::_speech_event(int p_msg_id, int p_type) {
_THREAD_SAFE_METHOD_
if (!paused && ids.has(p_msg_id)) {
@ -226,7 +226,7 @@ void TTS_Linux::speak(const String &p_text, const String &p_voice, int p_volume,
if (is_paused()) {
resume();
} else {
_speech_event(0, 0, (int)SPD_EVENT_BEGIN);
_speech_event(0, (int)SPD_EVENT_BEGIN);
}
}

View File

@ -72,8 +72,8 @@ class TTS_Linux : public Object {
protected:
void _load_voices();
void _speech_event(size_t p_msg_id, size_t p_client_id, int p_type);
void _speech_index_mark(size_t p_msg_id, size_t p_client_id, int p_type, const String &p_index_mark);
void _speech_event(int p_msg_id, int p_type);
void _speech_index_mark(int p_msg_id, int p_type, const String &p_index_mark);
public:
static TTS_Linux *get_singleton();

View File

@ -1401,7 +1401,9 @@ void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window)
SystemParametersInfoA(SPI_SETMOUSETRAILS, restore_mouse_trails, 0, 0);
restore_mouse_trails = 0;
}
} else if (p_mode == WINDOW_MODE_WINDOWED) {
}
if (p_mode == WINDOW_MODE_WINDOWED) {
ShowWindow(wd.hWnd, SW_RESTORE);
wd.maximized = false;
wd.minimized = false;

View File

@ -100,8 +100,8 @@ static bool nvapi_err_check(const char *msg, int status) {
}
// On windows we have to disable threaded optimization when using NVIDIA graphics cards
// to avoid stuttering, see https://github.com/microsoft/vscode-cpptools/issues/6592
// also see https://github.com/Ryujinx/Ryujinx/blob/master/Ryujinx.Common/GraphicsDriver/NVThreadedOptimization.cs
// to avoid stuttering, see https://stackoverflow.com/questions/36959508/nvidia-graphics-driver-causing-noticeable-frame-stuttering/37632948
// also see https://github.com/Ryujinx/Ryujinx/blob/master/src/Ryujinx.Common/GraphicsDriver/NVThreadedOptimization.cs
void GLManager_Windows::_nvapi_disable_threaded_optimization() {
HMODULE nvapi = 0;
#ifdef _WIN64
@ -145,6 +145,10 @@ void GLManager_Windows::_nvapi_disable_threaded_optimization() {
NvDRSSessionHandle session_handle;
if (NvAPI_DRS_CreateSession == nullptr) {
return;
}
if (!nvapi_err_check("NVAPI: Error creating DRS session", NvAPI_DRS_CreateSession(&session_handle))) {
NvAPI_Unload();
return;

View File

@ -48,12 +48,14 @@ bool Bone2D::_set(const StringName &p_path, const Variant &p_value) {
} else if (path.begins_with("default_length")) {
set_length(p_value);
}
#ifdef TOOLS_ENABLED
if (path.begins_with("editor_settings/show_bone_gizmo")) {
else if (path.begins_with("editor_settings/show_bone_gizmo")) {
_editor_set_show_bone_gizmo(p_value);
}
#endif // TOOLS_ENABLED
else {
return false;
}
return true;
}
@ -70,12 +72,14 @@ bool Bone2D::_get(const StringName &p_path, Variant &r_ret) const {
} else if (path.begins_with("default_length")) {
r_ret = get_length();
}
#ifdef TOOLS_ENABLED
if (path.begins_with("editor_settings/show_bone_gizmo")) {
else if (path.begins_with("editor_settings/show_bone_gizmo")) {
r_ret = _editor_get_show_bone_gizmo();
}
#endif // TOOLS_ENABLED
else {
return false;
}
return true;
}

View File

@ -49,6 +49,26 @@ Ref<MultiMesh> MultiMeshInstance3D::get_multimesh() const {
return multimesh;
}
Array MultiMeshInstance3D::get_meshes() const {
if (multimesh.is_null() || multimesh->get_mesh().is_null() || multimesh->get_transform_format() != MultiMesh::TransformFormat::TRANSFORM_3D) {
return Array();
}
int count = multimesh->get_visible_instance_count();
if (count == -1) {
count = multimesh->get_instance_count();
}
Ref<Mesh> mesh = multimesh->get_mesh();
Array results;
for (int i = 0; i < count; i++) {
results.push_back(multimesh->get_instance_transform(i));
results.push_back(mesh);
}
return results;
}
AABB MultiMeshInstance3D::get_aabb() const {
if (multimesh.is_null()) {
return AABB();

View File

@ -47,6 +47,8 @@ public:
void set_multimesh(const Ref<MultiMesh> &p_multimesh);
Ref<MultiMesh> get_multimesh() const;
Array get_meshes() const;
virtual AABB get_aabb() const override;
MultiMeshInstance3D();

View File

@ -68,8 +68,9 @@ Color ReflectionProbe::get_ambient_color() const {
}
void ReflectionProbe::set_max_distance(float p_distance) {
max_distance = p_distance;
RS::get_singleton()->reflection_probe_set_max_distance(probe, p_distance);
max_distance = CLAMP(p_distance, 0.0, 262'144.0);
// Reflection rendering breaks if distance exceeds 262,144 units (due to floating-point precision with the near plane being 0.01).
RS::get_singleton()->reflection_probe_set_max_distance(probe, max_distance);
}
float ReflectionProbe::get_max_distance() const {

View File

@ -314,9 +314,21 @@ Ref<CameraAttributes> VoxelGI::get_camera_attributes() const {
return camera_attributes;
}
static bool is_node_voxel_bakeable(Node3D *p_node) {
if (!p_node->is_visible_in_tree()) {
return false;
}
GeometryInstance3D *geometry = Object::cast_to<GeometryInstance3D>(p_node);
if (geometry != nullptr && geometry->get_gi_mode() != GeometryInstance3D::GI_MODE_STATIC) {
return false;
}
return true;
}
void VoxelGI::_find_meshes(Node *p_at_node, List<PlotMesh> &plot_meshes) {
MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_at_node);
if (mi && mi->get_gi_mode() == GeometryInstance3D::GI_MODE_STATIC && mi->is_visible_in_tree()) {
if (mi && is_node_voxel_bakeable(mi)) {
Ref<Mesh> mesh = mi->get_mesh();
if (mesh.is_valid()) {
AABB aabb = mesh->get_aabb();
@ -338,8 +350,15 @@ void VoxelGI::_find_meshes(Node *p_at_node, List<PlotMesh> &plot_meshes) {
Node3D *s = Object::cast_to<Node3D>(p_at_node);
if (s) {
if (s->is_visible_in_tree()) {
Array meshes = p_at_node->call("get_meshes");
if (is_node_voxel_bakeable(s)) {
Array meshes;
MultiMeshInstance3D *multi_mesh = Object::cast_to<MultiMeshInstance3D>(p_at_node);
if (multi_mesh) {
meshes = multi_mesh->get_meshes();
} else {
meshes = p_at_node->call("get_meshes");
}
for (int i = 0; i < meshes.size(); i += 2) {
Transform3D mxf = meshes[i];
Ref<Mesh> mesh = meshes[i + 1];

View File

@ -383,6 +383,8 @@ Voxelizer::MaterialCache Voxelizer::_get_material_cache(Ref<Material> p_material
}
void Voxelizer::plot_mesh(const Transform3D &p_xform, Ref<Mesh> &p_mesh, const Vector<Ref<Material>> &p_materials, const Ref<Material> &p_override_material) {
ERR_FAIL_COND_MSG(!p_xform.is_finite(), "Invalid mesh bake transform.");
for (int i = 0; i < p_mesh->get_surface_count(); i++) {
if (p_mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) {
continue; //only triangles

View File

@ -1443,11 +1443,11 @@ void ItemList::_check_shape_changed() {
}
}
for (int j = items.size() - 1; j >= 0 && col > 0; j--, col--) {
items.write[j].rect_cache.size.y = max_h;
}
if (all_fit) {
for (int j = items.size() - 1; j >= 0 && col > 0; j--, col--) {
items.write[j].rect_cache.size.y = max_h;
}
float page = MAX(0, size.height - theme_cache.panel_style->get_minimum_size().height);
float max = MAX(page, ofs.y + max_h);
if (auto_height) {

View File

@ -1812,6 +1812,7 @@ void PopupMenu::scroll_to_item(int p_idx) {
}
bool PopupMenu::activate_item_by_event(const Ref<InputEvent> &p_event, bool p_for_global_only) {
ERR_FAIL_COND_V(p_event.is_null(), false);
Key code = Key::NONE;
Ref<InputEventKey> k = p_event;

View File

@ -1659,7 +1659,7 @@ void RichTextLabel::_scroll_changed(double) {
return;
}
if (scroll_follow && vscroll->get_value() >= (vscroll->get_max() - vscroll->get_page())) {
if (scroll_follow && vscroll->get_value() >= (vscroll->get_max() - Math::round(vscroll->get_page()))) {
scroll_following = true;
} else {
scroll_following = false;

View File

@ -2939,15 +2939,17 @@ void TextEdit::_update_placeholder() {
return; // Not in tree?
}
const String placeholder_translated = atr(placeholder_text);
// Placeholder is generally smaller then text documents, and updates less so this should be fast enough for now.
placeholder_data_buf->clear();
placeholder_data_buf->set_width(text.get_width());
placeholder_data_buf->set_break_flags(text.get_brk_flags());
placeholder_data_buf->set_direction((TextServer::Direction)text_direction);
placeholder_data_buf->set_preserve_control(draw_control_chars);
placeholder_data_buf->add_string(placeholder_text, theme_cache.font, theme_cache.font_size, language);
placeholder_data_buf->add_string(placeholder_translated, theme_cache.font, theme_cache.font_size, language);
placeholder_bidi_override = structured_text_parser(st_parser, st_args, placeholder_text);
placeholder_bidi_override = structured_text_parser(st_parser, st_args, placeholder_translated);
if (placeholder_bidi_override.is_empty()) {
TS->shaped_text_set_bidi_override(placeholder_data_buf->get_rid(), placeholder_bidi_override);
}
@ -2972,7 +2974,7 @@ void TextEdit::_update_placeholder() {
placeholder_wraped_rows.clear();
for (int i = 0; i <= wrap_amount; i++) {
Vector2i line_range = placeholder_data_buf->get_line_range(i);
placeholder_wraped_rows.push_back(placeholder_text.substr(line_range.x, line_range.y - line_range.x));
placeholder_wraped_rows.push_back(placeholder_translated.substr(line_range.x, line_range.y - line_range.x));
}
}
@ -4131,6 +4133,9 @@ Point2i TextEdit::search(const String &p_key, uint32_t p_search_flags, int p_fro
int line = p_from_line;
int pos = -1;
bool key_start_is_symbol = is_symbol(p_key[0]);
bool key_end_is_symbol = is_symbol(p_key[p_key.length() - 1]);
for (int i = 0; i < text.size() + 1; i++) {
if (line < 0) {
line = text.size() - 1;
@ -4194,9 +4199,9 @@ Point2i TextEdit::search(const String &p_key, uint32_t p_search_flags, int p_fro
if (pos != -1 && (p_search_flags & SEARCH_WHOLE_WORDS)) {
// Validate for whole words.
if (pos > 0 && !is_symbol(text_line[pos - 1])) {
if (!key_start_is_symbol && pos > 0 && !is_symbol(text_line[pos - 1])) {
is_match = false;
} else if (pos + p_key.length() < text_line.length() && !is_symbol(text_line[pos + p_key.length()])) {
} else if (!key_end_is_symbol && pos + p_key.length() < text_line.length() && !is_symbol(text_line[pos + p_key.length()])) {
is_match = false;
}
}
@ -6990,6 +6995,9 @@ int TextEdit::_get_column_pos_of_word(const String &p_key, const String &p_searc
p_from_column = 0;
}
bool key_start_is_symbol = is_symbol(p_key[0]);
bool key_end_is_symbol = is_symbol(p_key[p_key.length() - 1]);
while (col == -1 && p_from_column <= p_search.length()) {
if (p_search_flags & SEARCH_MATCH_CASE) {
col = p_search.find(p_key, p_from_column);
@ -7001,9 +7009,9 @@ int TextEdit::_get_column_pos_of_word(const String &p_key, const String &p_searc
if (col != -1 && p_search_flags & SEARCH_WHOLE_WORDS) {
p_from_column = col;
if (col > 0 && !is_symbol(p_search[col - 1])) {
if (!key_start_is_symbol && col > 0 && !is_symbol(p_search[col - 1])) {
col = -1;
} else if ((col + p_key.length()) < p_search.length() && !is_symbol(p_search[col + p_key.length()])) {
} else if (!key_end_is_symbol && (col + p_key.length()) < p_search.length() && !is_symbol(p_search[col + p_key.length()])) {
col = -1;
}
}

View File

@ -224,6 +224,12 @@ bool VideoStreamPlayer::has_expand() const {
void VideoStreamPlayer::set_stream(const Ref<VideoStream> &p_stream) {
stop();
// Make sure to handle stream changes seamlessly, e.g. when done via
// translation remapping.
if (stream.is_valid()) {
stream->disconnect("changed", callable_mp(this, &VideoStreamPlayer::set_stream));
}
AudioServer::get_singleton()->lock();
mix_buffer.resize(AudioServer::get_singleton()->thread_get_mix_buffer_size());
stream = p_stream;
@ -235,6 +241,10 @@ void VideoStreamPlayer::set_stream(const Ref<VideoStream> &p_stream) {
}
AudioServer::get_singleton()->unlock();
if (stream.is_valid()) {
stream->connect("changed", callable_mp(this, &VideoStreamPlayer::set_stream).bind(stream));
}
if (!playback.is_null()) {
playback->set_paused(paused);
texture = playback->get_texture();

View File

@ -2727,9 +2727,15 @@ void Node::_duplicate_signals(const Node *p_original, Node *p_copy) const {
copytarget = p_copy->get_node(ptarget);
}
if (copy && copytarget) {
const Callable copy_callable = Callable(copytarget, E.callable.get_method());
if (copy && copytarget && E.callable.get_method() != StringName()) {
Callable copy_callable = Callable(copytarget, E.callable.get_method());
if (!copy->is_connected(E.signal.get_name(), copy_callable)) {
int arg_count = E.callable.get_bound_arguments_count();
if (arg_count > 0) {
copy_callable = copy_callable.bindv(E.callable.get_bound_arguments());
} else if (arg_count < 0) {
copy_callable = copy_callable.unbind(-arg_count);
}
copy->connect(E.signal.get_name(), copy_callable, E.flags);
}
}

View File

@ -37,7 +37,7 @@ bool BoneMap::_set(const StringName &p_path, const Variant &p_value) {
set_skeleton_bone_name(which, p_value);
return true;
}
return true;
return false;
}
bool BoneMap::_get(const StringName &p_path, Variant &r_ret) const {
@ -47,7 +47,7 @@ bool BoneMap::_get(const StringName &p_path, Variant &r_ret) const {
r_ret = get_skeleton_bone_name(which);
return true;
}
return true;
return false;
}
void BoneMap::_get_property_list(List<PropertyInfo> *p_list) const {

View File

@ -82,7 +82,7 @@ float FogMaterial::get_edge_fade() const {
void FogMaterial::set_density_texture(const Ref<Texture3D> &p_texture) {
density_texture = p_texture;
RID tex_rid = p_texture.is_valid() ? p_texture->get_rid() : RID();
Variant tex_rid = p_texture.is_valid() ? Variant(p_texture->get_rid()) : Variant();
RS::get_singleton()->material_set_param(_get_material(), "density_texture", tex_rid);
}

View File

@ -2973,14 +2973,19 @@ void SystemFont::_update_base_font() {
continue;
}
// If it's a font collection check all faces to match requested style.
// If it's a font collection check all faces to match requested style and name.
int best_score = 0;
for (int i = 0; i < file->get_face_count(); i++) {
int score = 0;
file->set_face_index(0, i);
const String n = file->get_font_name();
if (n.to_upper() == E.to_upper()) {
score += 80;
}
BitField<TextServer::FontStyle> style = file->get_font_style();
int font_weight = file->get_font_weight();
int font_stretch = file->get_font_stretch();
int score = (20 - Math::abs(font_weight - weight) / 50);
score += (20 - Math::abs(font_weight - weight) / 50);
score += (20 - Math::abs(font_stretch - stretch) / 10);
if (bool(style & TextServer::FONT_ITALIC) == italic) {
score += 30;
@ -2999,7 +3004,7 @@ void SystemFont::_update_base_font() {
file->set_face_index(0, face_indeces[0]);
// If it's a variable font, apply weight, stretch and italic coordinates to match requested style.
if (best_score != 50) {
if (best_score != 150) {
Dictionary ftr = file->get_supported_variation_list();
if (ftr.has(TS->name_to_tag("width"))) {
ftr_stretch = stretch;

View File

@ -971,7 +971,7 @@ Vector<Ref<Shape3D>> ImporterMesh::convex_decompose(const Ref<MeshConvexDecompos
if (found_vertex) {
index = found_vertex->value;
} else {
index = ++vertex_count;
index = vertex_count++;
vertex_map[vertex] = index;
vertex_w[index] = vertex;
}

View File

@ -1914,7 +1914,7 @@ void BaseMaterial3D::set_texture(TextureParam p_param, const Ref<Texture2D> &p_t
ERR_FAIL_INDEX(p_param, TEXTURE_MAX);
textures[p_param] = p_texture;
RID rid = p_texture.is_valid() ? p_texture->get_rid() : RID();
Variant rid = p_texture.is_valid() ? Variant(p_texture->get_rid()) : Variant();
RS::get_singleton()->material_set_param(_get_material(), shader_names->texture_names[p_param], rid);
if (p_texture.is_valid() && p_param == TEXTURE_ALBEDO) {
@ -3147,7 +3147,7 @@ bool StandardMaterial3D::_set(const StringName &p_name, const Variant &p_value)
{ "flags_use_shadow_to_opacity", "shadow_to_opacity" },
{ "flags_no_depth_test", "no_depth_test" },
{ "flags_use_point_size", "use_point_size" },
{ "flags_fixed_size", "fixed_Size" },
{ "flags_fixed_size", "fixed_size" },
{ "flags_albedo_tex_force_srgb", "albedo_texture_force_srgb" },
{ "flags_do_not_receive_shadows", "disable_receive_shadows" },
{ "flags_disable_ambient_light", "disable_ambient_light" },

View File

@ -1684,7 +1684,7 @@ bool ArrayMesh::_get(const StringName &p_name, Variant &r_ret) const {
return true;
}
return true;
return false;
}
void ArrayMesh::reset_state() {

View File

@ -89,7 +89,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
}
int nc = nodes.size();
ERR_FAIL_COND_V(nc == 0, nullptr);
ERR_FAIL_COND_V_MSG(nc == 0, nullptr, vformat("Failed to instantiate scene state of \"%s\", node count is 0. Make sure the PackedScene resource is valid.", path));
const StringName *snames = nullptr;
int sname_count = names.size();
@ -170,7 +170,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
Ref<PackedScene> sdata = props[n.instance & FLAG_MASK];
ERR_FAIL_COND_V(!sdata.is_valid(), nullptr);
node = sdata->instantiate(p_edit_state == GEN_EDIT_STATE_DISABLED ? PackedScene::GEN_EDIT_STATE_DISABLED : PackedScene::GEN_EDIT_STATE_INSTANCE);
ERR_FAIL_NULL_V(node, nullptr);
ERR_FAIL_NULL_V_MSG(node, nullptr, vformat("Failed to load scene dependency: \"%s\". Make sure the required scene is valid.", sdata->get_path()));
}
} else if (n.type == TYPE_INSTANTIATED) {

View File

@ -1118,7 +1118,7 @@ void ParticleProcessMaterial::set_param_texture(Parameter p_param, const Ref<Tex
tex_parameters[p_param] = p_texture;
RID tex_rid = p_texture.is_valid() ? p_texture->get_rid() : RID();
Variant tex_rid = p_texture.is_valid() ? Variant(p_texture->get_rid()) : Variant();
switch (p_param) {
case PARAM_INITIAL_LINEAR_VELOCITY: {
@ -1201,7 +1201,7 @@ Color ParticleProcessMaterial::get_color() const {
void ParticleProcessMaterial::set_color_ramp(const Ref<Texture2D> &p_texture) {
color_ramp = p_texture;
RID tex_rid = p_texture.is_valid() ? p_texture->get_rid() : RID();
Variant tex_rid = p_texture.is_valid() ? Variant(p_texture->get_rid()) : Variant();
RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->color_ramp, tex_rid);
_queue_shader_change();
notify_property_list_changed();
@ -1213,7 +1213,7 @@ Ref<Texture2D> ParticleProcessMaterial::get_color_ramp() const {
void ParticleProcessMaterial::set_color_initial_ramp(const Ref<Texture2D> &p_texture) {
color_initial_ramp = p_texture;
RID tex_rid = p_texture.is_valid() ? p_texture->get_rid() : RID();
Variant tex_rid = p_texture.is_valid() ? Variant(p_texture->get_rid()) : Variant();
RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->color_initial_ramp, tex_rid);
_queue_shader_change();
notify_property_list_changed();
@ -1256,19 +1256,19 @@ void ParticleProcessMaterial::set_emission_box_extents(Vector3 p_extents) {
void ParticleProcessMaterial::set_emission_point_texture(const Ref<Texture2D> &p_points) {
emission_point_texture = p_points;
RID tex_rid = p_points.is_valid() ? p_points->get_rid() : RID();
Variant tex_rid = p_points.is_valid() ? Variant(p_points->get_rid()) : Variant();
RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_points, tex_rid);
}
void ParticleProcessMaterial::set_emission_normal_texture(const Ref<Texture2D> &p_normals) {
emission_normal_texture = p_normals;
RID tex_rid = p_normals.is_valid() ? p_normals->get_rid() : RID();
Variant tex_rid = p_normals.is_valid() ? Variant(p_normals->get_rid()) : Variant();
RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_normal, tex_rid);
}
void ParticleProcessMaterial::set_emission_color_texture(const Ref<Texture2D> &p_colors) {
emission_color_texture = p_colors;
RID tex_rid = p_colors.is_valid() ? p_colors->get_rid() : RID();
Variant tex_rid = p_colors.is_valid() ? Variant(p_colors->get_rid()) : Variant();
RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_color, tex_rid);
_queue_shader_change();
}

View File

@ -1301,7 +1301,11 @@ void PlaneMesh::_create_mesh_array(Array &p_arr) const {
points.push_back(Vector3(-x, z, 0.0) + center_offset);
}
normals.push_back(normal);
ADD_TANGENT(1.0, 0.0, 0.0, 1.0);
if (orientation == FACE_X) {
ADD_TANGENT(0.0, 0.0, -1.0, 1.0);
} else {
ADD_TANGENT(1.0, 0.0, 0.0, 1.0);
}
uvs.push_back(Vector2(1.0 - u, 1.0 - v)); /* 1.0 - uv to match orientation with Quad */
point++;

Some files were not shown because too many files have changed in this diff Show More