diff options
265 files changed, 8771 insertions, 3002 deletions
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 8de1bda12e..0911ff98bf 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -140,10 +140,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 diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 449d2159f1..9169bdd456 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -5,19 +5,25 @@ body: - type: markdown attributes: value: | - - When reporting bugs, you'll make our life simpler (and the fix will come sooner) if you follow the guidelines in this template. + When reporting bugs, please follow the guidelines in this template. This helps identify the problem precisely and thus enables contributors to fix it faster. - Write a descriptive issue title above. - The golden rule is to **always open *one* issue for *one* bug**. If you notice several bugs and want to report them, make sure to create one new issue for each of them. - Search [open](https://github.com/godotengine/godot/issues) and [closed](https://github.com/godotengine/godot/issues?q=is%3Aissue+is%3Aclosed) issues to ensure it has not already been reported. If you don't find a relevant match or if you're unsure, don't hesitate to **open a new issue**. The bugsquad will handle it from there if it's a duplicate. - - Verify that you are using a [supported Godot version](https://docs.godotengine.org/en/stable/about/release_policy.html). + - Verify that you are using a [supported Godot version](https://docs.godotengine.org/en/stable/about/release_policy.html). Please always check if your issue is reproducible in the latest version – it may already have been fixed! + - If you use a custom build, please test if your issue is reproducible in official builds too. Likewise if you use any C++ modules, GDExtensions, or editor plugins, you should check if the bug is reproducible in a project without these. -- type: input +- type: textarea attributes: - label: Godot version - description: > - Specify the Godot version, including the Git commit hash if using a development or non-official build. The exact Godot version (including the commit hash) can be copied by clicking the version shown in the editor (bottom bar) or in the project manager (top bar). - If you use a custom build, please test if your issue is reproducible in official builds too. - placeholder: 3.5.stable, 4.0.dev [3041becc6] + label: Tested versions + description: | + To properly fix a bug, we need to identify if the bug was recently introduced in the engine, or if it was always present. + - Please specify the Godot version you found the issue in, including the **Git commit hash** if using a development or non-official build. The exact Godot version (including the commit hash) can be copied by clicking the version shown in the editor (bottom bar) or in the project manager (top bar). + - If you can, **please test earlier Godot versions** (previous stable branch, and development snapshots of the current feature release) and, if applicable, newer versions (development snapshots for the next feature release). Mention whether the bug is reproducible or not in the versions you tested. You can find all Godot releases in our [download archive](https://godotengine.org/download/archive/). + - The aim is for us to identify whether a bug is a **regression**, i.e. an issue that didn't exist in a previous version, but was introduced later on, breaking existing functionality. For example, if a bug is reproducible in 4.2.stable but not in 4.1.stable, we would like you to test intermediate 4.2 dev and beta snapshots to find which snapshot is the first one where the issue can be reproduced. + placeholder: | + + - Reproducible in: 4.3.dev [d76c1d0e5], 4.2.stable, 4.2.dev5 and later 4.2 snapshots. + - Not reproducible in: 4.1.3.stable, 4.2.dev4 and earlier 4.2 snapshots. validations: required: true @@ -54,12 +60,12 @@ body: - type: textarea attributes: - label: Minimal reproduction project + label: Minimal reproduction project (MRP) description: | - A small Godot project which reproduces the issue, with no unnecessary files included. Be sure to not include the `.godot` folder in the archive (but keep `project.godot`). - - Required, unless the reproduction steps are trivial and don't require any project files to be followed. In this case, write "N/A" in the field. - - Drag and drop a ZIP archive to upload it. **Do not select another field until the project is done uploading.** + - Having an MRP is very important for contributors to be able to reproduce the bug in the same way that you are experiencing it. When testing a potential fix for the issue, contributors will use the MRP to validate that the fix is working as intended. + - If the reproduction steps are not project dependent (e.g. the bug is visible in a brand new project), you can write "N/A" in the field. + - Drag and drop a ZIP archive to upload it (max 10 MB). **Do not select another field until the project is done uploading.** - **Note for C# users:** If your issue is *not* C#-specific, please upload a minimal reproduction project written in GDScript. This will make it easier for contributors to reproduce the issue locally as not everyone has a .NET setup available. - - **If you've been asked by a maintainer to upload a minimal reproduction project, you *must* do so within 7 days.** Otherwise, your bug report will be closed as it'll be considered too difficult to diagnose. validations: required: true diff --git a/core/input/input.cpp b/core/input/input.cpp index 257452b3d8..14cb8bcf4e 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -241,8 +241,8 @@ bool Input::is_anything_pressed() const { return true; } - for (const KeyValue<StringName, Input::Action> &E : action_state) { - if (E.value.pressed) { + for (const KeyValue<StringName, Input::ActionState> &E : action_states) { + if (E.value.cache.pressed) { return true; } } @@ -285,12 +285,17 @@ bool Input::is_joy_button_pressed(int p_device, JoyButton p_button) const { bool Input::is_action_pressed(const StringName &p_action, bool p_exact) const { ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), false, InputMap::get_singleton()->suggest_actions(p_action)); - return action_state.has(p_action) && action_state[p_action].pressed > 0 && (p_exact ? action_state[p_action].exact : true); + HashMap<StringName, ActionState>::ConstIterator E = action_states.find(p_action); + if (!E) { + return false; + } + + return E->value.cache.pressed && (p_exact ? E->value.exact : true); } bool Input::is_action_just_pressed(const StringName &p_action, bool p_exact) const { ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), false, InputMap::get_singleton()->suggest_actions(p_action)); - HashMap<StringName, Action>::ConstIterator E = action_state.find(p_action); + HashMap<StringName, ActionState>::ConstIterator E = action_states.find(p_action); if (!E) { return false; } @@ -300,7 +305,7 @@ bool Input::is_action_just_pressed(const StringName &p_action, bool p_exact) con } // Backward compatibility for legacy behavior, only return true if currently pressed. - bool pressed_requirement = legacy_just_pressed_behavior ? E->value.pressed : true; + bool pressed_requirement = legacy_just_pressed_behavior ? E->value.cache.pressed : true; if (Engine::get_singleton()->is_in_physics_frame()) { return pressed_requirement && E->value.pressed_physics_frame == Engine::get_singleton()->get_physics_frames(); @@ -311,7 +316,7 @@ bool Input::is_action_just_pressed(const StringName &p_action, bool p_exact) con bool Input::is_action_just_released(const StringName &p_action, bool p_exact) const { ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), false, InputMap::get_singleton()->suggest_actions(p_action)); - HashMap<StringName, Action>::ConstIterator E = action_state.find(p_action); + HashMap<StringName, ActionState>::ConstIterator E = action_states.find(p_action); if (!E) { return false; } @@ -321,7 +326,7 @@ bool Input::is_action_just_released(const StringName &p_action, bool p_exact) co } // Backward compatibility for legacy behavior, only return true if currently released. - bool released_requirement = legacy_just_pressed_behavior ? !E->value.pressed : true; + bool released_requirement = legacy_just_pressed_behavior ? !E->value.cache.pressed : true; if (Engine::get_singleton()->is_in_physics_frame()) { return released_requirement && E->value.released_physics_frame == Engine::get_singleton()->get_physics_frames(); @@ -332,7 +337,7 @@ bool Input::is_action_just_released(const StringName &p_action, bool p_exact) co float Input::get_action_strength(const StringName &p_action, bool p_exact) const { ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), 0.0, InputMap::get_singleton()->suggest_actions(p_action)); - HashMap<StringName, Action>::ConstIterator E = action_state.find(p_action); + HashMap<StringName, ActionState>::ConstIterator E = action_states.find(p_action); if (!E) { return 0.0f; } @@ -341,12 +346,12 @@ float Input::get_action_strength(const StringName &p_action, bool p_exact) const return 0.0f; } - return E->value.strength; + return E->value.cache.strength; } float Input::get_action_raw_strength(const StringName &p_action, bool p_exact) const { ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), 0.0, InputMap::get_singleton()->suggest_actions(p_action)); - HashMap<StringName, Action>::ConstIterator E = action_state.find(p_action); + HashMap<StringName, ActionState>::ConstIterator E = action_states.find(p_action); if (!E) { return 0.0f; } @@ -355,7 +360,7 @@ float Input::get_action_raw_strength(const StringName &p_action, bool p_exact) c return 0.0f; } - return E->value.raw_strength; + return E->value.cache.raw_strength; } float Input::get_axis(const StringName &p_negative_action, const StringName &p_positive_action) const { @@ -440,6 +445,18 @@ static String _hex_str(uint8_t p_byte) { void Input::joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid, Dictionary p_joypad_info) { _THREAD_SAFE_METHOD_ + + // Clear the pressed status if a Joypad gets disconnected. + if (!p_connected) { + for (KeyValue<StringName, ActionState> &E : action_states) { + HashMap<int, ActionState::DeviceState>::Iterator it = E.value.device_states.find(p_idx); + if (it) { + E.value.device_states.remove(it); + _update_action_cache(E.key, E.value); + } + } + } + Joypad js; js.name = p_connected ? p_name : ""; js.uid = p_connected ? p_guid : ""; @@ -699,30 +716,35 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em if (event_index == -1) { continue; } + ERR_FAIL_COND_MSG(event_index >= (int)MAX_EVENT, vformat("Input singleton does not support more than %d events assigned to an action.", MAX_EVENT)); + + int device_id = p_event->get_device(); + bool is_pressed = p_event->is_action_pressed(E.key, true); + ActionState &action_state = action_states[E.key]; + + // Update the action's per-device state. + ActionState::DeviceState &device_state = action_state.device_states[device_id]; + device_state.pressed[event_index] = is_pressed; + device_state.strength[event_index] = p_event->get_action_strength(E.key); + device_state.raw_strength[event_index] = p_event->get_action_raw_strength(E.key); + + // Update the action's global state and cache. + if (!is_pressed) { + action_state.api_pressed = false; // Always release the event from action_press() method. + action_state.api_strength = 0.0; + } + action_state.exact = InputMap::get_singleton()->event_is_action(p_event, E.key, true); - Action &action = action_state[E.key]; - if (!p_event->is_echo()) { - if (p_event->is_action_pressed(E.key)) { - if (!action.pressed) { - action.pressed_physics_frame = Engine::get_singleton()->get_physics_frames(); - action.pressed_process_frame = Engine::get_singleton()->get_process_frames(); - } - action.pressed |= ((uint64_t)1 << event_index); - } else { - action.pressed &= ~((uint64_t)1 << event_index); - action.pressed &= ~(1 << MAX_EVENT); // Always release the event from action_press() method. - - if (!action.pressed) { - action.released_physics_frame = Engine::get_singleton()->get_physics_frames(); - action.released_process_frame = Engine::get_singleton()->get_process_frames(); - } - _update_action_strength(action, MAX_EVENT, 0.0); - _update_action_raw_strength(action, MAX_EVENT, 0.0); - } - action.exact = InputMap::get_singleton()->event_is_action(p_event, E.key, true); + bool was_pressed = action_state.cache.pressed; + _update_action_cache(E.key, action_state); + if (action_state.cache.pressed && !was_pressed) { + action_state.pressed_physics_frame = Engine::get_singleton()->get_physics_frames(); + action_state.pressed_process_frame = Engine::get_singleton()->get_process_frames(); + } + if (!action_state.cache.pressed && was_pressed) { + action_state.released_physics_frame = Engine::get_singleton()->get_physics_frames(); + action_state.released_process_frame = Engine::get_singleton()->get_process_frames(); } - _update_action_strength(action, event_index, p_event->get_action_strength(E.key)); - _update_action_raw_strength(action, event_index, p_event->get_action_raw_strength(E.key)); } if (event_dispatch_function) { @@ -837,32 +859,30 @@ Point2i Input::warp_mouse_motion(const Ref<InputEventMouseMotion> &p_motion, con void Input::action_press(const StringName &p_action, float p_strength) { // Create or retrieve existing action. - Action &action = action_state[p_action]; + ActionState &action_state = action_states[p_action]; - if (!action.pressed) { - action.pressed_physics_frame = Engine::get_singleton()->get_physics_frames(); - action.pressed_process_frame = Engine::get_singleton()->get_process_frames(); + if (!action_state.cache.pressed) { + action_state.pressed_physics_frame = Engine::get_singleton()->get_physics_frames(); + action_state.pressed_process_frame = Engine::get_singleton()->get_process_frames(); } - action.pressed |= 1 << MAX_EVENT; - _update_action_strength(action, MAX_EVENT, p_strength); - _update_action_raw_strength(action, MAX_EVENT, p_strength); - action.exact = true; + action_state.exact = true; + action_state.api_pressed = true; + action_state.api_strength = p_strength; + _update_action_cache(p_action, action_state); } void Input::action_release(const StringName &p_action) { // Create or retrieve existing action. - Action &action = action_state[p_action]; - - action.pressed = 0; - action.strength = 0.0; - action.raw_strength = 0.0; - action.released_physics_frame = Engine::get_singleton()->get_physics_frames(); - action.released_process_frame = Engine::get_singleton()->get_process_frames(); - for (uint64_t i = 0; i <= MAX_EVENT; i++) { - action.strengths[i] = 0.0; - action.raw_strengths[i] = 0.0; - } - action.exact = true; + ActionState &action_state = action_states[p_action]; + action_state.cache.pressed = 0; + action_state.cache.strength = 0.0; + action_state.cache.raw_strength = 0.0; + action_state.released_physics_frame = Engine::get_singleton()->get_physics_frames(); + action_state.released_process_frame = Engine::get_singleton()->get_process_frames(); + action_state.device_states.clear(); + action_state.exact = true; + action_state.api_pressed = false; + action_state.api_strength = 0.0; } void Input::set_emulate_touch_from_mouse(bool p_emulate) { @@ -1020,10 +1040,8 @@ void Input::release_pressed_events() { joy_buttons_pressed.clear(); _joy_axis.clear(); - for (KeyValue<StringName, Input::Action> &E : action_state) { - if (E.value.pressed > 0) { - // Make sure the action is really released. - E.value.pressed = 1; + for (KeyValue<StringName, Input::ActionState> &E : action_states) { + if (E.value.cache.pressed) { action_release(E.key); } } @@ -1190,35 +1208,26 @@ void Input::_axis_event(int p_device, JoyAxis p_axis, float p_value) { parse_input_event(ievent); } -void Input::_update_action_strength(Action &p_action, int p_event_index, float p_strength) { - ERR_FAIL_INDEX(p_event_index, (int)MAX_EVENT + 1); +void Input::_update_action_cache(const StringName &p_action_name, ActionState &r_action_state) { + // Update the action cache, computed from the per-device and per-event states. + r_action_state.cache.pressed = false; + r_action_state.cache.strength = 0.0; + r_action_state.cache.raw_strength = 0.0; - float old_strength = p_action.strengths[p_event_index]; - p_action.strengths[p_event_index] = p_strength; - - if (p_strength > p_action.strength) { - p_action.strength = p_strength; - } else if (Math::is_equal_approx(old_strength, p_action.strength)) { - p_action.strength = p_strength; - for (uint64_t i = 0; i <= MAX_EVENT; i++) { - p_action.strength = MAX(p_action.strength, p_action.strengths[i]); + int max_event = InputMap::get_singleton()->action_get_events(p_action_name)->size(); + for (const KeyValue<int, ActionState::DeviceState> &kv : r_action_state.device_states) { + const ActionState::DeviceState &device_state = kv.value; + for (int i = 0; i < max_event; i++) { + r_action_state.cache.pressed = r_action_state.cache.pressed || device_state.pressed[i]; + r_action_state.cache.strength = MAX(r_action_state.cache.strength, device_state.strength[i]); + r_action_state.cache.raw_strength = MAX(r_action_state.cache.raw_strength, device_state.raw_strength[i]); } } -} - -void Input::_update_action_raw_strength(Action &p_action, int p_event_index, float p_strength) { - ERR_FAIL_INDEX(p_event_index, (int)MAX_EVENT + 1); - float old_strength = p_action.raw_strengths[p_event_index]; - p_action.raw_strengths[p_event_index] = p_strength; - - if (p_strength > p_action.raw_strength) { - p_action.raw_strength = p_strength; - } else if (Math::is_equal_approx(old_strength, p_action.raw_strength)) { - p_action.raw_strength = p_strength; - for (uint64_t i = 0; i <= MAX_EVENT; i++) { - p_action.raw_strength = MAX(p_action.raw_strength, p_action.raw_strengths[i]); - } + if (r_action_state.api_pressed) { + r_action_state.cache.pressed = true; + r_action_state.cache.strength = MAX(r_action_state.cache.strength, r_action_state.api_strength); + r_action_state.cache.raw_strength = MAX(r_action_state.cache.raw_strength, r_action_state.api_strength); // Use the strength as raw_strength for API-pressed states. } } diff --git a/core/input/input.h b/core/input/input.h index dd613c4877..b98406e884 100644 --- a/core/input/input.h +++ b/core/input/input.h @@ -44,7 +44,7 @@ class Input : public Object { static Input *singleton; - static constexpr uint64_t MAX_EVENT = 31; + static constexpr uint64_t MAX_EVENT = 32; public: enum MouseMode { @@ -100,30 +100,31 @@ private: int64_t mouse_window = 0; bool legacy_just_pressed_behavior = false; - struct Action { + struct ActionState { uint64_t pressed_physics_frame = UINT64_MAX; uint64_t pressed_process_frame = UINT64_MAX; uint64_t released_physics_frame = UINT64_MAX; uint64_t released_process_frame = UINT64_MAX; - uint64_t pressed = 0; bool exact = true; - float strength = 0.0f; - float raw_strength = 0.0f; - LocalVector<float> strengths; - LocalVector<float> raw_strengths; - - Action() { - strengths.resize(MAX_EVENT + 1); - raw_strengths.resize(MAX_EVENT + 1); - - for (uint64_t i = 0; i <= MAX_EVENT; i++) { - strengths[i] = 0.0; - raw_strengths[i] = 0.0; - } - } + + struct DeviceState { + bool pressed[MAX_EVENT] = { false }; + float strength[MAX_EVENT] = { 0.0 }; + float raw_strength[MAX_EVENT] = { 0.0 }; + }; + bool api_pressed = false; + float api_strength = 0.0; + HashMap<int, DeviceState> device_states; + + // Cache. + struct ActionStateCache { + bool pressed = false; + float strength = false; + float raw_strength = false; + } cache; }; - HashMap<StringName, Action> action_state; + HashMap<StringName, ActionState> action_states; bool emulate_touch_from_mouse = false; bool emulate_mouse_from_touch = false; @@ -240,8 +241,7 @@ private: JoyAxis _get_output_axis(String output); void _button_event(int p_device, JoyButton p_index, bool p_pressed); void _axis_event(int p_device, JoyAxis p_axis, float p_value); - void _update_action_strength(Action &p_action, int p_event_index, float p_strength); - void _update_action_raw_strength(Action &p_action, int p_event_index, float p_strength); + void _update_action_cache(const StringName &p_action_name, ActionState &r_action_state); void _parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_emulated); diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index 529128b9a2..0c7764392a 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -340,7 +340,7 @@ void ResourceLoader::_thread_load_function(void *p_userdata) { if (load_task.resource.is_valid()) { if (load_task.cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) { - load_task.resource->set_path(load_task.local_path); + load_task.resource->set_path(load_task.local_path, load_task.cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE); } else if (!load_task.local_path.is_resource_file()) { load_task.resource->set_path_cache(load_task.local_path); } @@ -361,6 +361,17 @@ void ResourceLoader::_thread_load_function(void *p_userdata) { if (_loaded_callback) { _loaded_callback(load_task.resource, load_task.local_path); } + } else if (load_task.cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) { + Ref<Resource> existing = ResourceCache::get_ref(load_task.local_path); + if (existing.is_valid()) { + load_task.resource = existing; + load_task.status = THREAD_LOAD_LOADED; + load_task.progress = 1.0; + + if (_loaded_callback) { + _loaded_callback(load_task.resource, load_task.local_path); + } + } } thread_load_mutex.unlock(); @@ -463,7 +474,7 @@ Ref<ResourceLoader::LoadToken> ResourceLoader::_load_start(const String &p_path, load_task.type_hint = p_type_hint; load_task.cache_mode = p_cache_mode; load_task.use_sub_threads = p_thread_mode == LOAD_THREAD_DISTRIBUTE; - if (p_cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) { + if (p_cache_mode == ResourceFormatLoader::CACHE_MODE_REUSE) { Ref<Resource> existing = ResourceCache::get_ref(local_path); if (existing.is_valid()) { //referencing is fine @@ -1113,11 +1124,10 @@ bool ResourceLoader::add_custom_resource_format_loader(String script_path) { Ref<Script> s = res; StringName ibt = s->get_instance_base_type(); bool valid_type = ClassDB::is_parent_class(ibt, "ResourceFormatLoader"); - ERR_FAIL_COND_V_MSG(!valid_type, false, "Script does not inherit a CustomResourceLoader: " + script_path + "."); + ERR_FAIL_COND_V_MSG(!valid_type, false, vformat("Failed to add a custom resource loader, script '%s' does not inherit 'ResourceFormatLoader'.", script_path)); Object *obj = ClassDB::instantiate(ibt); - - ERR_FAIL_NULL_V_MSG(obj, false, "Cannot instance script as custom resource loader, expected 'ResourceFormatLoader' inheritance, got: " + String(ibt) + "."); + ERR_FAIL_NULL_V_MSG(obj, false, vformat("Failed to add a custom resource loader, cannot instantiate '%s'.", ibt)); Ref<ResourceFormatLoader> crl = Object::cast_to<ResourceFormatLoader>(obj); crl->set_script(s); diff --git a/core/io/resource_saver.cpp b/core/io/resource_saver.cpp index 564a54b913..1c6c18b015 100644 --- a/core/io/resource_saver.cpp +++ b/core/io/resource_saver.cpp @@ -237,11 +237,10 @@ bool ResourceSaver::add_custom_resource_format_saver(String script_path) { Ref<Script> s = res; StringName ibt = s->get_instance_base_type(); bool valid_type = ClassDB::is_parent_class(ibt, "ResourceFormatSaver"); - ERR_FAIL_COND_V_MSG(!valid_type, false, "Script does not inherit a CustomResourceSaver: " + script_path + "."); + ERR_FAIL_COND_V_MSG(!valid_type, false, vformat("Failed to add a custom resource saver, script '%s' does not inherit 'ResourceFormatSaver'.", script_path)); Object *obj = ClassDB::instantiate(ibt); - - ERR_FAIL_NULL_V_MSG(obj, false, "Cannot instance script as custom resource saver, expected 'ResourceFormatSaver' inheritance, got: " + String(ibt) + "."); + ERR_FAIL_NULL_V_MSG(obj, false, vformat("Failed to add a custom resource saver, cannot instantiate '%s'.", ibt)); Ref<ResourceFormatSaver> crl = Object::cast_to<ResourceFormatSaver>(obj); crl->set_script(s); diff --git a/core/math/basis.cpp b/core/math/basis.cpp index 9796ac59c2..cd8c87b158 100644 --- a/core/math/basis.cpp +++ b/core/math/basis.cpp @@ -89,13 +89,26 @@ Basis Basis::orthogonalized() const { return c; } +// Returns true if the basis vectors are orthogonal (perpendicular), so it has no skew or shear, and can be decomposed into rotation and scale. +// See https://en.wikipedia.org/wiki/Orthogonal_basis bool Basis::is_orthogonal() const { - Basis identity; - Basis m = (*this) * transposed(); + const Vector3 x = get_column(0); + const Vector3 y = get_column(1); + const Vector3 z = get_column(2); + return Math::is_zero_approx(x.dot(y)) && Math::is_zero_approx(x.dot(z)) && Math::is_zero_approx(y.dot(z)); +} - return m.is_equal_approx(identity); +// Returns true if the basis vectors are orthonormal (orthogonal and normalized), so it has no scale, skew, or shear. +// See https://en.wikipedia.org/wiki/Orthonormal_basis +bool Basis::is_orthonormal() const { + const Vector3 x = get_column(0); + const Vector3 y = get_column(1); + const Vector3 z = get_column(2); + return Math::is_equal_approx(x.length_squared(), 1) && Math::is_equal_approx(y.length_squared(), 1) && Math::is_equal_approx(z.length_squared(), 1) && Math::is_zero_approx(x.dot(y)) && Math::is_zero_approx(x.dot(z)) && Math::is_zero_approx(y.dot(z)); } +// Returns true if the basis is conformal (orthogonal, uniform scale, preserves angles and distance ratios). +// See https://en.wikipedia.org/wiki/Conformal_linear_transformation bool Basis::is_conformal() const { const Vector3 x = get_column(0); const Vector3 y = get_column(1); @@ -104,6 +117,7 @@ bool Basis::is_conformal() const { return Math::is_equal_approx(x_len_sq, y.length_squared()) && Math::is_equal_approx(x_len_sq, z.length_squared()) && Math::is_zero_approx(x.dot(y)) && Math::is_zero_approx(x.dot(z)) && Math::is_zero_approx(y.dot(z)); } +// Returns true if the basis only has diagonal elements, so it may only have scale or flip, but no rotation, skew, or shear. bool Basis::is_diagonal() const { return ( Math::is_zero_approx(rows[0][1]) && Math::is_zero_approx(rows[0][2]) && @@ -111,8 +125,9 @@ bool Basis::is_diagonal() const { Math::is_zero_approx(rows[2][0]) && Math::is_zero_approx(rows[2][1])); } +// Returns true if the basis is a pure rotation matrix, so it has no scale, skew, shear, or flip. bool Basis::is_rotation() const { - return Math::is_equal_approx(determinant(), 1, (real_t)UNIT_EPSILON) && is_orthogonal(); + return is_conformal() && Math::is_equal_approx(determinant(), 1, (real_t)UNIT_EPSILON); } #ifdef MATH_CHECKS diff --git a/core/math/basis.h b/core/math/basis.h index adacd1c216..b4d971464e 100644 --- a/core/math/basis.h +++ b/core/math/basis.h @@ -138,6 +138,7 @@ struct _NO_DISCARD_ Basis { _FORCE_INLINE_ Basis operator*(const real_t p_val) const; bool is_orthogonal() const; + bool is_orthonormal() const; bool is_conformal() const; bool is_diagonal() const; bool is_rotation() const; diff --git a/core/object/make_virtuals.py b/core/object/make_virtuals.py index 79a8df6c8a..ae70981f72 100644 --- a/core/object/make_virtuals.py +++ b/core/object/make_virtuals.py @@ -1,78 +1,74 @@ -proto = """ -#define GDVIRTUAL$VER($RET m_name $ARG) \\ -StringName _gdvirtual_##m_name##_sn = #m_name;\\ -mutable bool _gdvirtual_##m_name##_initialized = false;\\ -mutable void* _gdvirtual_##m_name = nullptr;\\ -template<bool required>\\ -_FORCE_INLINE_ bool _gdvirtual_##m_name##_call($CALLARGS) $CONST { \\ - ScriptInstance *_script_instance = ((Object*)(this))->get_script_instance();\\ - if (_script_instance) {\\ - Callable::CallError ce; \\ - $CALLSIARGS\\ - $CALLSIBEGIN_script_instance->callp(_gdvirtual_##m_name##_sn, $CALLSIARGPASS, ce);\\ - if (ce.error == Callable::CallError::CALL_OK) {\\ - $CALLSIRET\\ +proto = """#define GDVIRTUAL$VER($RET m_name $ARG)\\ + StringName _gdvirtual_##m_name##_sn = #m_name;\\ + mutable bool _gdvirtual_##m_name##_initialized = false;\\ + mutable void *_gdvirtual_##m_name = nullptr;\\ + template <bool required>\\ + _FORCE_INLINE_ bool _gdvirtual_##m_name##_call($CALLARGS) $CONST {\\ + ScriptInstance *_script_instance = ((Object *)(this))->get_script_instance();\\ + if (_script_instance) {\\ + Callable::CallError ce;\\ + $CALLSIARGS\\ + $CALLSIBEGIN_script_instance->callp(_gdvirtual_##m_name##_sn, $CALLSIARGPASS, ce);\\ + if (ce.error == Callable::CallError::CALL_OK) {\\ + $CALLSIRET\\ + return true;\\ + }\\ + }\\ + if (unlikely(_get_extension() && !_gdvirtual_##m_name##_initialized)) {\\ + _gdvirtual_##m_name = nullptr;\\ + if (_get_extension()->get_virtual_call_data && _get_extension()->call_virtual_with_data) {\\ + _gdvirtual_##m_name = _get_extension()->get_virtual_call_data(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\ + } else if (_get_extension()->get_virtual) {\\ + _gdvirtual_##m_name = (void *)_get_extension()->get_virtual(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\ + }\\ + GDVIRTUAL_TRACK(_gdvirtual_##m_name, _gdvirtual_##m_name##_initialized);\\ + _gdvirtual_##m_name##_initialized = true;\\ + }\\ + if (_gdvirtual_##m_name) {\\ + $CALLPTRARGS\\ + $CALLPTRRETDEF\\ + if (_get_extension()->get_virtual_call_data && _get_extension()->call_virtual_with_data) {\\ + _get_extension()->call_virtual_with_data(_get_extension_instance(), &_gdvirtual_##m_name##_sn, _gdvirtual_##m_name, $CALLPTRARGPASS, $CALLPTRRETPASS);\\ + $CALLPTRRET\\ + } else {\\ + ((GDExtensionClassCallVirtual)_gdvirtual_##m_name)(_get_extension_instance(), $CALLPTRARGPASS, $CALLPTRRETPASS);\\ + $CALLPTRRET\\ + }\\ return true;\\ - } \\ + }\\ + if (required) {\\ + ERR_PRINT_ONCE("Required virtual method " + get_class() + "::" + #m_name + " must be overridden before calling.");\\ + $RVOID\\ + }\\ + return false;\\ }\\ - if (unlikely(_get_extension() && !_gdvirtual_##m_name##_initialized)) {\\ - _gdvirtual_##m_name = nullptr;\\ - if (_get_extension()->get_virtual_call_data && _get_extension()->call_virtual_with_data) {\\ - _gdvirtual_##m_name = _get_extension()->get_virtual_call_data(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\ - } else if (_get_extension()->get_virtual) {\\ - _gdvirtual_##m_name = (void *)_get_extension()->get_virtual(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\ - }\\ - GDVIRTUAL_TRACK(_gdvirtual_##m_name, _gdvirtual_##m_name##_initialized); \\ - _gdvirtual_##m_name##_initialized = true;\\ - }\\ - if (_gdvirtual_##m_name) {\\ - $CALLPTRARGS\\ - $CALLPTRRETDEF\\ - if (_get_extension()->get_virtual_call_data && _get_extension()->call_virtual_with_data) {\\ - _get_extension()->call_virtual_with_data(_get_extension_instance(), &_gdvirtual_##m_name##_sn, _gdvirtual_##m_name, $CALLPTRARGPASS,$CALLPTRRETPASS);\\ - $CALLPTRRET\\ - } else {\\ - ((GDExtensionClassCallVirtual)_gdvirtual_##m_name)(_get_extension_instance(),$CALLPTRARGPASS,$CALLPTRRETPASS);\\ - $CALLPTRRET\\ - }\\ - return true;\\ - }\\ - \\ - if (required) {\\ - ERR_PRINT_ONCE("Required virtual method " + get_class() + "::" + #m_name + " must be overridden before calling.");\\ - $RVOID\\ - }\\ -\\ - return false;\\ -}\\ -_FORCE_INLINE_ bool _gdvirtual_##m_name##_overridden() const { \\ - ScriptInstance *_script_instance = ((Object*)(this))->get_script_instance();\\ - if (_script_instance && _script_instance->has_method(_gdvirtual_##m_name##_sn)) {\\ - return true;\\ - }\\ - if (unlikely(_get_extension() && !_gdvirtual_##m_name##_initialized)) {\\ - _gdvirtual_##m_name = nullptr;\\ - if (_get_extension()->get_virtual_call_data && _get_extension()->call_virtual_with_data) {\\ - _gdvirtual_##m_name = _get_extension()->get_virtual_call_data(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\ - } else if (_get_extension()->get_virtual) {\\ - _gdvirtual_##m_name = (void *)_get_extension()->get_virtual(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\ - }\\ - GDVIRTUAL_TRACK(_gdvirtual_##m_name, _gdvirtual_##m_name##_initialized); \\ - _gdvirtual_##m_name##_initialized = true;\\ - }\\ - if (_gdvirtual_##m_name) {\\ - return true;\\ + _FORCE_INLINE_ bool _gdvirtual_##m_name##_overridden() const {\\ + ScriptInstance *_script_instance = ((Object *)(this))->get_script_instance();\\ + if (_script_instance && _script_instance->has_method(_gdvirtual_##m_name##_sn)) {\\ + return true;\\ + }\\ + if (unlikely(_get_extension() && !_gdvirtual_##m_name##_initialized)) {\\ + _gdvirtual_##m_name = nullptr;\\ + if (_get_extension()->get_virtual_call_data && _get_extension()->call_virtual_with_data) {\\ + _gdvirtual_##m_name = _get_extension()->get_virtual_call_data(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\ + } else if (_get_extension()->get_virtual) {\\ + _gdvirtual_##m_name = (void *)_get_extension()->get_virtual(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\ + }\\ + GDVIRTUAL_TRACK(_gdvirtual_##m_name, _gdvirtual_##m_name##_initialized);\\ + _gdvirtual_##m_name##_initialized = true;\\ + }\\ + if (_gdvirtual_##m_name) {\\ + return true;\\ + }\\ + return false;\\ }\\ - return false;\\ -}\\ -\\ -_FORCE_INLINE_ static MethodInfo _gdvirtual_##m_name##_get_method_info() { \\ - MethodInfo method_info;\\ - method_info.name = #m_name;\\ - method_info.flags = METHOD_FLAG_VIRTUAL;\\ - $FILL_METHOD_INFO\\ - return method_info;\\ -} + _FORCE_INLINE_ static MethodInfo _gdvirtual_##m_name##_get_method_info() {\\ + MethodInfo method_info;\\ + method_info.name = #m_name;\\ + method_info.flags = $METHOD_FLAGS;\\ + $FILL_METHOD_INFO\\ + return method_info;\\ + } """ @@ -83,22 +79,23 @@ def generate_version(argcount, const=False, returns=False): method_info = "" if returns: sproto += "R" - s = s.replace("$RET", "m_ret, ") + s = s.replace("$RET", "m_ret,") s = s.replace("$RVOID", "(void)r_ret;") # If required, may lead to uninitialized errors s = s.replace("$CALLPTRRETDEF", "PtrToArg<m_ret>::EncodeT ret;") - method_info += "\tmethod_info.return_val = GetTypeInfo<m_ret>::get_class_info();\\\n" - method_info += "\tmethod_info.return_val_metadata = GetTypeInfo<m_ret>::METADATA;\\\n" + method_info += "method_info.return_val = GetTypeInfo<m_ret>::get_class_info();\\\n" + method_info += "\t\tmethod_info.return_val_metadata = GetTypeInfo<m_ret>::METADATA;" else: - s = s.replace("$RET", "") - s = s.replace("$RVOID", "") - s = s.replace("$CALLPTRRETDEF", "") + s = s.replace("$RET ", "") + s = s.replace("\t\t\t$RVOID\\\n", "") + s = s.replace("\t\t\t$CALLPTRRETDEF\\\n", "") if const: sproto += "C" s = s.replace("$CONST", "const") - method_info += "\tmethod_info.flags|=METHOD_FLAG_CONST;\\\n" + s = s.replace("$METHOD_FLAGS", "METHOD_FLAG_VIRTUAL | METHOD_FLAG_CONST") else: - s = s.replace("$CONST", "") + s = s.replace("$CONST ", "") + s = s.replace("$METHOD_FLAGS", "METHOD_FLAG_VIRTUAL") s = s.replace("$VER", sproto) argtext = "" @@ -108,9 +105,9 @@ def generate_version(argcount, const=False, returns=False): callptrargsptr = "" if argcount > 0: argtext += ", " - callsiargs = "Variant vargs[" + str(argcount) + "]={" - callsiargptrs = "\t\tconst Variant *vargptrs[" + str(argcount) + "]={" - callptrargsptr = "\t\tGDExtensionConstTypePtr argptrs[" + str(argcount) + "]={" + callsiargs = f"Variant vargs[{argcount}] = {{ " + callsiargptrs = f"\t\t\tconst Variant *vargptrs[{argcount}] = {{ " + callptrargsptr = f"\t\t\tGDExtensionConstTypePtr argptrs[{argcount}] = {{ " callptrargs = "" for i in range(argcount): if i > 0: @@ -118,52 +115,55 @@ def generate_version(argcount, const=False, returns=False): callargtext += ", " callsiargs += ", " callsiargptrs += ", " - callptrargs += "\t\t" + callptrargs += "\t\t\t" callptrargsptr += ", " - argtext += "m_type" + str(i + 1) - callargtext += "m_type" + str(i + 1) + " arg" + str(i + 1) - callsiargs += "Variant(arg" + str(i + 1) + ")" - callsiargptrs += "&vargs[" + str(i) + "]" + argtext += f"m_type{i + 1}" + callargtext += f"m_type{i + 1} arg{i + 1}" + callsiargs += f"Variant(arg{i + 1})" + callsiargptrs += f"&vargs[{i}]" callptrargs += ( - "PtrToArg<m_type" + str(i + 1) + ">::EncodeT argval" + str(i + 1) + " = arg" + str(i + 1) + ";\\\n" - ) - callptrargsptr += "&argval" + str(i + 1) - method_info += "\tmethod_info.arguments.push_back(GetTypeInfo<m_type" + str(i + 1) + ">::get_class_info());\\\n" - method_info += ( - "\tmethod_info.arguments_metadata.push_back(GetTypeInfo<m_type" + str(i + 1) + ">::METADATA);\\\n" + f"PtrToArg<m_type{i + 1}>::EncodeT argval{i + 1} = (PtrToArg<m_type{i + 1}>::EncodeT)arg{i + 1};\\\n" ) + callptrargsptr += f"&argval{i + 1}" + if method_info: + method_info += "\\\n\t\t" + method_info += f"method_info.arguments.push_back(GetTypeInfo<m_type{i + 1}>::get_class_info());\\\n" + method_info += f"\t\tmethod_info.arguments_metadata.push_back(GetTypeInfo<m_type{i + 1}>::METADATA);" if argcount: - callsiargs += "};\\\n" - callsiargptrs += "};\\\n" + callsiargs += " };\\\n" + callsiargptrs += " };" s = s.replace("$CALLSIARGS", callsiargs + callsiargptrs) - s = s.replace("$CALLSIARGPASS", "(const Variant **)vargptrs," + str(argcount)) - callptrargsptr += "};\\\n" + s = s.replace("$CALLSIARGPASS", f"(const Variant **)vargptrs, {argcount}") + callptrargsptr += " };" s = s.replace("$CALLPTRARGS", callptrargs + callptrargsptr) - s = s.replace("$CALLPTRARGPASS", "reinterpret_cast<GDExtensionConstTypePtr*>(argptrs)") + s = s.replace("$CALLPTRARGPASS", "reinterpret_cast<GDExtensionConstTypePtr *>(argptrs)") else: - s = s.replace("$CALLSIARGS", "") + s = s.replace("\t\t\t$CALLSIARGS\\\n", "") s = s.replace("$CALLSIARGPASS", "nullptr, 0") - s = s.replace("$CALLPTRARGS", "") + s = s.replace("\t\t\t$CALLPTRARGS\\\n", "") s = s.replace("$CALLPTRARGPASS", "nullptr") if returns: if argcount > 0: - callargtext += "," - callargtext += " m_ret& r_ret" + callargtext += ", " + callargtext += "m_ret &r_ret" s = s.replace("$CALLSIBEGIN", "Variant ret = ") s = s.replace("$CALLSIRET", "r_ret = VariantCaster<m_ret>::cast(ret);") s = s.replace("$CALLPTRRETPASS", "&ret") s = s.replace("$CALLPTRRET", "r_ret = (m_ret)ret;") else: s = s.replace("$CALLSIBEGIN", "") - s = s.replace("$CALLSIRET", "") + s = s.replace("\t\t\t\t$CALLSIRET\\\n", "") s = s.replace("$CALLPTRRETPASS", "nullptr") - s = s.replace("$CALLPTRRET", "") + s = s.replace("\t\t\t\t$CALLPTRRET\\\n", "") - s = s.replace("$ARG", argtext) + s = s.replace(" $ARG", argtext) s = s.replace("$CALLARGS", callargtext) - s = s.replace("$FILL_METHOD_INFO", method_info) + if method_info: + s = s.replace("$FILL_METHOD_INFO", method_info) + else: + s = s.replace("\t\t$FILL_METHOD_INFO\\\n", method_info) return s @@ -171,21 +171,21 @@ def generate_version(argcount, const=False, returns=False): def run(target, source, env): max_versions = 12 - txt = """ + txt = """/* THIS FILE IS GENERATED DO NOT EDIT */ #ifndef GDVIRTUAL_GEN_H #define GDVIRTUAL_GEN_H #include "core/object/script_instance.h" #ifdef TOOLS_ENABLED -#define GDVIRTUAL_TRACK(m_virtual, m_initialized) \\ - if (_get_extension()->reloadable) {\\ - VirtualMethodTracker *tracker = memnew(VirtualMethodTracker);\\ - tracker->method = (void **)&m_virtual;\\ - tracker->initialized = &m_initialized;\\ - tracker->next = virtual_method_list;\\ - virtual_method_list = tracker;\\ - } +#define GDVIRTUAL_TRACK(m_virtual, m_initialized)\\ + if (_get_extension()->reloadable) {\\ + VirtualMethodTracker *tracker = memnew(VirtualMethodTracker);\\ + tracker->method = (void **)&m_virtual;\\ + tracker->initialized = &m_initialized;\\ + tracker->next = virtual_method_list;\\ + virtual_method_list = tracker;\\ + } #else #define GDVIRTUAL_TRACK(m_virtual, m_initialized) #endif @@ -193,13 +193,13 @@ def run(target, source, env): """ for i in range(max_versions + 1): - txt += "/* " + str(i) + " Arguments */\n\n" + txt += f"/* {i} Arguments */\n\n" txt += generate_version(i, False, False) txt += generate_version(i, False, True) txt += generate_version(i, True, False) txt += generate_version(i, True, True) - txt += "#endif" + txt += "#endif // GDVIRTUAL_GEN_H\n" with open(target[0], "w") as f: f.write(txt) diff --git a/core/object/message_queue.cpp b/core/object/message_queue.cpp index de71295ee5..a394c957d0 100644 --- a/core/object/message_queue.cpp +++ b/core/object/message_queue.cpp @@ -35,6 +35,8 @@ #include "core/object/class_db.h" #include "core/object/script_language.h" +#include <stdio.h> + #ifdef DEV_ENABLED // Includes safety 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; diff --git a/core/object/script_language.cpp b/core/object/script_language.cpp index 086f8a666e..3b274ecc2f 100644 --- a/core/object/script_language.cpp +++ b/core/object/script_language.cpp @@ -631,6 +631,10 @@ bool PlaceHolderScriptInstance::has_method(const StringName &p_method) const { void PlaceHolderScriptInstance::update(const List<PropertyInfo> &p_properties, const HashMap<StringName, Variant> &p_values) { HashSet<StringName> new_values; for (const PropertyInfo &E : p_properties) { + if (E.usage & (PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SUBGROUP | PROPERTY_USAGE_CATEGORY)) { + continue; + } + StringName n = E.name; new_values.insert(n); diff --git a/core/os/os.cpp b/core/os/os.cpp index f5d55ca107..26ae286979 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -626,17 +626,22 @@ String OS::get_benchmark_file() { return benchmark_file; } -void OS::benchmark_begin_measure(const String &p_what) { +void OS::benchmark_begin_measure(const String &p_context, const String &p_what) { #ifdef TOOLS_ENABLED - start_benchmark_from[p_what] = OS::get_singleton()->get_ticks_usec(); + Pair<String, String> mark_key(p_context, p_what); + ERR_FAIL_COND_MSG(benchmark_marks_from.has(mark_key), vformat("Benchmark key '%s:%s' already exists.", p_context, p_what)); + + benchmark_marks_from[mark_key] = OS::get_singleton()->get_ticks_usec(); #endif } -void OS::benchmark_end_measure(const String &p_what) { +void OS::benchmark_end_measure(const String &p_context, const String &p_what) { #ifdef TOOLS_ENABLED - uint64_t total = OS::get_singleton()->get_ticks_usec() - start_benchmark_from[p_what]; - double total_f = double(total) / double(1000000); + Pair<String, String> mark_key(p_context, p_what); + ERR_FAIL_COND_MSG(!benchmark_marks_from.has(mark_key), vformat("Benchmark key '%s:%s' doesn't exist.", p_context, p_what)); - startup_benchmark_json[p_what] = total_f; + uint64_t total = OS::get_singleton()->get_ticks_usec() - benchmark_marks_from[mark_key]; + double total_f = double(total) / double(1000000); + benchmark_marks_final[mark_key] = total_f; #endif } @@ -645,19 +650,33 @@ void OS::benchmark_dump() { if (!use_benchmark) { return; } + if (!benchmark_file.is_empty()) { Ref<FileAccess> f = FileAccess::open(benchmark_file, FileAccess::WRITE); if (f.is_valid()) { + Dictionary benchmark_marks; + for (const KeyValue<Pair<String, String>, double> &E : benchmark_marks_final) { + const String mark_key = vformat("[%s] %s", E.key.first, E.key.second); + benchmark_marks[mark_key] = E.value; + } + Ref<JSON> json; json.instantiate(); - f->store_string(json->stringify(startup_benchmark_json, "\t", false, true)); + f->store_string(json->stringify(benchmark_marks, "\t", false, true)); } } else { - List<Variant> keys; - startup_benchmark_json.get_key_list(&keys); + HashMap<String, String> results; + for (const KeyValue<Pair<String, String>, double> &E : benchmark_marks_final) { + if (E.key.first == "Startup" && !results.has(E.key.first)) { + results.insert(E.key.first, "", true); // Hack to make sure "Startup" always comes first. + } + + results[E.key.first] += vformat("\t\t- %s: %.3f msec.\n", E.key.second, (E.value * 1000)); + } + print_line("BENCHMARK:"); - for (const Variant &K : keys) { - print_line("\t-", K, ": ", startup_benchmark_json[K], +" sec."); + for (const KeyValue<String, String> &E : results) { + print_line(vformat("\t[%s]\n%s", E.key, E.value)); } } #endif diff --git a/core/os/os.h b/core/os/os.h index cc5ebe1bc8..e22514dce3 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -79,8 +79,8 @@ class OS { // For tracking benchmark data bool use_benchmark = false; String benchmark_file; - HashMap<String, uint64_t> start_benchmark_from; - Dictionary startup_benchmark_json; + HashMap<Pair<String, String>, uint64_t, PairHash<String, String>> benchmark_marks_from; + HashMap<Pair<String, String>, double, PairHash<String, String>> benchmark_marks_final; protected: void _set_logger(CompositeLogger *p_logger); @@ -313,8 +313,8 @@ public: bool is_use_benchmark_set(); void set_benchmark_file(const String &p_benchmark_file); String get_benchmark_file(); - virtual void benchmark_begin_measure(const String &p_what); - virtual void benchmark_end_measure(const String &p_what); + virtual void benchmark_begin_measure(const String &p_context, const String &p_what); + virtual void benchmark_end_measure(const String &p_context, const String &p_what); virtual void benchmark_dump(); virtual void process_and_drop_events() {} diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp index 4ad9dd43c4..2785d1daa5 100644 --- a/core/register_core_types.cpp +++ b/core/register_core_types.cpp @@ -120,7 +120,8 @@ static ResourceUID *resource_uid = nullptr; static bool _is_core_extensions_registered = false; void register_core_types() { - OS::get_singleton()->benchmark_begin_measure("register_core_types"); + OS::get_singleton()->benchmark_begin_measure("Core", "Register Types"); + //consistency check static_assert(sizeof(Callable) <= 16); @@ -296,7 +297,7 @@ void register_core_types() { worker_thread_pool = memnew(WorkerThreadPool); - OS::get_singleton()->benchmark_end_measure("register_core_types"); + OS::get_singleton()->benchmark_end_measure("Core", "Register Types"); } void register_core_settings() { @@ -311,6 +312,8 @@ void register_core_settings() { } void register_core_singletons() { + OS::get_singleton()->benchmark_begin_measure("Core", "Register Singletons"); + GDREGISTER_CLASS(ProjectSettings); GDREGISTER_ABSTRACT_CLASS(IP); GDREGISTER_CLASS(core_bind::Geometry2D); @@ -346,25 +349,35 @@ void register_core_singletons() { Engine::get_singleton()->add_singleton(Engine::Singleton("GDExtensionManager", GDExtensionManager::get_singleton())); Engine::get_singleton()->add_singleton(Engine::Singleton("ResourceUID", ResourceUID::get_singleton())); Engine::get_singleton()->add_singleton(Engine::Singleton("WorkerThreadPool", worker_thread_pool)); + + OS::get_singleton()->benchmark_end_measure("Core", "Register Singletons"); } void register_core_extensions() { + OS::get_singleton()->benchmark_begin_measure("Core", "Register Extensions"); + // Hardcoded for now. GDExtension::initialize_gdextensions(); gdextension_manager->load_extensions(); gdextension_manager->initialize_extensions(GDExtension::INITIALIZATION_LEVEL_CORE); _is_core_extensions_registered = true; + + OS::get_singleton()->benchmark_end_measure("Core", "Register Extensions"); } void unregister_core_extensions() { + OS::get_singleton()->benchmark_begin_measure("Core", "Unregister Extensions"); + if (_is_core_extensions_registered) { gdextension_manager->deinitialize_extensions(GDExtension::INITIALIZATION_LEVEL_CORE); } GDExtension::finalize_gdextensions(); + + OS::get_singleton()->benchmark_end_measure("Core", "Unregister Extensions"); } void unregister_core_types() { - OS::get_singleton()->benchmark_begin_measure("unregister_core_types"); + OS::get_singleton()->benchmark_begin_measure("Core", "Unregister Types"); // Destroy singletons in reverse order to ensure dependencies are not broken. @@ -435,5 +448,5 @@ void unregister_core_types() { CoreStringNames::free(); StringName::cleanup(); - OS::get_singleton()->benchmark_end_measure("unregister_core_types"); + OS::get_singleton()->benchmark_end_measure("Core", "Unregister Types"); } diff --git a/core/string/translation.cpp b/core/string/translation.cpp index a443ed308d..8fcf2b24b5 100644 --- a/core/string/translation.cpp +++ b/core/string/translation.cpp @@ -518,8 +518,12 @@ String TranslationServer::get_country_name(const String &p_country) const { } void TranslationServer::set_locale(const String &p_locale) { - locale = standardize_locale(p_locale); + String new_locale = standardize_locale(p_locale); + if (locale == new_locale) { + return; + } + locale = new_locale; ResourceLoader::reload_translation_remaps(); if (OS::get_singleton()->get_main_loop()) { diff --git a/core/templates/hashfuncs.h b/core/templates/hashfuncs.h index 2a212f3dcb..05960292f5 100644 --- a/core/templates/hashfuncs.h +++ b/core/templates/hashfuncs.h @@ -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); } diff --git a/doc/classes/BaseMaterial3D.xml b/doc/classes/BaseMaterial3D.xml index c3295d854f..653397ebc3 100644 --- a/doc/classes/BaseMaterial3D.xml +++ b/doc/classes/BaseMaterial3D.xml @@ -501,22 +501,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. diff --git a/doc/classes/CameraAttributesPhysical.xml b/doc/classes/CameraAttributesPhysical.xml index 69af64b7ff..faedfee712 100644 --- a/doc/classes/CameraAttributesPhysical.xml +++ b/doc/classes/CameraAttributesPhysical.xml @@ -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"> diff --git a/doc/classes/CanvasItem.xml b/doc/classes/CanvasItem.xml index 5a2df0e8a4..2f76f64cff 100644 --- a/doc/classes/CanvasItem.xml +++ b/doc/classes/CanvasItem.xml @@ -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. diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml index 6edd8af7cc..ec051c0545 100644 --- a/doc/classes/EditorSettings.xml +++ b/doc/classes/EditorSettings.xml @@ -665,8 +665,8 @@ <member name="interface/theme/icon_and_font_color" type="int" setter="" getter=""> The icon and font color scheme to use in the editor. - [b]Auto[/b] determines the color scheme to use automatically based on [member interface/theme/base_color]. - - [b]Dark[/b] makes fonts and icons light (suitable for dark themes). - - [b]Light[/b] makes fonts and icons dark (suitable for light themes). Icon colors are automatically converted by the editor following [url=https://github.com/godotengine/godot/blob/master/editor/editor_themes.cpp#L135]this set of rules[/url]. + - [b]Dark[/b] makes fonts and icons dark (suitable for light themes). Icon colors are automatically converted by the editor following the set of rules defined in [url=https://github.com/godotengine/godot/blob/master/editor/editor_themes.cpp]this file[/url]. + - [b]Light[/b] makes fonts and icons light (suitable for dark themes). </member> <member name="interface/theme/icon_saturation" type="float" setter="" getter=""> The saturation to use for editor icons. Higher values result in more vibrant colors. diff --git a/doc/classes/GPUParticles2D.xml b/doc/classes/GPUParticles2D.xml index 2308ec43c5..f4ba305f8b 100644 --- a/doc/classes/GPUParticles2D.xml +++ b/doc/classes/GPUParticles2D.xml @@ -37,6 +37,7 @@ <param index="4" name="flags" type="int" /> <description> Emits a single particle. Whether [param xform], [param velocity], [param color] and [param custom] are applied depends on the value of [param flags]. See [enum EmitFlags]. + The default ParticleProcessMaterial will overwrite [param color] and use the contents of [param custom] as [code](rotation, age, animation, lifetime)[/code]. </description> </method> <method name="restart"> diff --git a/doc/classes/GPUParticles3D.xml b/doc/classes/GPUParticles3D.xml index b5af63a8f4..d1903b85cd 100644 --- a/doc/classes/GPUParticles3D.xml +++ b/doc/classes/GPUParticles3D.xml @@ -35,6 +35,7 @@ <param index="4" name="flags" type="int" /> <description> Emits a single particle. Whether [param xform], [param velocity], [param color] and [param custom] are applied depends on the value of [param flags]. See [enum EmitFlags]. + The default ParticleProcessMaterial will overwrite [param color] and use the contents of [param custom] as [code](rotation, age, animation, lifetime)[/code]. </description> </method> <method name="get_draw_pass_mesh" qualifiers="const"> diff --git a/doc/classes/NavigationLink2D.xml b/doc/classes/NavigationLink2D.xml index b12051b4f4..75b691aaf4 100644 --- a/doc/classes/NavigationLink2D.xml +++ b/doc/classes/NavigationLink2D.xml @@ -29,6 +29,12 @@ Returns whether or not the specified layer of the [member navigation_layers] bitmask is enabled, given a [param layer_number] between 1 and 32. </description> </method> + <method name="get_rid" qualifiers="const"> + <return type="RID" /> + <description> + Returns the [RID] of this link on the [NavigationServer2D]. + </description> + </method> <method name="set_global_end_position"> <return type="void" /> <param index="0" name="position" type="Vector2" /> diff --git a/doc/classes/NavigationLink3D.xml b/doc/classes/NavigationLink3D.xml index 90eaaaee6d..711c637dc5 100644 --- a/doc/classes/NavigationLink3D.xml +++ b/doc/classes/NavigationLink3D.xml @@ -29,6 +29,12 @@ Returns whether or not the specified layer of the [member navigation_layers] bitmask is enabled, given a [param layer_number] between 1 and 32. </description> </method> + <method name="get_rid" qualifiers="const"> + <return type="RID" /> + <description> + Returns the [RID] of this link on the [NavigationServer3D]. + </description> + </method> <method name="set_global_end_position"> <return type="void" /> <param index="0" name="position" type="Vector3" /> diff --git a/doc/classes/NavigationRegion2D.xml b/doc/classes/NavigationRegion2D.xml index ef660305f4..089359da7a 100644 --- a/doc/classes/NavigationRegion2D.xml +++ b/doc/classes/NavigationRegion2D.xml @@ -43,7 +43,14 @@ Returns the current navigation map [RID] used by this region. </description> </method> - <method name="get_region_rid" qualifiers="const"> + <method name="get_region_rid" qualifiers="const" is_deprecated="true"> + <return type="RID" /> + <description> + Returns the [RID] of this region on the [NavigationServer2D]. + [i]Deprecated.[/i] Use [method get_rid] instead. + </description> + </method> + <method name="get_rid" qualifiers="const"> <return type="RID" /> <description> Returns the [RID] of this region on the [NavigationServer2D]. Combined with [method NavigationServer2D.map_get_closest_point_owner] can be used to identify the [NavigationRegion2D] closest to a point on the merged navigation map. diff --git a/doc/classes/NavigationRegion3D.xml b/doc/classes/NavigationRegion3D.xml index 3257160485..4415c10210 100644 --- a/doc/classes/NavigationRegion3D.xml +++ b/doc/classes/NavigationRegion3D.xml @@ -36,7 +36,14 @@ Returns the current navigation map [RID] used by this region. </description> </method> - <method name="get_region_rid" qualifiers="const"> + <method name="get_region_rid" qualifiers="const" is_deprecated="true"> + <return type="RID" /> + <description> + Returns the [RID] of this region on the [NavigationServer3D]. + [i]Deprecated.[/i] Use [method get_rid] instead. + </description> + </method> + <method name="get_rid" qualifiers="const"> <return type="RID" /> <description> Returns the [RID] of this region on the [NavigationServer3D]. Combined with [method NavigationServer3D.map_get_closest_point_owner] can be used to identify the [NavigationRegion3D] closest to a point on the merged navigation map. diff --git a/doc/classes/NavigationServer2D.xml b/doc/classes/NavigationServer2D.xml index a25f048df3..e1c79ad8a8 100644 --- a/doc/classes/NavigationServer2D.xml +++ b/doc/classes/NavigationServer2D.xml @@ -445,6 +445,17 @@ Returns the navigation path to reach the destination from the origin. [param navigation_layers] is a bitmask of all region navigation layers that are allowed to be in the path. </description> </method> + <method name="map_get_random_point" qualifiers="const"> + <return type="Vector2" /> + <param index="0" name="map" type="RID" /> + <param index="1" name="navigation_layers" type="int" /> + <param index="2" name="uniformly" type="bool" /> + <description> + Returns a random position picked from all map region polygons with matching [param navigation_layers]. + If [param uniformly] is [code]true[/code], all map regions, polygons, and faces are weighted by their surface area (slower). + If [param uniformly] is [code]false[/code], just a random region and a random polygon are picked (faster). + </description> + </method> <method name="map_get_regions" qualifiers="const"> <return type="RID[]" /> <param index="0" name="map" type="RID" /> @@ -681,6 +692,17 @@ Returns the [code]ObjectID[/code] of the object which manages this region. </description> </method> + <method name="region_get_random_point" qualifiers="const"> + <return type="Vector2" /> + <param index="0" name="region" type="RID" /> + <param index="1" name="navigation_layers" type="int" /> + <param index="2" name="uniformly" type="bool" /> + <description> + Returns a random position picked from all region polygons with matching [param navigation_layers]. + If [param uniformly] is [code]true[/code], all region polygons and faces are weighted by their surface area (slower). + If [param uniformly] is [code]false[/code], just a random polygon and face is picked (faster). + </description> + </method> <method name="region_get_travel_cost" qualifiers="const"> <return type="float" /> <param index="0" name="region" type="RID" /> diff --git a/doc/classes/NavigationServer3D.xml b/doc/classes/NavigationServer3D.xml index b56b86f435..46b6697947 100644 --- a/doc/classes/NavigationServer3D.xml +++ b/doc/classes/NavigationServer3D.xml @@ -502,6 +502,17 @@ Returns the navigation path to reach the destination from the origin. [param navigation_layers] is a bitmask of all region navigation layers that are allowed to be in the path. </description> </method> + <method name="map_get_random_point" qualifiers="const"> + <return type="Vector3" /> + <param index="0" name="map" type="RID" /> + <param index="1" name="navigation_layers" type="int" /> + <param index="2" name="uniformly" type="bool" /> + <description> + Returns a random position picked from all map region polygons with matching [param navigation_layers]. + If [param uniformly] is [code]true[/code], all map regions, polygons, and faces are weighted by their surface area (slower). + If [param uniformly] is [code]false[/code], just a random region and a random polygon are picked (faster). + </description> + </method> <method name="map_get_regions" qualifiers="const"> <return type="RID[]" /> <param index="0" name="map" type="RID" /> @@ -793,6 +804,17 @@ Returns the [code]ObjectID[/code] of the object which manages this region. </description> </method> + <method name="region_get_random_point" qualifiers="const"> + <return type="Vector3" /> + <param index="0" name="region" type="RID" /> + <param index="1" name="navigation_layers" type="int" /> + <param index="2" name="uniformly" type="bool" /> + <description> + Returns a random position picked from all region polygons with matching [param navigation_layers]. + If [param uniformly] is [code]true[/code], all region polygons and faces are weighted by their surface area (slower). + If [param uniformly] is [code]false[/code], just a random polygon and face is picked (faster). + </description> + </method> <method name="region_get_travel_cost" qualifiers="const"> <return type="float" /> <param index="0" name="region" type="RID" /> diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 3fc9e94427..0e82bc1497 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -2778,9 +2778,11 @@ </member> <member name="xr/openxr/foveation_dynamic" type="bool" setter="" getter="" default="false"> If true and foveation is supported, will automatically adjust foveation level based on framerate up to the level set on [member xr/openxr/foveation_level]. + [b]Note:[/b] Only works on compatibility renderer. </member> <member name="xr/openxr/foveation_level" type="int" setter="" getter="" default=""0""> Applied foveation level if supported: 0 = off, 1 = low, 2 = medium, 3 = high. + [b]Note:[/b] Only works on compatibility renderer. </member> <member name="xr/openxr/reference_space" type="int" setter="" getter="" default=""1""> Specify the default reference space. diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index da07582773..9c72abdef0 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -1362,7 +1362,7 @@ <param index="0" name="swap_buffers" type="bool" default="true" /> <param index="1" name="frame_step" type="float" default="0.0" /> <description> - Forces redrawing of all viewports at once. + Forces redrawing of all viewports at once. Must be called from the main thread. </description> </method> <method name="force_sync"> @@ -5130,22 +5130,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. diff --git a/doc/classes/VideoStreamPlayer.xml b/doc/classes/VideoStreamPlayer.xml index 0d8776b0b7..f903f171d1 100644 --- a/doc/classes/VideoStreamPlayer.xml +++ b/doc/classes/VideoStreamPlayer.xml @@ -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> diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml index 1b5f7148ac..b6a407e042 100644 --- a/doc/classes/Viewport.xml +++ b/doc/classes/Viewport.xml @@ -560,16 +560,18 @@ Draws the internal resolution buffer of the scene before post-processing is applied. </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. diff --git a/doc/classes/VisualShaderNodeTextureParameter.xml b/doc/classes/VisualShaderNodeTextureParameter.xml index 79a0657156..aa64016746 100644 --- a/doc/classes/VisualShaderNodeTextureParameter.xml +++ b/doc/classes/VisualShaderNodeTextureParameter.xml @@ -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. diff --git a/doc/tools/make_rst.py b/doc/tools/make_rst.py index 6a99bfbe06..c3a21f3d7b 100755 --- a/doc/tools/make_rst.py +++ b/doc/tools/make_rst.py @@ -1361,7 +1361,7 @@ def make_enum(t: str, is_bitfield: bool, state: State) -> str: if is_bitfield: if not state.classes[c].enums[e].is_bitfield: print_error(f'{state.current_class}.xml: Enum "{t}" is not bitfield.', state) - return f"|bitfield|\<:ref:`{e}<enum_{c}_{e}>`\>" + return f"|bitfield|\\<:ref:`{e}<enum_{c}_{e}>`\\>" else: return f":ref:`{e}<enum_{c}_{e}>`" @@ -1552,16 +1552,11 @@ def make_rst_index(grouped_classes: Dict[str, List[str]], dry_run: bool, output_ f.write(".. _doc_class_reference:\n\n") - main_title = translate("All classes") - f.write(f"{main_title}\n") - f.write(f"{'=' * len(main_title)}\n\n") + f.write(make_heading("All classes", "=")) for group_name in CLASS_GROUPS: if group_name in grouped_classes: - group_title = translate(CLASS_GROUPS[group_name]) - - f.write(f"{group_title}\n") - f.write(f"{'=' * len(group_title)}\n\n") + f.write(make_heading(CLASS_GROUPS[group_name], "=")) f.write(".. toctree::\n") f.write(" :maxdepth: 1\n") @@ -2082,9 +2077,9 @@ def format_text_block( post_text = text[endurl_pos + 6 :] if pre_text and pre_text[-1] not in MARKUP_ALLOWED_PRECEDENT: - pre_text += "\ " + pre_text += "\\ " if post_text and post_text[0] not in MARKUP_ALLOWED_SUBSEQUENT: - post_text = "\ " + post_text + post_text = "\\ " + post_text text = pre_text + tag_text + post_text pos = len(pre_text) + len(tag_text) @@ -2162,9 +2157,9 @@ def format_text_block( # Properly escape things like `[Node]s` if escape_pre and pre_text and pre_text[-1] not in MARKUP_ALLOWED_PRECEDENT: - pre_text += "\ " + pre_text += "\\ " if escape_post and post_text and post_text[0] not in MARKUP_ALLOWED_SUBSEQUENT: - post_text = "\ " + post_text + post_text = "\\ " + post_text next_brac_pos = post_text.find("[", 0) iter_pos = 0 @@ -2172,7 +2167,7 @@ def format_text_block( iter_pos = post_text.find("*", iter_pos, next_brac_pos) if iter_pos == -1: break - post_text = f"{post_text[:iter_pos]}\*{post_text[iter_pos + 1 :]}" + post_text = f"{post_text[:iter_pos]}\\*{post_text[iter_pos + 1 :]}" iter_pos += 2 iter_pos = 0 @@ -2181,7 +2176,7 @@ def format_text_block( if iter_pos == -1: break if not post_text[iter_pos + 1].isalnum(): # don't escape within a snake_case word - post_text = f"{post_text[:iter_pos]}\_{post_text[iter_pos + 1 :]}" + post_text = f"{post_text[:iter_pos]}\\_{post_text[iter_pos + 1 :]}" iter_pos += 2 else: iter_pos += 1 @@ -2222,7 +2217,7 @@ def escape_rst(text: str, until_pos: int = -1) -> str: pos = text.find("*", pos, until_pos) if pos == -1: break - text = f"{text[:pos]}\*{text[pos + 1 :]}" + text = f"{text[:pos]}\\*{text[pos + 1 :]}" pos += 2 # Escape _ character at the end of a word to avoid interpreting it as an inline hyperlink @@ -2232,7 +2227,7 @@ def escape_rst(text: str, until_pos: int = -1) -> str: if pos == -1: break if not text[pos + 1].isalnum(): # don't escape within a snake_case word - text = f"{text[:pos]}\_{text[pos + 1 :]}" + text = f"{text[:pos]}\\_{text[pos + 1 :]}" pos += 2 else: pos += 1 diff --git a/doc/translations/de.po b/doc/translations/de.po index 964a1e0eb8..d6aad893bb 100644 --- a/doc/translations/de.po +++ b/doc/translations/de.po @@ -76,12 +76,13 @@ # Cerno_b <cerno.b@gmail.com>, 2023. # Janosch Lion <janorico@posteo.de>, 2023. # Tobias Mohr <tobias_mohr_1991@gmx.de>, 2023. +# Florian Schaupp <fschaupp@hotmail.com>, 2023. msgid "" msgstr "" "Project-Id-Version: Godot Engine class reference\n" "Report-Msgid-Bugs-To: https://github.com/godotengine/godot\n" -"PO-Revision-Date: 2023-11-28 16:10+0000\n" -"Last-Translator: HolonProduction <holonproduction@gmail.com>\n" +"PO-Revision-Date: 2023-12-02 19:36+0000\n" +"Last-Translator: Florian Schaupp <fschaupp@hotmail.com>\n" "Language-Team: German <https://hosted.weblate.org/projects/godot-engine/godot-" "class-reference/de/>\n" "Language: de\n" @@ -91,6 +92,15 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.3-dev\n" +msgid "All classes" +msgstr "Alle Klassen" + +msgid "Nodes" +msgstr "Nodes" + +msgid "Resources" +msgstr "Ressourcen" + msgid "Description" msgstr "Beschreibung" @@ -5976,6 +5986,13 @@ msgstr "" "[b]Hinweis:[/b] Der abschließende Doppelpunkt ist erforderlich, um eingebaute " "Typen korrekt zu erkennen." +msgid "" +"[i]Deprecated.[/i] This hint is not used anywhere and will be removed in the " +"future." +msgstr "" +"[i]Obsolet.[/i] Dieser Hinweis ist nicht mehr in Verwendung und wird in " +"Zukunft entfernt." + msgid "Hints that an object is too big to be sent via the debugger." msgstr "" "Weist darauf hin, dass ein Objekt zu groß ist, um über den Debugger gesendet " @@ -6065,6 +6082,13 @@ msgstr "" "Dient dazu, Eigenschaften im Editor in einer Untergruppe (unter einer Gruppe) " "zusammenzufassen. Siehe [EditorInspector]." +msgid "" +"The property is a bitfield, i.e. it contains multiple flags represented as " +"bits." +msgstr "" +"Die Eigenschaft ist ein Bitfeld und repräsentiert daher beispielsweise " +"mehrere Flags als Bits." + msgid "The property does not save its state in [PackedScene]." msgstr "Die Eigenschaft speichert ihren Zustand nicht in [PackedScene]." @@ -6086,6 +6110,15 @@ msgstr "" "aktualisiert." msgid "" +"Signifies a default value from a placeholder script instance.\n" +"[i]Deprecated.[/i] This hint is not used anywhere and will be removed in the " +"future." +msgstr "" +"Beschreibt einen Default-Wert aus einer Platzhalter Skript-Instanz.\n" +"[i]Obsolet.[/i] Dieser Hinweis ist nicht in Verwendung und wird zukünftig " +"entfernt." + +msgid "" "The property is an enum, i.e. it only takes named integer constants from its " "associated enumeration." msgstr "" @@ -6128,6 +6161,47 @@ msgstr "" "Die Eigenschaft wird im Editor nur angezeigt, wenn moderne Renderer " "unterstützt werden (die Rendering-Methode Kompatibilität ist ausgeschlossen)." +msgid "" +"The [NodePath] property will always be relative to the scene's root. Mostly " +"useful for local resources." +msgstr "" +"Die Eigenschaft [NodePath] ist immer relativ zur Szenen-Verzeichnis. Meist " +"für lokale Ressourcen hilfreich." + +msgid "" +"Inserting an animation key frame of this property will automatically " +"increment the value, allowing to easily keyframe multiple values in a row." +msgstr "" +"Das Setzen eines Animations-Keyframes auf diese Eigenschaft erhöht den Wert " +"automatisch. Dies ermöglicht die Verwendung von Keyframes auf mehreren Werten " +"einer Zeile." + +msgid "" +"When loading, the resource for this property can be set at the end of " +"loading.\n" +"[i]Deprecated.[/i] This hint is not used anywhere and will be removed in the " +"future." +msgstr "" +"Während dem Ladevorgang kann die Ressource der Eigenschaft auch am Ende " +"dessen gesetzt werden.\n" +"[i]Obsolet.[/i] Dieser Hinweis ist nicht in Verwendung und wird zukünftig " +"entfernt." + +msgid "" +"When this property is a [Resource] and base object is a [Node], a resource " +"instance will be automatically created whenever the node is created in the " +"editor." +msgstr "" +"Ist die Eigenschaft eine [Resource] und das Basisobjekt ein [Node], so wird " +"die Ressource instantiiert, sobald der Node im Editor erzeugt wird." + +msgid "" +"The property is considered a basic setting and will appear even when advanced " +"mode is disabled. Used for project settings." +msgstr "" +"Die Eigenschaft wird als Grundeinstellung gesehen, wodurch diese auch im " +"Standardmodus erscheint. Sie wird für Projekteinstellungen verwendet." + msgid "The property is read-only in the [EditorInspector]." msgstr "Die Eigenschaft ist im [EditorInspector] schreibgeschützt." diff --git a/doc/translations/es.po b/doc/translations/es.po index 0ed7d61c59..2ef586679c 100644 --- a/doc/translations/es.po +++ b/doc/translations/es.po @@ -65,12 +65,14 @@ # Jorge González <jlexgog@gmail.com>, 2023. # Jorge Julio Torres <jjulio.tlg.89@gmail.com>, 2023. # simomi 073 <arcemoyanomanuel@gmail.com>, 2023. +# Alejandro Ruiz Esclapez <ruizesa24@gmail.com>, 2023. +# Carlos Cortes Garcia <carlos.cortes.games@gmail.com>, 2023. msgid "" msgstr "" "Project-Id-Version: Godot Engine class reference\n" "Report-Msgid-Bugs-To: https://github.com/godotengine/godot\n" -"PO-Revision-Date: 2023-10-25 16:15+0000\n" -"Last-Translator: Jorge Julio Torres <jjulio.tlg.89@gmail.com>\n" +"PO-Revision-Date: 2023-12-11 21:00+0000\n" +"Last-Translator: Carlos Cortes Garcia <carlos.cortes.games@gmail.com>\n" "Language-Team: Spanish <https://hosted.weblate.org/projects/godot-engine/" "godot-class-reference/es/>\n" "Language: es\n" @@ -78,11 +80,29 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8-bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 5.1.1\n" +"X-Generator: Weblate 5.3-dev\n" + +msgid "All classes" +msgstr "Todas las clases" + +msgid "Globals" +msgstr "Globales" + +msgid "Nodes" +msgstr "Nodos" msgid "Resources" msgstr "Recursos" +msgid "Editor-only" +msgstr "Exclusivo-Editor" + +msgid "Other objects" +msgstr "Otros objetos" + +msgid "Variant types" +msgstr "Tipo de variantes" + msgid "Description" msgstr "Descripción" @@ -113,6 +133,9 @@ msgstr "Enumeraciones" msgid "Constants" msgstr "Constantes" +msgid "Annotations" +msgstr "Anotaciones" + msgid "Property Descriptions" msgstr "Descripciones de Propiedades" @@ -135,14 +158,11 @@ msgid "Inherited By:" msgstr "Heredado por:" msgid "(overrides %s)" -msgstr "(sobreescribe %s)" +msgstr "(sobrescribe %s)" msgid "Default" msgstr "Predeterminado" -msgid "Setter" -msgstr "Regulador o fijador" - msgid "value" msgstr "valor" @@ -152,7 +172,7 @@ msgstr "Método de Acceso al Valor o Getter" msgid "" "This method should typically be overridden by the user to have any effect." msgstr "" -"Típicamente, este método debería ser sobreescrito por el usuario para que " +"Normalmente, este método debería ser sobrescrito por el usuario para que " "tenga algún efecto." msgid "" @@ -1401,6 +1421,16 @@ msgstr "" msgid "Random number generation" msgstr "Generación de números aleatorios" +msgid "" +"Returns the difference between the two angles, in the range of [code][-PI, " +"+PI][/code]. When [param from] and [param to] are opposite, returns [code]-" +"PI[/code] if [param from] is smaller than [param to], or [code]PI[/code] " +"otherwise." +msgstr "" +"Devuelve la diferencia entre dos ángulos, entre [code][-PI,+PI][/code]. " +"Cuando [param from] y [param to] son contrarios, devuelve [code]-PI[/code] si " +"[param from] es menor que [param to], o [code]PI[/code] si no lo es." + msgid "Converts from decibels to linear energy (audio)." msgstr "Convierte de decibeles a energía lineal (audio)." diff --git a/doc/translations/zh_CN.po b/doc/translations/zh_CN.po index b1e1ef4cd4..0b5e9239a1 100644 --- a/doc/translations/zh_CN.po +++ b/doc/translations/zh_CN.po @@ -86,7 +86,7 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine class reference\n" "Report-Msgid-Bugs-To: https://github.com/godotengine/godot\n" -"PO-Revision-Date: 2023-11-26 04:03+0000\n" +"PO-Revision-Date: 2023-12-07 14:59+0000\n" "Last-Translator: 风青山 <idleman@yeah.net>\n" "Language-Team: Chinese (Simplified) <https://hosted.weblate.org/projects/" "godot-engine/godot-class-reference/zh_Hans/>\n" @@ -95,11 +95,29 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8-bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Weblate 5.2.1-rc\n" +"X-Generator: Weblate 5.3-dev\n" + +msgid "All classes" +msgstr "所有类" + +msgid "Globals" +msgstr "全局" + +msgid "Nodes" +msgstr "节点" msgid "Resources" msgstr "资源" +msgid "Editor-only" +msgstr "编辑器专用" + +msgid "Other objects" +msgstr "其他对象" + +msgid "Variant types" +msgstr "变体类型" + msgid "Description" msgstr "描述" @@ -130,6 +148,9 @@ msgstr "枚举" msgid "Constants" msgstr "常量" +msgid "Annotations" +msgstr "注解" + msgid "Property Descriptions" msgstr "属性说明" @@ -6382,8 +6403,7 @@ msgstr "返回该 [AABB] 归一化后的最短轴。" msgid "" "Returns the index of the shortest axis of the [AABB] (according to [Vector3]::" "AXIS* enum)." -msgstr "" -"返回该 [AABB] 最短轴的索引(根据 [Vector3] 的 [code]AXIS_*[/code] 常量)。" +msgstr "返回该 [AABB] 最短轴的索引(根据 [Vector3]::AXIS* 常量)。" msgid "Returns the scalar length of the shortest axis of the [AABB]." msgstr "返回该 [AABB] 最短轴的标量长度。" @@ -17171,58 +17191,81 @@ msgid "Represents the size of the [enum TextureParam] enum." msgstr "代表 [enum TextureParam] 枚举的大小。" msgid "" -"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)." msgstr "" -"纹理过滤器仅读取最邻近的像素。最简单快速的过滤方法,但纹理看起来会像素化。" +"纹理过滤仅从最近的像素读取。这使得纹理从近距离看是像素化的,从远处看是颗粒状的" +"(由于多级渐远纹理没有被采样)。" msgid "" -"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)." msgstr "" -"纹理过滤器在最邻近的 4 个像素之间混合。如果你想要避免像素化风格,但又不想使用 " -"mipmap,那么请使用这个选项。" +"纹理过滤在最近的 4 个像素之间进行混合。这使得纹理从近处看起来很平滑,从远处看" +"起来却有颗粒感(由于多级渐远纹理没有被采样)。" msgid "" -"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." msgstr "" -"纹理过滤器读取最邻近的 mipmap 中的最邻近的像素。带有 mipmap 的纹理的最快读取方" -"法。" +"纹理过滤从最近的像素读取并在最近的 2 个多级渐远纹理之间进行混合(或者如果 " +"[member ProjectSettings.rendering/textures/default_filters/" +"use_nearest_mipmap_filter] 为 [code]true[/code],则使用最近的多级渐远纹理)。" +"这使得纹理从近处看起来像素化,从远处看起来平滑。" msgid "" "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." +"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." msgstr "" -"纹理过滤器在最邻近的 4 个像素和最邻近的 2 个 mipmap 之间混合。请在大多数情况下" -"使用,因为 mipmap 对于平滑远离相机的像素很重要。" - -msgid "" -"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]." +"纹理过滤在最近的 4 个像素和最近的 2 个多级渐远纹理之间进行混合(或者如果 " +"[member ProjectSettings.rendering/textures/default_filters/" +"use_nearest_mipmap_filter] 为 [code]true[/code],则使用最近的多级渐远纹理)。" +"这使得纹理从近处看起来平滑,从远处看起来也平滑。" + +msgid "" +"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]." msgstr "" -"纹理过滤器读取最邻近的像素,但会根据表面和相机视图之间的夹角选择 mipmap。可以" -"减少几乎与相机成一直线的表面的不自然情况。各向异性过滤级别可以通过调整 " +"纹理过滤从最近的像素读取并根据表面和相机视图之间的角度在 2 个多级渐远纹理之间" +"进行混合(或者如果 [member ProjectSettings.rendering/textures/default_filters/" +"use_nearest_mipmap_filter] 为 [code]true[/code],则使用最近的多级渐远纹理)。" +"这使得纹理从近处看起来像素化,从远处看起来平滑。各向异性过滤提高了几乎与相机位" +"于一条线上的表面上的纹理质量,但速度稍慢。各向异性过滤级别可以通过调整 " "[member ProjectSettings.rendering/textures/default_filters/" "anisotropic_filtering_level] 来改变。" msgid "" -"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]." -msgstr "" -"纹理过滤器在最邻近的 4 个像素之间进行混合,并会根据表面和相机视图之间的夹角选" -"择 mipmap。可以减少几乎与相机成一直线的表面的不自然情况。这是过滤选项中最慢的" -"一个,但可以得到最高质量的纹理。各向异性过滤级别可以通过调整 [member " -"ProjectSettings.rendering/textures/default_filters/" +"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]." +msgstr "" +"纹理过滤在最近的 4 个像素之间进行混合,并基于表面与相机视图之间的角度在 2 个多" +"级渐远纹理之间进行混合 (或者如果 [member ProjectSettings.rendering/textures/" +"default_filters/use_nearest_mipmap_filter] 为 [code]true[/code],则使用最近的" +"多级渐远纹理)。这使得纹理从近处看起来平滑,从远处看起来也平滑。各向异性过滤提" +"高了几乎与相机位于一条线的表面上的纹理质量,但速度稍慢。各向异性过滤级别可以通" +"过调整 [member ProjectSettings.rendering/textures/default_filters/" "anisotropic_filtering_level] 来改变。" msgid "Represents the size of the [enum TextureFilter] enum." @@ -20118,15 +20161,16 @@ msgstr "" "use_physical_light_units] 时可用。" msgid "" -"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.\n" +"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).\n" "Only available when [member ProjectSettings.rendering/lights_and_shadows/" "use_physical_light_units] is enabled." msgstr "" -"快门打开和关闭的时间,单位:秒。较高的值将使更多的光线进入,从而使图像更亮;而" -"较低的值将使更少的光线进入,从而使图像更暗。\n" -"仅在启用 [member ProjectSettings.rendering/lights_and_shadows/" +"快门打开和关闭的时间,计算方式为 [code]1 / shutter_speed[/code] 秒。较高的值将" +"允许较少的光线(导致图像较暗),而较低的值将允许更多的光线(导致图像较亮)。\n" +"仅当启用 [member ProjectSettings.rendering/lights_and_shadows/" "use_physical_light_units] 时可用。" msgid "" @@ -21350,67 +21394,84 @@ msgid "The [CanvasItem] will inherit the filter from its parent." msgstr "该 [CanvasItem] 将从其父级继承过滤器。" msgid "" -"The texture filter reads from the nearest pixel only. The simplest and " -"fastest method of filtering. Useful for pixel art." -msgstr "纹理过滤器仅读取最邻近的像素。最简单、最快的过滤方法。可用于像素画。" - -msgid "" -"The texture filter blends between the nearest four pixels. Use this for most " -"cases where you want to avoid a pixelated style." -msgstr "" -"纹理过滤器在最邻近的四个像素之间混合。如果想要避免像素化样式,大多数情况下请使" -"用此选项。" - -msgid "" -"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.\n" +"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." msgstr "" -"纹理过滤器读取最邻近的 mipmap 中的最邻近像素。这是使用 mipmap 从纹理中读取的最" -"快方法。" +"纹理过滤从最近的像素读取并在最近的 2 个多级渐远纹理之间进行混合(或者如果 " +"[member ProjectSettings.rendering/textures/default_filters/" +"use_nearest_mipmap_filter] 为 [code]true[/code],则使用最近的多级渐远纹理)。" +"这使得纹理从近处看起来像素化,从远处看起来平滑。\n" +"将此用于可能以低缩放查看的非像素艺术纹理(例如,由于 [Camera2D] 缩放或精灵缩" +"放),因为多级渐远纹理对于平滑小于屏幕像素的像素很重要。" msgid "" "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 " +"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.\n" +"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." msgstr "" -"纹理过滤器在最邻近的 4 个像素和最邻近的 2 个 mipmap 之间混合。请用于可能以低缩" -"放率查看的非像素画纹理(例如由 [Camera2D] 缩放造成),因为 mipmap 对于平滑小于" -"屏幕像素的像素很重要。" - -msgid "" -"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].\n" +"纹理过滤在最近的 4 个像素和最近的 2 个多级渐远纹理之间进行混合(或者如果 " +"[member ProjectSettings.rendering/textures/default_filters/" +"use_nearest_mipmap_filter] 为 [code]true[/code],则使用最近的多级渐远纹理)。" +"这使得纹理从近处看起来平滑,从远处看起来也平滑。\n" +"将此用于可能以低缩放查看的非像素艺术纹理(例如,由于 [Camera2D] 缩放或精灵缩" +"放),因为多级渐远纹理对于平滑小于屏幕像素的像素很重要。" + +msgid "" +"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].\n" "[b]Note:[/b] This texture filter is rarely useful in 2D projects. [constant " -"TEXTURE_FILTER_NEAREST_WITH_MIPMAPS] is usually more appropriate." +"TEXTURE_FILTER_NEAREST_WITH_MIPMAPS] is usually more appropriate in this case." msgstr "" -"纹理过滤器读取最邻近的像素,但会根据表面和相机视图之间的角度选择 mipmap。可以" -"减少几乎与相机成一直线的表面的伪影。各向异性过滤级别可以通过调整 [member " +"纹理过滤从最近的像素读取并根据表面和相机视图之间的角度在 2 个多级渐远纹理之间" +"进行混合(或者如果 [member ProjectSettings.rendering/textures/default_filters/" +"use_nearest_mipmap_filter] 为 [code]true[/code],则使用最近的多级渐远纹理)。" +"这使得纹理从近处看起来像素化,从远处看起来平滑。各向异性过滤提高了几乎与相机位" +"于一条线的表面上的纹理质量,但速度稍慢。各向异性过滤级别可以通过调整 [member " "ProjectSettings.rendering/textures/default_filters/" "anisotropic_filtering_level] 来改变。\n" -"[b]注意:[/b]这个纹理过滤器很少用于 2D 项目。[constant " -"TEXTURE_FILTER_NEAREST_WITH_MIPMAPS] 通常更合适。" - -msgid "" -"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].\n" +"[b]注意:[/b]该纹理过滤在 2D 项目中很少有用。[constant " +"TEXTURE_FILTER_NEAREST_WITH_MIPMAPS] 在这种情况下通常更合适。" + +msgid "" +"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].\n" "[b]Note:[/b] This texture filter is rarely useful in 2D projects. [constant " -"TEXTURE_FILTER_LINEAR_WITH_MIPMAPS] is usually more appropriate." -msgstr "" -"纹理过滤器在最邻近的 4 个像素之间进行混合,并会根据表面和相机视图之间的角度选" -"择 mipmap。可以减少几乎与相机成一直线的表面的伪影。这是最慢的过滤选项,但可以" -"得到最高质量的纹理。各向异性过滤级别可以通过调整 [member ProjectSettings." -"rendering/textures/default_filters/anisotropic_filtering_level] 来改变。\n" -"[b]注意:[/b]这个纹理过滤器很少用于 2D 项目。[constant " -"TEXTURE_FILTER_NEAREST_WITH_MIPMAPS] 通常更合适。" +"TEXTURE_FILTER_LINEAR_WITH_MIPMAPS] is usually more appropriate in this case." +msgstr "" +"纹理过滤在最近的 4 个像素之间进行混合,并基于表面与相机视图之间的角度在 2 个多" +"级渐远纹理之间进行混合(或者如果 [member ProjectSettings.rendering/textures/" +"default_filters/use_nearest_mipmap_filter] 为 [code]true[/code],则使用最近的" +"多级渐远纹理)。这使得纹理从近处看起来平滑,从远处看起来也平滑。各向异性过滤提" +"高了几乎与相机位于一条线的表面上的纹理质量,但速度稍慢。各向异性过滤级别可以通" +"过调整 [member ProjectSettings.rendering/textures/default_filters/" +"anisotropic_filtering_level] 来改变。\n" +"[b]注意:[/b]该纹理过滤在 2D 项目中很少有用。[constant " +"TEXTURE_FILTER_LINEAR_WITH_MIPMAPS] 在这种情况下通常更合适。" msgid "Texture will not repeat." msgstr "纹理不会重复。" @@ -24219,7 +24280,7 @@ msgid "" msgstr "将碰撞形状的形状设置为其所有凸面 [MeshInstance3D] 兄弟几何体的相加。" msgid "[i]Obsoleted.[/i] Use [signal Resource.changed] instead." -msgstr "[i]已废弃 [/i] 请改用 [signal Resource.changed] 。" +msgstr "[i]已废弃。[/i]请改用 [signal Resource.changed] 。" msgid "A disabled collision shape has no effect in the world." msgstr "禁用的碰撞形状对世界没有任何影响。" @@ -45480,19 +45541,19 @@ msgid "" "The icon and font color scheme to use in the editor.\n" "- [b]Auto[/b] determines the color scheme to use automatically based on " "[member interface/theme/base_color].\n" -"- [b]Dark[/b] makes fonts and icons light (suitable for dark themes).\n" -"- [b]Light[/b] makes fonts and icons dark (suitable for light themes). Icon " -"colors are automatically converted by the editor following [url=https://" -"github.com/godotengine/godot/blob/master/editor/editor_themes.cpp#L135]this " -"set of rules[/url]." +"- [b]Dark[/b] makes fonts and icons dark (suitable for light themes). Icon " +"colors are automatically converted by the editor following the set of rules " +"defined in [url=https://github.com/godotengine/godot/blob/master/editor/" +"editor_themes.cpp]this file[/url].\n" +"- [b]Light[/b] makes fonts and icons light (suitable for dark themes)." msgstr "" "在编辑器中使用的图标和字体的配色方案。\n" "- [b]Auto[/b] 根据 [member interface/theme/base_color] 自动确定要使用的配色方" "案。\n" -"- [b]Dark[/b] 使字体和图标变亮(适合深色主题)。\n" -"- [b]Light[/b] 使字体和图标变暗(适合浅色主题)。图标颜色由编辑器按照" +"- [b]Dark[/b] 使字体和图标变暗(适合浅色主题)。图标颜色由编辑器按照" "[url=https://github.com/godotengine/godot/blob/master/editor/editor_themes." -"cpp#L135]这组规则[/url]自动转换。" +"cpp]该文件[/url]中定义的一组规则自动转换。\n" +"- [b]Light[/b] 使字体和图标变亮(适合深色主题)。" msgid "" "The saturation to use for editor icons. Higher values result in more vibrant " @@ -70943,6 +71004,9 @@ msgid "" "position." msgstr "返回该链接的 [member start_position] 的全局位置。" +msgid "Returns the [RID] of this link on the [NavigationServer2D]." +msgstr "返回 [NavigationServer2D] 上该链接的 [RID]。" + msgid "" "Sets the [member end_position] that is relative to the link from a global " "[param position]." @@ -71032,6 +71096,9 @@ msgstr "" "接可以用来表达沿着导航网格表面行进以外的导航方法,例如滑锁、传送、跳过沟壑等" "等。" +msgid "Returns the [RID] of this link on the [NavigationServer3D]." +msgstr "返回 [NavigationServer3D] 上该链接的 [RID]。" + msgid "" "Whether this link is currently active. If [code]false[/code], [method " "NavigationServer3D.map_get_path] will ignore this link." @@ -72150,6 +72217,13 @@ msgid "Returns the current navigation map [RID] used by this region." msgstr "返回该区块使用的当前导航地图 [RID]。" msgid "" +"Returns the [RID] of this region on the [NavigationServer2D].\n" +"[i]Deprecated.[/i] Use [method get_rid] instead." +msgstr "" +"返回 [NavigationServer2D] 上该区块的 [RID]。\n" +"[i]已废弃。[/i]请改用 [method get_rid]。" + +msgid "" "Returns the [RID] of this region on the [NavigationServer2D]. Combined with " "[method NavigationServer2D.map_get_closest_point_owner] can be used to " "identify the [NavigationRegion2D] closest to a point on the merged navigation " @@ -72284,6 +72358,13 @@ msgstr "" "用独立线程烘焙。" msgid "" +"Returns the [RID] of this region on the [NavigationServer3D].\n" +"[i]Deprecated.[/i] Use [method get_rid] instead." +msgstr "" +"返回 [NavigationServer3D] 上该区块的 [RID]。\n" +"[i]已废弃。[/i]请改用 [method get_rid]。" + +msgid "" "Returns the [RID] of this region on the [NavigationServer3D]. Combined with " "[method NavigationServer3D.map_get_closest_point_owner] can be used to " "identify the [NavigationRegion3D] closest to a point on the merged navigation " @@ -110908,28 +110989,52 @@ msgid "Uses the default filter mode for this [Viewport]." msgstr "为 [Viewport] 使用默认过滤模式。" msgid "" -"The texture filter blends between the nearest 4 pixels and between the " -"nearest 2 mipmaps." -msgstr "纹理过滤在最近的 4 个像素之间和最近的 2 个 mipmap 之间混合。" - -msgid "" -"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." -msgstr "" -"纹理过滤从最近的像素读取,但根据表面和相机视图之间的角度选择一个 mipmap。这减" -"少了几乎与相机共线的表面上的伪影。" - -msgid "" -"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." -msgstr "" -"纹理过滤在最近的 4 个像素之间进行混合,并根据表面和相机视图之间的角度选择一个 " -"mipmap。这减少了几乎与相机共线的表面上的伪影。这是最慢的过滤选项,但会产生最高" -"质量的纹理。" +"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].\n" +"[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." +msgstr "" +"纹理过滤从最近的像素读取并根据表面和相机视图之间的角度在 2 个多级渐远纹理之间" +"进行混合(或者如果 [member ProjectSettings.rendering/textures/default_filters/" +"use_nearest_mipmap_filter] 为 [code]true[/code],则使用最近的多级渐远纹理)。" +"这使得纹理从近处看起来像素化,从远处看起来平滑。各向异性过滤提高了几乎与相机位" +"于一条线的表面上的纹理质量,但速度稍慢。各向异性过滤级别可以通过调整 [member " +"ProjectSettings.rendering/textures/default_filters/" +"anisotropic_filtering_level] 来改变。\n" +"[b]注意:[/b]该纹理过滤在 2D 项目中很少有用。[constant " +"CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS] 在这种情况下通常更合适。" + +msgid "" +"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].\n" +"[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." +msgstr "" +"纹理过滤在最近的 4 个像素之间进行混合,并基于表面与相机视图之间的角度在 2 个多" +"级渐远纹理之间进行混合(或者如果 [member ProjectSettings.rendering/textures/" +"default_filters/use_nearest_mipmap_filter] 为 [code]true[/code],则使用最近的" +"多级渐远纹理)。这使得纹理从近处看起来平滑,从远处看起来也平滑。各向异性过滤提" +"高了几乎与相机位于一条线的表面上的纹理质量,但速度稍慢。各向异性过滤级别可以通" +"过调整 [member ProjectSettings.rendering/textures/default_filters/" +"anisotropic_filtering_level] 来改变。\n" +"[b]注意:[/b]该纹理过滤在 2D 项目中很少有用。[constant " +"CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS] 在这种情况下通常更合适。" msgid "Max value for [enum CanvasItemTextureFilter] enum." msgstr "[enum CanvasItemTextureFilter] 枚举的最大值。" @@ -116070,6 +116175,55 @@ msgstr "" "set_deferred] 类似。" msgid "" +"Changes the running scene to the one at the given [param path], after loading " +"it into a [PackedScene] and creating a new instance.\n" +"Returns [constant OK] on success, [constant ERR_CANT_OPEN] if the [param " +"path] cannot be loaded into a [PackedScene], or [constant ERR_CANT_CREATE] if " +"that scene cannot be instantiated.\n" +"[b]Note:[/b] See [method change_scene_to_packed] for details on the order of " +"operations." +msgstr "" +"将位于给定路径 [param path] 的场景加载进一个 [PackedScene] 并新建其实例,然后" +"将正在运行的场景修改为这个场景。\n" +"成功时返回 [constant OK];如果 [param path] 不能被加载到一个 [PackedScene] " +"中,则返回 [constant ERR_CANT_OPEN];如果该场景无法被实例化,则返回 [constant " +"ERR_CANT_CREATE]。\n" +"[b]注意:[/b]有关操作顺序的详细信息,请参阅 [method change_scene_to_packed]。" + +msgid "" +"Changes the running scene to a new instance of the given [PackedScene] (which " +"must be valid).\n" +"Returns [constant OK] on success, [constant ERR_CANT_CREATE] if the scene " +"cannot be instantiated, or [constant ERR_INVALID_PARAMETER] if the scene is " +"invalid.\n" +"[b]Note:[/b] Operations happen in the following order when [method " +"change_scene_to_packed] is called:\n" +"1. The current scene node is immediately removed from the tree. From that " +"point, [method Node.get_tree] called on the current (outgoing) scene will " +"return [code]null[/code]. [member current_scene] will be [code]null[/code], " +"too, because the new scene is not available yet.\n" +"2. At the end of the frame, the formerly current scene, already removed from " +"the tree, will be deleted (freed from memory) and then the new scene will be " +"instantiated and added to the tree. [method Node.get_tree] and [member " +"current_scene] will be back to working as usual.\n" +"This ensures that both scenes aren't running at the same time, while still " +"freeing the previous scene in a safe way similar to [method Node.queue_free]." +msgstr "" +"将正在运行的场景更改为给定 [PackedScene] 的新实例(新实例必须有效)。\n" +"成功时返回 [constant OK],场景无法被实例化时返回 [constant ERR_CANT_CREATE]," +"场景无效时返回 [constant ERR_INVALID_PARAMETER]。\n" +"[b]注意:[/b]当 [method change_scene_to_packed] 被调用时,操作按以下顺序发" +"生:\n" +"1. 当前场景节点被立即从树中移除。从那时起,在当前(传出)场景上调用的 [method " +"Node.get_tree] 将返回 [code]null[/code]。[member current_scene] 也将变为 " +"[code]null[/code],因为新场景尚不可用。\n" +"2. 在帧末尾时,已从树中移除的、之前的当前场景将被删除(从内存中释放),然后新" +"场景将被实例化并添加到树中。[method Node.get_tree] 和 [member current_scene] " +"将恢复正常工作。\n" +"这确保了两个场景不会同时运行,并且仍然会以类似于 [method Node.queue_free] 的安" +"全方式释放之前的场景。" + +msgid "" "Returns a [SceneTreeTimer] which will emit [signal SceneTreeTimer.timeout] " "after the given time in seconds elapsed in this [SceneTree].\n" "If [param process_always] is set to [code]false[/code], pausing the " @@ -139065,15 +139219,12 @@ msgid "" "Supported video formats are [url=https://www.theora.org/]Ogg Theora[/url] " "([code].ogv[/code], [VideoStreamTheora]) and any format exposed via a " "GDExtension plugin.\n" -"[b]Note:[/b] Due to a bug, VideoStreamPlayer does not support localization " -"remapping yet.\n" "[b]Warning:[/b] On Web, video playback [i]will[/i] perform poorly due to " "missing architecture-specific assembly optimizations." msgstr "" "用于播放 [VideoStream] 资源的控件。\n" "支持的视频格式有 [url=https://www.theora.org/]Ogg Theora[/url]([code].ogv[/" "code],[VideoStreamTheora])以及任何通过 GDExtension 插件公开的格式。\n" -"[b]注意:[/b]由于一个错误,VideoStreamPlayer 还不支持本地化重映射。\n" "[b]警告:[/b]在 Web 上,视频播放[i]会[/i]由于缺少特定于体系结构的汇编优化而表" "现不佳。" @@ -142963,38 +143114,50 @@ msgid "" msgstr "使用由该着色器所附加到的节点决定的过滤器对纹理进行采样。" msgid "" -"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].\n" +"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].\n" "[b]Note:[/b] This texture filter is rarely useful in 2D projects. [constant " -"FILTER_LINEAR_MIPMAP] is usually more appropriate." +"FILTER_NEAREST_MIPMAP] is usually more appropriate in this case." msgstr "" -"纹理过滤从最近的像素读取,但根据表面和相机视图之间的角度选择一个 mipmap。这减" -"少了几乎与相机对齐的表面上的伪影。可以通过调整 [member ProjectSettings." -"rendering/textures/default_filters/anisotropic_filtering_level],来更改各向异" -"性过滤级别。\n" -"[b]注意:[/b]这种纹理过滤在 2D 项目中很少有用。[constant " -"FILTER_LINEAR_MIPMAP] 通常更合适。" - -msgid "" -"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].\n" +"纹理过滤从最近的像素读取并根据表面和相机视图之间的角度在 2 个多级渐远纹理之间" +"进行混合(或者如果 [member ProjectSettings.rendering/textures/default_filters/" +"use_nearest_mipmap_filter] 为 [code]true[/code],则使用最近的多级渐远纹理)。" +"这使得纹理从近处看起来像素化,从远处看起来平滑。各向异性过滤提高了几乎与相机位" +"于一条线的表面上的纹理质量,但速度稍慢。各向异性过滤级别可以通过调整 [member " +"ProjectSettings.rendering/textures/default_filters/" +"anisotropic_filtering_level] 来改变。\n" +"[b]注意:[/b]该纹理过滤在 2D 项目中很少有用。[constant FILTER_NEAREST_MIPMAP] " +"在这种情况下通常更合适。" + +msgid "" +"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].\n" "[b]Note:[/b] This texture filter is rarely useful in 2D projects. [constant " -"FILTER_LINEAR_MIPMAP] is usually more appropriate." -msgstr "" -"纹理过滤在最近的 4 个像素之间进行混合,并根据表面和相机视图之间的角度选择一个 " -"mipmap。这减少了几乎与相机对齐的表面上的伪影。这是最慢的过滤选项,但会产生最高" -"质量的纹理。可以通过调整 [member ProjectSettings.rendering/textures/" -"default_filters/anisotropic_filtering_level],来更改各向异性过滤级别。\n" -"[b]注意:[/b]这种纹理过滤在 2D 项目中很少有用。[constant " -"FILTER_LINEAR_MIPMAP] 通常更合适。" +"FILTER_LINEAR_MIPMAP] is usually more appropriate in this case." +msgstr "" +"纹理过滤在最近的 4 个像素之间进行混合,并基于表面与相机视图之间的角度在 2 个多" +"级渐远纹理之间进行混合(或者如果 [member ProjectSettings.rendering/textures/" +"default_filters/use_nearest_mipmap_filter] 为 [code]true[/code],则使用最近的" +"多级渐远纹理)。这使得纹理从近处看起来平滑,从远处看起来也平滑。各向异性过滤提" +"高了几乎与相机位于一条线的表面上的纹理质量,但速度稍慢。各向异性过滤级别可以通" +"过调整 [member ProjectSettings.rendering/textures/default_filters/" +"anisotropic_filtering_level] 来改变。\n" +"[b]注意:[/b]该纹理过滤在 2D 项目中很少有用。[constant FILTER_LINEAR_MIPMAP] " +"在这种情况下通常更合适。" msgid "" "Sample the texture using the repeat mode determined by the node this shader " diff --git a/doc/translations/zh_TW.po b/doc/translations/zh_TW.po index 342563ac9c..4f8eb7c8af 100644 --- a/doc/translations/zh_TW.po +++ b/doc/translations/zh_TW.po @@ -16178,61 +16178,6 @@ msgstr "保存環境光遮蔽、粗糙度和金屬度資訊的紋理。" msgid "Represents the size of the [enum TextureParam] enum." msgstr "代表 [enum TextureParam] 列舉的大小。" -msgid "" -"The texture filter reads from the nearest pixel only. The simplest and " -"fastest method of filtering, but the texture will look pixelized." -msgstr "" -"紋理篩檢程式僅讀取最鄰近的圖元。最簡單快速的篩選方法,但紋理看起來會圖元化。" - -msgid "" -"The texture filter blends between the nearest 4 pixels. Use this when you " -"want to avoid a pixelated style, but do not want mipmaps." -msgstr "" -"紋理篩檢程式在最鄰近的 4 個圖元之間混合。如果你想要避免圖元化風格,但又不想使" -"用 mipmap,那麼請使用這個選項。" - -msgid "" -"The texture filter reads from the nearest pixel in the nearest mipmap. The " -"fastest way to read from textures with mipmaps." -msgstr "" -"紋理篩檢程式讀取最鄰近的 mipmap 中的最鄰近的圖元。帶有 mipmap 的紋理的最快讀取" -"方法。" - -msgid "" -"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." -msgstr "" -"紋理篩檢程式在最鄰近的 4 個圖元和最鄰近的 2 個 mipmap 之間混合。請在大多數情況" -"下使用,因為 mipmap 對於平滑遠離相機的圖元很重要。" - -msgid "" -"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]." -msgstr "" -"紋理篩檢程式讀取最鄰近的圖元,但會根據表面和相機視圖之間的夾角選擇 mipmap。可" -"以減少幾乎與相機成一直線的表面的不自然情況。各向異性篩選級別可以通過調整 " -"[member ProjectSettings.rendering/textures/default_filters/" -"anisotropic_filtering_level] 來改變。" - -msgid "" -"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]." -msgstr "" -"紋理篩檢程式在最鄰近的 4 個圖元之間進行混合,並會根據表面和相機視圖之間的夾角" -"選擇 mipmap。可以減少幾乎與相機成一直線的表面的不自然情況。這是篩選選項中最慢" -"的一個,但可以得到最高品質的紋理。各向異性篩選級別可以通過調整 [member " -"ProjectSettings.rendering/textures/default_filters/" -"anisotropic_filtering_level] 來改變。" - msgid "Represents the size of the [enum TextureFilter] enum." msgstr "代表 [enum TextureFilter] 列舉的大小。" @@ -19053,18 +18998,6 @@ msgstr "" "use_physical_light_units] 時可用。" msgid "" -"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.\n" -"Only available when [member ProjectSettings.rendering/lights_and_shadows/" -"use_physical_light_units] is enabled." -msgstr "" -"快門打開和關閉的時間,單位:秒。較高的值將使更多的光線進入,從而使圖像更亮;而" -"較低的值將使更少的光線進入,從而使圖像更暗。\n" -"僅在啟用 [member ProjectSettings.rendering/lights_and_shadows/" -"use_physical_light_units] 時可用。" - -msgid "" "Override value for [member Camera3D.far]. Used internally when calculating " "depth of field. When attached to a [Camera3D] as its [member Camera3D." "attributes], it will override the [member Camera3D.far] property." @@ -20210,69 +20143,6 @@ msgstr "該 [CanvasItem] 的活動 [World2D] 已更改。" msgid "The [CanvasItem] will inherit the filter from its parent." msgstr "該 [CanvasItem] 將從其父級繼承篩檢程式。" -msgid "" -"The texture filter reads from the nearest pixel only. The simplest and " -"fastest method of filtering. Useful for pixel art." -msgstr "紋理篩檢程式僅讀取最鄰近的圖元。最簡單、最快的篩選方法。可用於圖元畫。" - -msgid "" -"The texture filter blends between the nearest four pixels. Use this for most " -"cases where you want to avoid a pixelated style." -msgstr "" -"紋理篩檢程式在最鄰近的四個圖元之間混合。如果想要避免圖元化樣式,大多數情況下請" -"使用此選項。" - -msgid "" -"The texture filter reads from the nearest pixel in the nearest mipmap. This " -"is the fastest way to read from textures with mipmaps." -msgstr "" -"紋理篩檢程式讀取最鄰近的 mipmap 中的最鄰近圖元。這是使用 mipmap 從紋理中讀取的" -"最快方法。" - -msgid "" -"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." -msgstr "" -"紋理篩檢程式在最鄰近的 4 個圖元和最鄰近的 2 個 mipmap 之間混合。請用於可能以低" -"縮放率查看的非圖元畫紋理(例如由 [Camera2D] 縮放造成),因為 mipmap 對於平滑小" -"於螢幕圖元的圖元很重要。" - -msgid "" -"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].\n" -"[b]Note:[/b] This texture filter is rarely useful in 2D projects. [constant " -"TEXTURE_FILTER_NEAREST_WITH_MIPMAPS] is usually more appropriate." -msgstr "" -"紋理篩檢程式讀取最鄰近的圖元,但會根據表面和相機視圖之間的角度選擇 mipmap。可" -"以減少幾乎與相機成一直線的表面的偽影。各向異性篩選級別可以通過調整 [member " -"ProjectSettings.rendering/textures/default_filters/" -"anisotropic_filtering_level] 來改變。\n" -"[b]注意:[/b]這個紋理篩檢程式很少用於 2D 專案。[constant " -"TEXTURE_FILTER_NEAREST_WITH_MIPMAPS] 通常更合適。" - -msgid "" -"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].\n" -"[b]Note:[/b] This texture filter is rarely useful in 2D projects. [constant " -"TEXTURE_FILTER_LINEAR_WITH_MIPMAPS] is usually more appropriate." -msgstr "" -"紋理篩檢程式在最鄰近的 4 個圖元之間進行混合,並會根據表面和相機視圖之間的角度" -"選擇 mipmap。可以減少幾乎與相機成一直線的表面的偽影。這是最慢的篩選選項,但可" -"以得到最高品質的紋理。各向異性篩選級別可以通過調整 [member ProjectSettings." -"rendering/textures/default_filters/anisotropic_filtering_level] 來改變。\n" -"[b]注意:[/b]這個紋理篩檢程式很少用於 2D 專案。[constant " -"TEXTURE_FILTER_NEAREST_WITH_MIPMAPS] 通常更合適。" - msgid "Texture will not repeat." msgstr "紋理不會重複。" @@ -43317,24 +43187,6 @@ msgstr "" "[b]Black (OLED)[/b]主題預設時該項會自動啟用,因為該主題預設使用全黑背景。" msgid "" -"The icon and font color scheme to use in the editor.\n" -"- [b]Auto[/b] determines the color scheme to use automatically based on " -"[member interface/theme/base_color].\n" -"- [b]Dark[/b] makes fonts and icons light (suitable for dark themes).\n" -"- [b]Light[/b] makes fonts and icons dark (suitable for light themes). Icon " -"colors are automatically converted by the editor following [url=https://" -"github.com/godotengine/godot/blob/master/editor/editor_themes.cpp#L135]this " -"set of rules[/url]." -msgstr "" -"在編輯器中使用的圖示和字形的配色方案。\n" -"- [b]Auto[/b] 根據 [member interface/theme/base_color] 自動確定要使用的配色方" -"案。\n" -"- [b]Dark[/b] 使字形和圖示變亮(適合深色主題)。\n" -"- [b]Light[/b] 使字形和圖示變暗(適合淺色主題)。圖示顏色由編輯器按照" -"[url=https://github.com/godotengine/godot/blob/master/editor/editor_themes." -"cpp#L135]這組規則[/url]自動轉換。" - -msgid "" "The saturation to use for editor icons. Higher values result in more vibrant " "colors.\n" "[b]Note:[/b] The default editor icon saturation was increased by 30% in Godot " @@ -105181,30 +105033,6 @@ msgstr "九宮格在需要的地方填充圖塊,並在需要時將它們拉伸 msgid "Uses the default filter mode for this [Viewport]." msgstr "為 [Viewport] 使用預設篩選模式。" -msgid "" -"The texture filter blends between the nearest 4 pixels and between the " -"nearest 2 mipmaps." -msgstr "紋理篩選在最近的 4 個圖元之間和最近的 2 個 mipmap 之間混合。" - -msgid "" -"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." -msgstr "" -"紋理篩選從最近的圖元讀取,但根據表面和相機視圖之間的角度選擇一個 mipmap。這減" -"少了幾乎與相機共線的表面上的偽影。" - -msgid "" -"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." -msgstr "" -"紋理篩選在最近的 4 個圖元之間進行混合,並根據表面和相機視圖之間的角度選擇一個 " -"mipmap。這減少了幾乎與相機共線的表面上的偽影。這是最慢的篩選選項,但會產生最高" -"品質的紋理。" - msgid "Max value for [enum CanvasItemTextureFilter] enum." msgstr "[enum CanvasItemTextureFilter] 列舉的最大值。" @@ -132186,23 +132014,6 @@ msgid "A control used for video playback." msgstr "用於播放影片的控制項。" msgid "" -"A control used for playback of [VideoStream] resources.\n" -"Supported video formats are [url=https://www.theora.org/]Ogg Theora[/url] " -"([code].ogv[/code], [VideoStreamTheora]) and any format exposed via a " -"GDExtension plugin.\n" -"[b]Note:[/b] Due to a bug, VideoStreamPlayer does not support localization " -"remapping yet.\n" -"[b]Warning:[/b] On Web, video playback [i]will[/i] perform poorly due to " -"missing architecture-specific assembly optimizations." -msgstr "" -"用於播放 [VideoStream] 資源的控制項。\n" -"支援的影片格式有 [url=https://www.theora.org/]Ogg Theora[/url]([code].ogv[/" -"code],[VideoStreamTheora])以及任何通過 GDExtension 外掛程式公開的格式。\n" -"[b]注意:[/b]由於一個錯誤,VideoStreamPlayer 還不支援當地語系化重對應。\n" -"[b]警告:[/b]在 Web 上,影片播放[i]會[/i]由於缺少特定於體系結構的組合優化而表" -"現不佳。" - -msgid "" "The length of the current stream, in seconds.\n" "[b]Note:[/b] For [VideoStreamTheora] streams (the built-in format supported " "by Godot), this value will always be zero, as getting the stream length is " @@ -135856,40 +135667,6 @@ msgid "" msgstr "使用由該著色器所附加到的節點決定的篩檢程式對紋理進行取樣。" msgid "" -"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].\n" -"[b]Note:[/b] This texture filter is rarely useful in 2D projects. [constant " -"FILTER_LINEAR_MIPMAP] is usually more appropriate." -msgstr "" -"紋理篩選從最近的圖元讀取,但根據表面和相機視圖之間的角度選擇一個 mipmap。這減" -"少了幾乎與相機對齊的表面上的偽影。可以通過調整 [member ProjectSettings." -"rendering/textures/default_filters/anisotropic_filtering_level],來更改各向異" -"性篩選級別。\n" -"[b]注意:[/b]這種紋理篩選在 2D 專案中很少有用。[constant " -"FILTER_LINEAR_MIPMAP] 通常更合適。" - -msgid "" -"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].\n" -"[b]Note:[/b] This texture filter is rarely useful in 2D projects. [constant " -"FILTER_LINEAR_MIPMAP] is usually more appropriate." -msgstr "" -"紋理篩選在最近的 4 個圖元之間進行混合,並根據表面和相機視圖之間的角度選擇一個 " -"mipmap。這減少了幾乎與相機對齊的表面上的偽影。這是最慢的篩選選項,但會產生最高" -"品質的紋理。可以通過調整 [member ProjectSettings.rendering/textures/" -"default_filters/anisotropic_filtering_level],來更改各向異性篩選級別。\n" -"[b]注意:[/b]這種紋理篩選在 2D 專案中很少有用。[constant " -"FILTER_LINEAR_MIPMAP] 通常更合適。" - -msgid "" "Sample the texture using the repeat mode determined by the node this shader " "is attached to." msgstr "使用由該著色器所附加到的節點決定的重複模式對該紋理進行取樣。" diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index 2db17e96f7..85a7da0c27 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -661,6 +661,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; @@ -1383,7 +1388,7 @@ void RasterizerCanvasGLES3::_render_batch(Light *p_lights, uint32_t p_index) { GLuint vertex_array_gl = 0; GLuint index_array_gl = 0; - uint64_t input_mask = 0; // 2D meshes always use the same vertex format. + uint64_t input_mask = RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_COLOR | RS::ARRAY_FORMAT_TEX_UV; // 2D meshes always use the same vertex format. if (mesh_instance.is_valid()) { mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(mesh_instance, j, input_mask, vertex_array_gl); } else { diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 205f9a552e..6cdcbd9f00 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -130,9 +130,27 @@ void RasterizerSceneGLES3::GeometryInstanceGLES3::_mark_dirty() { } void RasterizerSceneGLES3::GeometryInstanceGLES3::set_use_lightmap(RID p_lightmap_instance, const Rect2 &p_lightmap_uv_scale, int p_lightmap_slice_index) { + lightmap_instance = p_lightmap_instance; + lightmap_uv_scale = p_lightmap_uv_scale; + lightmap_slice_index = p_lightmap_slice_index; + + _mark_dirty(); } void RasterizerSceneGLES3::GeometryInstanceGLES3::set_lightmap_capture(const Color *p_sh9) { + if (p_sh9) { + if (lightmap_sh == nullptr) { + lightmap_sh = memnew(GeometryInstanceLightmapSH); + } + + memcpy(lightmap_sh->sh, p_sh9, sizeof(Color) * 9); + } else { + if (lightmap_sh != nullptr) { + memdelete(lightmap_sh); + lightmap_sh = nullptr; + } + } + _mark_dirty(); } void RasterizerSceneGLES3::_update_dirty_geometry_instances() { @@ -1271,12 +1289,15 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const int32_t shadow_id = GLES3::LightStorage::get_singleton()->light_instance_get_shadow_id(light_instance); if (GLES3::LightStorage::get_singleton()->light_has_shadow(light) && shadow_id >= 0) { - GeometryInstanceGLES3::LightPass pass; - pass.light_id = GLES3::LightStorage::get_singleton()->light_instance_get_gl_id(light_instance); - pass.shadow_id = shadow_id; - pass.light_instance_rid = light_instance; - pass.is_omni = true; - inst->light_passes.push_back(pass); + // Skip static lights when a lightmap is used. + if (!inst->lightmap_instance.is_valid() || GLES3::LightStorage::get_singleton()->light_get_bake_mode(light) != RenderingServer::LIGHT_BAKE_STATIC) { + GeometryInstanceGLES3::LightPass pass; + pass.light_id = GLES3::LightStorage::get_singleton()->light_instance_get_gl_id(light_instance); + pass.shadow_id = shadow_id; + pass.light_instance_rid = light_instance; + pass.is_omni = true; + inst->light_passes.push_back(pass); + } } else { // Lights without shadow can all go in base pass. inst->omni_light_gl_cache.push_back((uint32_t)GLES3::LightStorage::get_singleton()->light_instance_get_gl_id(light_instance)); @@ -1294,11 +1315,14 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const int32_t shadow_id = GLES3::LightStorage::get_singleton()->light_instance_get_shadow_id(light_instance); if (GLES3::LightStorage::get_singleton()->light_has_shadow(light) && shadow_id >= 0) { - GeometryInstanceGLES3::LightPass pass; - pass.light_id = GLES3::LightStorage::get_singleton()->light_instance_get_gl_id(light_instance); - pass.shadow_id = shadow_id; - pass.light_instance_rid = light_instance; - inst->light_passes.push_back(pass); + // Skip static lights when a lightmap is used. + if (!inst->lightmap_instance.is_valid() || GLES3::LightStorage::get_singleton()->light_get_bake_mode(light) != RenderingServer::LIGHT_BAKE_STATIC) { + GeometryInstanceGLES3::LightPass pass; + pass.light_id = GLES3::LightStorage::get_singleton()->light_instance_get_gl_id(light_instance); + pass.shadow_id = shadow_id; + pass.light_instance_rid = light_instance; + inst->light_passes.push_back(pass); + } } else { // Lights without shadow can all go in base pass. inst->spot_light_gl_cache.push_back((uint32_t)GLES3::LightStorage::get_singleton()->light_instance_get_gl_id(light_instance)); @@ -1610,6 +1634,8 @@ void RasterizerSceneGLES3::_setup_lights(const RenderDataGLES3 *p_render_data, b light_data.direction[1] = direction.y; light_data.direction[2] = direction.z; + light_data.bake_mode = light_storage->light_get_bake_mode(base); + float sign = light_storage->light_is_negative(base) ? -1 : 1; light_data.energy = sign * light_storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY); @@ -1758,6 +1784,8 @@ void RasterizerSceneGLES3::_setup_lights(const RenderDataGLES3 *p_render_data, b light_data.position[1] = pos.y; light_data.position[2] = pos.z; + light_data.bake_mode = light_storage->light_get_bake_mode(base); + float radius = MAX(0.001, light_storage->light_get_param(base, RS::LIGHT_PARAM_RANGE)); light_data.inv_radius = 1.0 / radius; @@ -2238,9 +2266,7 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_ bool fb_cleared = false; - Size2i screen_size; - screen_size.x = rb->width; - screen_size.y = rb->height; + Size2i screen_size = rb->internal_size; bool use_wireframe = get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME; @@ -2360,8 +2386,10 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_ } } - glBindFramebuffer(GL_FRAMEBUFFER, rt->fbo); - glViewport(0, 0, rb->width, rb->height); + GLuint fbo = rb->get_render_fbo(); + + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glViewport(0, 0, rb->internal_size.x, rb->internal_size.y); glCullFace(GL_BACK); glEnable(GL_CULL_FACE); @@ -2463,25 +2491,48 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_ } if (scene_state.used_screen_texture || scene_state.used_depth_texture) { - texture_storage->copy_scene_to_backbuffer(rt, scene_state.used_screen_texture, scene_state.used_depth_texture); - glBindFramebuffer(GL_READ_FRAMEBUFFER, rt->fbo); - glReadBuffer(GL_COLOR_ATTACHMENT0); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, rt->backbuffer_fbo); - if (scene_state.used_screen_texture) { - glBlitFramebuffer(0, 0, rt->size.x, rt->size.y, - 0, 0, rt->size.x, rt->size.y, - GL_COLOR_BUFFER_BIT, GL_NEAREST); - glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 5); - glBindTexture(GL_TEXTURE_2D, rt->backbuffer); - } - if (scene_state.used_depth_texture) { - glBlitFramebuffer(0, 0, rt->size.x, rt->size.y, - 0, 0, rt->size.x, rt->size.y, - GL_DEPTH_BUFFER_BIT, GL_NEAREST); - glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 6); - glBindTexture(GL_TEXTURE_2D, rt->backbuffer_depth); - } - glBindFramebuffer(GL_FRAMEBUFFER, rt->fbo); + Size2i size; + GLuint backbuffer_fbo = 0; + GLuint backbuffer = 0; + GLuint backbuffer_depth = 0; + + if (rb->get_scaling_3d_mode() == RS::VIEWPORT_SCALING_3D_MODE_OFF) { + texture_storage->check_backbuffer(rt, scene_state.used_screen_texture, scene_state.used_depth_texture); // note, badly names, this just allocates! + + size = rt->size; + backbuffer_fbo = rt->backbuffer_fbo; + backbuffer = rt->backbuffer; + backbuffer_depth = rt->backbuffer_depth; + } else { + rb->check_backbuffer(scene_state.used_screen_texture, scene_state.used_depth_texture); + size = rb->get_internal_size(); + backbuffer_fbo = rb->get_backbuffer_fbo(); + backbuffer = rb->get_backbuffer(); + backbuffer_depth = rb->get_backbuffer_depth(); + } + + if (backbuffer_fbo != 0) { + glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); + glReadBuffer(GL_COLOR_ATTACHMENT0); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, backbuffer_fbo); + if (scene_state.used_screen_texture) { + glBlitFramebuffer(0, 0, size.x, size.y, + 0, 0, size.x, size.y, + GL_COLOR_BUFFER_BIT, GL_NEAREST); + glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 5); + glBindTexture(GL_TEXTURE_2D, backbuffer); + } + if (scene_state.used_depth_texture) { + glBlitFramebuffer(0, 0, size.x, size.y, + 0, 0, size.x, size.y, + GL_DEPTH_BUFFER_BIT, GL_NEAREST); + glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 6); + glBindTexture(GL_TEXTURE_2D, backbuffer_depth); + } + } + + // Bound framebuffer may have changed, so change it back + glBindFramebuffer(GL_FRAMEBUFFER, fbo); } RENDER_TIMESTAMP("Render 3D Transparent Pass"); @@ -2498,14 +2549,110 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_ } if (rb.is_valid()) { - _render_buffers_debug_draw(rb, p_shadow_atlas); + _render_buffers_debug_draw(rb, p_shadow_atlas, fbo); } glDisable(GL_BLEND); + + _render_post_processing(&render_data); + texture_storage->render_target_disable_clear_request(rb->render_target); glActiveTexture(GL_TEXTURE0); } +void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_render_data) { + GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); + Ref<RenderSceneBuffersGLES3> rb = p_render_data->render_buffers; + ERR_FAIL_COND(rb.is_null()); + + RID render_target = rb->get_render_target(); + Size2i internal_size = rb->get_internal_size(); + Size2i target_size = rb->get_target_size(); + uint32_t view_count = rb->get_view_count(); + + // bool msaa2d_needs_resolve = texture_storage->render_target_get_msaa(render_target) != RS::VIEWPORT_MSAA_DISABLED && !GLES3::Config::get_singleton()->rt_msaa_supported; + bool msaa3d_needs_resolve = rb->get_msaa_needs_resolve(); + GLuint fbo_msaa_3d = rb->get_msaa3d_fbo(); + GLuint fbo_int = rb->get_internal_fbo(); + GLuint fbo_rt = texture_storage->render_target_get_fbo(render_target); // TODO if MSAA 2D is enabled and we're not using rt_msaa, get 2D render target here. + + if (view_count == 1) { + // Resolve if needed. + if (fbo_msaa_3d != 0 && msaa3d_needs_resolve) { + // We can use blit to copy things over + glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_msaa_3d); + + if (fbo_int != 0) { + // We can't combine resolve and scaling, so resolve into our internal buffer + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_int); + } else { + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_rt); + } + glBlitFramebuffer(0, 0, internal_size.x, internal_size.y, 0, 0, internal_size.x, internal_size.y, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, GL_NEAREST); + } + + if (fbo_int != 0) { + // TODO If we have glow or other post processing, we upscale only depth here, post processing will also do scaling. + glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_int); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_rt); + glBlitFramebuffer(0, 0, internal_size.x, internal_size.y, 0, 0, target_size.x, target_size.y, GL_COLOR_BUFFER_BIT, GL_LINEAR); + glBlitFramebuffer(0, 0, internal_size.x, internal_size.y, 0, 0, target_size.x, target_size.y, GL_DEPTH_BUFFER_BIT, GL_NEAREST); + } + + glBindFramebuffer(GL_FRAMEBUFFER, fbo_rt); + } else if ((fbo_msaa_3d != 0 && msaa3d_needs_resolve) || (fbo_int != 0)) { + // TODO investigate if it's smarter to cache these FBOs + GLuint fbos[2]; // read and write + glGenFramebuffers(2, fbos); + + glBindFramebuffer(GL_READ_FRAMEBUFFER, fbos[0]); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbos[1]); + + if (fbo_msaa_3d != 0 && msaa3d_needs_resolve) { + GLuint read_color = rb->get_msaa3d_color(); + GLuint read_depth = rb->get_msaa3d_depth(); + GLuint write_color = 0; + GLuint write_depth = 0; + + if (fbo_int != 0) { + write_color = rb->get_internal_color(); + write_depth = rb->get_internal_depth(); + } else { + write_color = texture_storage->render_target_get_color(render_target); + write_depth = texture_storage->render_target_get_depth(render_target); + } + + for (uint32_t v = 0; v < view_count; v++) { + glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, read_color, 0, v); + glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, read_depth, 0, v); + glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, write_color, 0, v); + glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, write_depth, 0, v); + glBlitFramebuffer(0, 0, internal_size.x, internal_size.y, 0, 0, internal_size.x, internal_size.y, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, GL_NEAREST); + } + } + + if (fbo_int != 0) { + GLuint read_color = rb->get_internal_color(); + GLuint read_depth = rb->get_internal_depth(); + GLuint write_color = texture_storage->render_target_get_color(render_target); + GLuint write_depth = texture_storage->render_target_get_depth(render_target); + + for (uint32_t v = 0; v < view_count; v++) { + glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, read_color, 0, v); + glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, read_depth, 0, v); + glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, write_color, 0, v); + glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, write_depth, 0, v); + + glBlitFramebuffer(0, 0, internal_size.x, internal_size.y, 0, 0, target_size.x, target_size.y, GL_COLOR_BUFFER_BIT, GL_LINEAR); + glBlitFramebuffer(0, 0, internal_size.x, internal_size.y, 0, 0, target_size.x, target_size.y, GL_DEPTH_BUFFER_BIT, GL_NEAREST); + } + } + + glBindFramebuffer(GL_FRAMEBUFFER, fbo_rt); + glDeleteFramebuffers(2, fbos); + } +} + template <PassMode p_pass_mode> void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, const RenderDataGLES3 *p_render_data, uint32_t p_from_element, uint32_t p_to_element, bool p_alpha_pass) { GLES3::MeshStorage *mesh_storage = GLES3::MeshStorage::get_singleton(); @@ -2621,6 +2768,7 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, bool uses_additive_lighting = (inst->light_passes.size() + p_render_data->directional_shadow_count) > 0; uses_additive_lighting = uses_additive_lighting && !shader->unshaded; + // TODOS /* * Still a bug when atlas space is limited. Somehow need to evict light when it doesn't have a spot on the atlas, current check isn't enough @@ -2650,6 +2798,12 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, // Shadow wasn't able to get a spot on the atlas. So skip it. continue; } + } else if (pass > 0) { + uint32_t shadow_id = MAX_DIRECTIONAL_LIGHTS - 1 - (pass - int32_t(inst->light_passes.size())); + if (inst->lightmap_instance.is_valid() && scene_state.directional_lights[shadow_id].bake_mode == RenderingServer::LIGHT_BAKE_STATIC) { + // Skip shadows for static lights on meshes with a lightmap. + continue; + } } } @@ -2738,12 +2892,16 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, GLuint vertex_array_gl = 0; GLuint index_array_gl = 0; + uint64_t vertex_input_mask = shader->vertex_input_mask; + if (inst->lightmap_instance.is_valid()) { + vertex_input_mask |= 1 << RS::ARRAY_TEX_UV2; + } - //skeleton and blend shape + // Skeleton and blend shapes. if (surf->owner->mesh_instance.is_valid()) { - mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(surf->owner->mesh_instance, surf->surface_index, shader->vertex_input_mask, vertex_array_gl); + mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(surf->owner->mesh_instance, surf->surface_index, vertex_input_mask, vertex_array_gl); } else { - mesh_storage->mesh_surface_get_vertex_arrays_and_format(mesh_surface, shader->vertex_input_mask, vertex_array_gl); + mesh_storage->mesh_surface_get_vertex_arrays_and_format(mesh_surface, vertex_input_mask, vertex_array_gl); } index_array_gl = mesh_storage->mesh_surface_get_index_buffer(mesh_surface, surf->lod_index); @@ -2758,6 +2916,15 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, prev_index_array_gl = 0; } + bool use_wireframe = false; + if (p_params->force_wireframe) { + GLuint wireframe_index_array_gl = mesh_storage->mesh_surface_get_index_buffer_wireframe(mesh_surface); + if (wireframe_index_array_gl) { + index_array_gl = wireframe_index_array_gl; + use_wireframe = true; + } + } + bool use_index_buffer = index_array_gl != 0; if (prev_index_array_gl != index_array_gl) { if (index_array_gl != 0) { @@ -2803,12 +2970,28 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, if (p_render_data->directional_light_count == p_render_data->directional_shadow_count) { spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_DIRECTIONAL; } + + if (inst->lightmap_instance.is_valid()) { + spec_constants |= SceneShaderGLES3::USE_LIGHTMAP; + + GLES3::LightmapInstance *li = GLES3::LightStorage::get_singleton()->get_lightmap_instance(inst->lightmap_instance); + GLES3::Lightmap *lm = GLES3::LightStorage::get_singleton()->get_lightmap(li->lightmap); + + if (lm->uses_spherical_harmonics) { + spec_constants |= SceneShaderGLES3::USE_SH_LIGHTMAP; + } + } else if (inst->lightmap_sh) { + spec_constants |= SceneShaderGLES3::USE_LIGHTMAP_CAPTURE; + } else { + spec_constants |= SceneShaderGLES3::DISABLE_LIGHTMAP; + } } else { // Only base pass uses the radiance map. spec_constants &= ~SceneShaderGLES3::USE_RADIANCE_MAP; spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_OMNI; spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_SPOT; spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_DIRECTIONAL; + spec_constants |= SceneShaderGLES3::DISABLE_LIGHTMAP; } if (uses_additive_lighting) { @@ -2831,6 +3014,10 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, // Render directional lights. uint32_t shadow_id = MAX_DIRECTIONAL_LIGHTS - 1 - (pass - int32_t(inst->light_passes.size())); + if (pass == 0 && inst->lightmap_instance.is_valid() && scene_state.directional_lights[shadow_id].bake_mode == RenderingServer::LIGHT_BAKE_STATIC) { + // Disable additive lighting with a static light and a lightmap. + spec_constants &= ~SceneShaderGLES3::USE_ADDITIVE_LIGHTING; + } if (scene_state.directional_shadows[shadow_id].shadow_split_offsets[0] == scene_state.directional_shadows[shadow_id].shadow_split_offsets[1]) { // Orthogonal, do nothing. } else if (scene_state.directional_shadows[shadow_id].shadow_split_offsets[1] == scene_state.directional_shadows[shadow_id].shadow_split_offsets[2]) { @@ -2920,6 +3107,46 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, glUniform1uiv(material_storage->shaders.scene_shader.version_get_uniform(SceneShaderGLES3::SPOT_LIGHT_INDICES, shader->version, instance_variant, spec_constants), inst->spot_light_gl_cache.size(), inst->spot_light_gl_cache.ptr()); } + if (inst->lightmap_instance.is_valid()) { + GLES3::LightmapInstance *li = GLES3::LightStorage::get_singleton()->get_lightmap_instance(inst->lightmap_instance); + GLES3::Lightmap *lm = GLES3::LightStorage::get_singleton()->get_lightmap(li->lightmap); + + GLuint tex = GLES3::TextureStorage::get_singleton()->texture_get_texid(lm->light_texture); + glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 4); + glBindTexture(GL_TEXTURE_2D_ARRAY, tex); + + material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::LIGHTMAP_SLICE, inst->lightmap_slice_index, shader->version, instance_variant, spec_constants); + + Vector4 uv_scale(inst->lightmap_uv_scale.position.x, inst->lightmap_uv_scale.position.y, inst->lightmap_uv_scale.size.x, inst->lightmap_uv_scale.size.y); + material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::LIGHTMAP_UV_SCALE, uv_scale, shader->version, instance_variant, spec_constants); + + float exposure_normalization = 1.0; + if (p_render_data->camera_attributes.is_valid()) { + float enf = RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + exposure_normalization = enf / lm->baked_exposure; + } + material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::LIGHTMAP_EXPOSURE_NORMALIZATION, exposure_normalization, shader->version, instance_variant, spec_constants); + + if (lm->uses_spherical_harmonics) { + Basis to_lm = li->transform.basis.inverse() * p_render_data->cam_transform.basis; + to_lm = to_lm.inverse().transposed(); + GLfloat matrix[9] = { + (GLfloat)to_lm.rows[0][0], + (GLfloat)to_lm.rows[1][0], + (GLfloat)to_lm.rows[2][0], + (GLfloat)to_lm.rows[0][1], + (GLfloat)to_lm.rows[1][1], + (GLfloat)to_lm.rows[2][1], + (GLfloat)to_lm.rows[0][2], + (GLfloat)to_lm.rows[1][2], + (GLfloat)to_lm.rows[2][2], + }; + glUniformMatrix3fv(material_storage->shaders.scene_shader.version_get_uniform(SceneShaderGLES3::LIGHTMAP_NORMAL_XFORM, shader->version, instance_variant, spec_constants), 1, GL_FALSE, matrix); + } + } else if (inst->lightmap_sh) { + glUniform4fv(material_storage->shaders.scene_shader.version_get_uniform(SceneShaderGLES3::LIGHTMAP_CAPTURES, shader->version, instance_variant, spec_constants), 9, reinterpret_cast<const GLfloat *>(inst->lightmap_sh->sh)); + } + prev_inst = inst; } } @@ -2938,6 +3165,8 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, } } + material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::MODEL_FLAGS, inst->flags_cache, shader->version, instance_variant, spec_constants); + // Can be index count or vertex count uint32_t count = 0; if (surf->lod_index > 0) { @@ -2946,6 +3175,11 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, count = mesh_storage->mesh_surface_get_vertices_drawn_count(mesh_surface); } + if (use_wireframe) { + // In this case we are using index count, and we need double the indices for the wireframe mesh. + count = count * 2; + } + if constexpr (p_pass_mode != PASS_MODE_DEPTH) { // Don't count draw calls during depth pre-pass to match the RD renderers. if (p_render_data->render_info) { @@ -3000,17 +3234,25 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, glVertexAttribI4ui(15, default_color, default_color, default_custom, default_custom); } - if (use_index_buffer) { - glDrawElementsInstanced(primitive_gl, count, mesh_storage->mesh_surface_get_index_type(mesh_surface), 0, inst->instance_count); + if (use_wireframe) { + glDrawElementsInstanced(GL_LINES, count, GL_UNSIGNED_INT, 0, inst->instance_count); } else { - glDrawArraysInstanced(primitive_gl, 0, count, inst->instance_count); + if (use_index_buffer) { + glDrawElementsInstanced(primitive_gl, count, mesh_storage->mesh_surface_get_index_type(mesh_surface), 0, inst->instance_count); + } else { + glDrawArraysInstanced(primitive_gl, 0, count, inst->instance_count); + } } } else { // Using regular Mesh. - if (use_index_buffer) { - glDrawElements(primitive_gl, count, mesh_storage->mesh_surface_get_index_type(mesh_surface), 0); + if (use_wireframe) { + glDrawElements(GL_LINES, count, GL_UNSIGNED_INT, 0); } else { - glDrawArrays(primitive_gl, 0, count); + if (use_index_buffer) { + glDrawElements(primitive_gl, count, mesh_storage->mesh_surface_get_index_type(mesh_surface), 0); + } else { + glDrawArrays(primitive_gl, 0, count); + } } } @@ -3123,7 +3365,7 @@ Ref<RenderSceneBuffers> RasterizerSceneGLES3::render_buffers_create() { return rb; } -void RasterizerSceneGLES3::_render_buffers_debug_draw(Ref<RenderSceneBuffersGLES3> p_render_buffers, RID p_shadow_atlas) { +void RasterizerSceneGLES3::_render_buffers_debug_draw(Ref<RenderSceneBuffersGLES3> p_render_buffers, RID p_shadow_atlas, GLuint p_fbo) { GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); GLES3::LightStorage *light_storage = GLES3::LightStorage::get_singleton(); GLES3::CopyEffects *copy_effects = GLES3::CopyEffects::get_singleton(); @@ -3200,8 +3442,11 @@ void RasterizerSceneGLES3::_render_buffers_debug_draw(Ref<RenderSceneBuffersGLES } } } - glBindFramebuffer(GL_FRAMEBUFFER, rt->fbo); - glViewport(0, 0, rt->size.width, rt->size.height); + + // Set back to FBO + glBindFramebuffer(GL_FRAMEBUFFER, p_fbo); + Size2i size = p_render_buffers->get_internal_size(); + glViewport(0, 0, size.width, size.height); glBindTexture(GL_TEXTURE_2D, shadow_atlas_texture); copy_effects->copy_to_rect(Rect2(Vector2(), Vector2(0.5, 0.5))); diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h index 7d3c8896da..a860793c21 100644 --- a/drivers/gles3/rasterizer_scene_gles3.h +++ b/drivers/gles3/rasterizer_scene_gles3.h @@ -170,6 +170,9 @@ private: float cos_spot_angle; float specular_amount; float shadow_opacity; + + float pad[3]; + uint32_t bake_mode; }; static_assert(sizeof(LightData) % 16 == 0, "LightData size must be a multiple of 16 bytes"); @@ -181,7 +184,7 @@ private: float size; uint32_t enabled; // For use by SkyShaders - float pad; + uint32_t bake_mode; float shadow_opacity; float specular; }; @@ -269,6 +272,10 @@ private: GeometryInstanceGLES3 *owner = nullptr; }; + struct GeometryInstanceLightmapSH { + Color sh[9]; + }; + class GeometryInstanceGLES3 : public RenderGeometryInstanceBase { public: //used during rendering @@ -296,6 +303,11 @@ private: LocalVector<uint32_t> omni_light_gl_cache; LocalVector<uint32_t> spot_light_gl_cache; + RID lightmap_instance; + Rect2 lightmap_uv_scale; + uint32_t lightmap_slice_index; + GeometryInstanceLightmapSH *lightmap_sh = nullptr; + // Used during setup. GeometryInstanceSurface *surface_caches = nullptr; SelfList<GeometryInstanceGLES3> dirty_list_element; @@ -518,6 +530,7 @@ private: void _fill_render_list(RenderListType p_render_list, const RenderDataGLES3 *p_render_data, PassMode p_pass_mode, bool p_append = false); void _render_shadows(const RenderDataGLES3 *p_render_data, const Size2i &p_viewport_size = Size2i(1, 1)); void _render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray<RenderGeometryInstance *> &p_instances, const Plane &p_camera_plane = Plane(), float p_lod_distance_multiplier = 0, float p_screen_mesh_lod_threshold = 0.0, RenderingMethod::RenderInfo *p_render_info = nullptr, const Size2i &p_viewport_size = Size2i(1, 1)); + void _render_post_processing(const RenderDataGLES3 *p_render_data); template <PassMode p_pass_mode> _FORCE_INLINE_ void _render_list_template(RenderListParameters *p_params, const RenderDataGLES3 *p_render_data, uint32_t p_from_element, uint32_t p_to_element, bool p_alpha_pass = false); @@ -530,7 +543,7 @@ protected: float screen_space_roughness_limiter_amount = 0.25; float screen_space_roughness_limiter_limit = 0.18; - void _render_buffers_debug_draw(Ref<RenderSceneBuffersGLES3> p_render_buffers, RID p_shadow_atlas); + void _render_buffers_debug_draw(Ref<RenderSceneBuffersGLES3> p_render_buffers, RID p_shadow_atlas, GLuint p_fbo); /* Camera Attributes */ diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl index e0f8e83373..1d9ba623c4 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -1,7 +1,7 @@ /* clang-format off */ #[modes] -mode_color = +mode_color = mode_color_instancing = \n#define USE_INSTANCING mode_depth = #define MODE_RENDER_DEPTH mode_depth_instancing = #define MODE_RENDER_DEPTH \n#define USE_INSTANCING @@ -14,6 +14,9 @@ DISABLE_LIGHT_OMNI = false DISABLE_LIGHT_SPOT = false DISABLE_FOG = false USE_RADIANCE_MAP = true +USE_LIGHTMAP = false +USE_SH_LIGHTMAP = false +USE_LIGHTMAP_CAPTURE = false USE_MULTIVIEW = false RENDER_SHADOWS = false RENDER_SHADOWS_LINEAR = false @@ -139,6 +142,8 @@ layout(location = 14) in highp vec4 instance_xform2; layout(location = 15) in highp uvec4 instance_color_custom_data; // Color packed into xy, Custom data into zw. #endif +#define FLAGS_NON_UNIFORM_SCALE (1 << 4) + layout(std140) uniform GlobalShaderUniformData { //ubo:1 vec4 global_shader_uniforms[MAX_GLOBAL_SHADER_UNIFORMS]; }; @@ -242,6 +247,8 @@ uniform highp vec3 compressed_aabb_position; uniform highp vec3 compressed_aabb_size; uniform highp vec4 uv_scale; +uniform highp uint model_flags; + /* Varyings */ out highp vec3 vertex_interp; @@ -310,7 +317,14 @@ void main() { #ifdef NORMAL_USED vec3 normal = oct_to_vec3(axis_tangent_attrib.xy * 2.0 - 1.0); #endif - highp mat3 model_normal_matrix = mat3(model_matrix); + + highp mat3 model_normal_matrix; + + if (bool(model_flags & uint(FLAGS_NON_UNIFORM_SCALE))) { + model_normal_matrix = transpose(inverse(mat3(model_matrix))); + } else { + model_normal_matrix = mat3(model_matrix); + } #if defined(NORMAL_USED) || defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED) @@ -665,6 +679,10 @@ multiview_data; /* clang-format on */ +#define LIGHT_BAKE_DISABLED 0u +#define LIGHT_BAKE_STATIC 1u +#define LIGHT_BAKE_DYNAMIC 2u + #ifndef MODE_RENDER_DEPTH // Directional light data. #if !defined(DISABLE_LIGHT_DIRECTIONAL) || (!defined(ADDITIVE_OMNI) && !defined(ADDITIVE_SPOT)) @@ -674,7 +692,8 @@ struct DirectionalLightData { mediump float energy; mediump vec3 color; mediump float size; - mediump vec2 pad; + lowp uint unused; + lowp uint bake_mode; mediump float shadow_opacity; mediump float specular; }; @@ -707,6 +726,9 @@ struct LightData { // This structure needs to be as packed as possible. mediump float cone_angle; mediump float specular_amount; mediump float shadow_opacity; + + lowp vec3 pad; + lowp uint bake_mode; }; #if !defined(DISABLE_LIGHT_OMNI) || defined(ADDITIVE_OMNI) @@ -823,6 +845,23 @@ float sample_shadow(highp sampler2DShadow shadow, float shadow_pixel_size, vec4 #endif // !MODE_RENDER_DEPTH +#ifndef DISABLE_LIGHTMAP +#ifdef USE_LIGHTMAP +uniform mediump sampler2DArray lightmap_textures; //texunit:-4 +uniform lowp uint lightmap_slice; +uniform highp vec4 lightmap_uv_scale; +uniform float lightmap_exposure_normalization; + +#ifdef USE_SH_LIGHTMAP +uniform mediump mat3 lightmap_normal_xform; +#endif // USE_SH_LIGHTMAP +#endif // USE_LIGHTMAP + +#ifdef USE_LIGHTMAP_CAPTURE +uniform mediump vec4[9] lightmap_captures; +#endif // USE_LIGHTMAP_CAPTURE +#endif // !DISABLE_LIGHTMAP + #ifdef USE_MULTIVIEW uniform highp sampler2DArray depth_buffer; // texunit:-6 uniform highp sampler2DArray color_buffer; // texunit:-5 @@ -1395,7 +1434,6 @@ void main() { #endif // Calculate Reflection probes - // Calculate Lightmaps #if defined(CUSTOM_RADIANCE_USED) specular_light = mix(specular_light, custom_radiance.rgb, custom_radiance.a); @@ -1420,6 +1458,61 @@ void main() { ambient_light = mix(ambient_light, custom_irradiance.rgb, custom_irradiance.a); #endif // CUSTOM_IRRADIANCE_USED +#ifndef DISABLE_LIGHTMAP +#ifdef USE_LIGHTMAP_CAPTURE + { + vec3 wnormal = mat3(scene_data.inv_view_matrix) * normal; + const float c1 = 0.429043; + const float c2 = 0.511664; + const float c3 = 0.743125; + const float c4 = 0.886227; + const float c5 = 0.247708; + ambient_light += (c1 * lightmap_captures[8].rgb * (wnormal.x * wnormal.x - wnormal.y * wnormal.y) + + c3 * lightmap_captures[6].rgb * wnormal.z * wnormal.z + + c4 * lightmap_captures[0].rgb - + c5 * lightmap_captures[6].rgb + + 2.0 * c1 * lightmap_captures[4].rgb * wnormal.x * wnormal.y + + 2.0 * c1 * lightmap_captures[7].rgb * wnormal.x * wnormal.z + + 2.0 * c1 * lightmap_captures[5].rgb * wnormal.y * wnormal.z + + 2.0 * c2 * lightmap_captures[3].rgb * wnormal.x + + 2.0 * c2 * lightmap_captures[1].rgb * wnormal.y + + 2.0 * c2 * lightmap_captures[2].rgb * wnormal.z) * + scene_data.emissive_exposure_normalization; + } +#else +#ifdef USE_LIGHTMAP + { + vec3 uvw; + uvw.xy = uv2 * lightmap_uv_scale.zw + lightmap_uv_scale.xy; + uvw.z = float(lightmap_slice); + +#ifdef USE_SH_LIGHTMAP + uvw.z *= 4.0; // SH textures use 4 times more data. + vec3 lm_light_l0 = textureLod(lightmap_textures, uvw + vec3(0.0, 0.0, 0.0), 0.0).rgb; + vec3 lm_light_l1n1 = textureLod(lightmap_textures, uvw + vec3(0.0, 0.0, 1.0), 0.0).rgb; + vec3 lm_light_l1_0 = textureLod(lightmap_textures, uvw + vec3(0.0, 0.0, 2.0), 0.0).rgb; + vec3 lm_light_l1p1 = textureLod(lightmap_textures, uvw + vec3(0.0, 0.0, 3.0), 0.0).rgb; + + vec3 n = normalize(lightmap_normal_xform * normal); + + ambient_light += lm_light_l0 * 0.282095f; + ambient_light += lm_light_l1n1 * 0.32573 * n.y * lightmap_exposure_normalization; + ambient_light += lm_light_l1_0 * 0.32573 * n.z * lightmap_exposure_normalization; + ambient_light += lm_light_l1p1 * 0.32573 * n.x * lightmap_exposure_normalization; + if (metallic > 0.01) { // Since the more direct bounced light is lost, we can kind of fake it with this trick. + vec3 r = reflect(normalize(-vertex), normal); + specular_light += lm_light_l1n1 * 0.32573 * r.y * lightmap_exposure_normalization; + specular_light += lm_light_l1_0 * 0.32573 * r.z * lightmap_exposure_normalization; + specular_light += lm_light_l1p1 * 0.32573 * r.x * lightmap_exposure_normalization; + } +#else + ambient_light += textureLod(lightmap_textures, uvw, 0.0).rgb * lightmap_exposure_normalization; +#endif + } +#endif // USE_LIGHTMAP +#endif // USE_LIGHTMAP_CAPTURE +#endif // !DISABLE_LIGHTMAP + { #if defined(AMBIENT_LIGHT_DISABLED) ambient_light = vec3(0.0, 0.0, 0.0); @@ -1455,6 +1548,11 @@ void main() { #ifndef DISABLE_LIGHT_DIRECTIONAL for (uint i = uint(0); i < scene_data.directional_light_count; i++) { +#if defined(USE_LIGHTMAP) && !defined(DISABLE_LIGHTMAP) + if (directional_lights[i].bake_mode == LIGHT_BAKE_STATIC) { + continue; + } +#endif light_compute(normal, normalize(directional_lights[i].direction), normalize(view), directional_lights[i].size, directional_lights[i].color * directional_lights[i].energy, true, 1.0, f0, roughness, metallic, 1.0, albedo, alpha, #ifdef LIGHT_BACKLIGHT_USED backlight, @@ -1479,6 +1577,11 @@ void main() { if (i >= omni_light_count) { break; } +#if defined(USE_LIGHTMAP) && !defined(DISABLE_LIGHTMAP) + if (omni_lights[omni_light_indices[i]].bake_mode == LIGHT_BAKE_STATIC) { + continue; + } +#endif light_process_omni(omni_light_indices[i], vertex, view, normal, f0, roughness, metallic, 1.0, albedo, alpha, #ifdef LIGHT_BACKLIGHT_USED backlight, @@ -1502,6 +1605,11 @@ void main() { if (i >= spot_light_count) { break; } +#if defined(USE_LIGHTMAP) && !defined(DISABLE_LIGHTMAP) + if (spot_lights[spot_light_indices[i]].bake_mode == LIGHT_BAKE_STATIC) { + continue; + } +#endif light_process_spot(spot_light_indices[i], vertex, view, normal, f0, roughness, metallic, 1.0, albedo, alpha, #ifdef LIGHT_BACKLIGHT_USED backlight, @@ -1600,6 +1708,8 @@ void main() { #if !defined(ADDITIVE_OMNI) && !defined(ADDITIVE_SPOT) +#ifndef SHADOWS_DISABLED + // Orthogonal shadows #if !defined(LIGHT_USE_PSSM2) && !defined(LIGHT_USE_PSSM4) float directional_shadow = sample_shadow(directional_shadow_atlas, directional_shadows[directional_shadow_index].shadow_atlas_pixel_size, shadow_coord); @@ -1706,6 +1816,9 @@ void main() { directional_shadow = mix(directional_shadow, 1.0, smoothstep(directional_shadows[directional_shadow_index].fade_from, directional_shadows[directional_shadow_index].fade_to, vertex.z)); directional_shadow = mix(1.0, directional_shadow, directional_lights[directional_shadow_index].shadow_opacity); +#else + float directional_shadow = 1.0f; +#endif // SHADOWS_DISABLED light_compute(normal, normalize(directional_lights[directional_shadow_index].direction), normalize(view), directional_lights[directional_shadow_index].size, directional_lights[directional_shadow_index].color * directional_lights[directional_shadow_index].energy, true, directional_shadow, f0, roughness, metallic, 1.0, albedo, alpha, #ifdef LIGHT_BACKLIGHT_USED backlight, @@ -1725,11 +1838,12 @@ void main() { #endif // !defined(ADDITIVE_OMNI) && !defined(ADDITIVE_SPOT) #ifdef ADDITIVE_OMNI + float omni_shadow = 1.0f; +#ifndef SHADOWS_DISABLED vec3 light_ray = ((positional_shadows[positional_shadow_index].shadow_matrix * vec4(shadow_coord.xyz, 1.0))).xyz; - - float omni_shadow = texture(omni_shadow_texture, vec4(light_ray, length(light_ray) * omni_lights[omni_light_index].inv_radius)); + omni_shadow = texture(omni_shadow_texture, vec4(light_ray, length(light_ray) * omni_lights[omni_light_index].inv_radius)); omni_shadow = mix(1.0, omni_shadow, omni_lights[omni_light_index].shadow_opacity); - +#endif // SHADOWS_DISABLED light_process_omni(omni_light_index, vertex, view, normal, f0, roughness, metallic, omni_shadow, albedo, alpha, #ifdef LIGHT_BACKLIGHT_USED backlight, @@ -1748,9 +1862,11 @@ void main() { #endif // ADDITIVE_OMNI #ifdef ADDITIVE_SPOT - float spot_shadow = sample_shadow(spot_shadow_texture, positional_shadows[positional_shadow_index].shadow_atlas_pixel_size, shadow_coord); + float spot_shadow = 1.0f; +#ifndef SHADOWS_DISABLED + spot_shadow = sample_shadow(spot_shadow_texture, positional_shadows[positional_shadow_index].shadow_atlas_pixel_size, shadow_coord); spot_shadow = mix(1.0, spot_shadow, spot_lights[spot_light_index].shadow_opacity); - +#endif // SHADOWS_DISABLED light_process_spot(spot_light_index, vertex, view, normal, f0, roughness, metallic, spot_shadow, albedo, alpha, #ifdef LIGHT_BACKLIGHT_USED backlight, diff --git a/drivers/gles3/storage/config.cpp b/drivers/gles3/storage/config.cpp index 4bf6165fe9..1200bf9626 100644 --- a/drivers/gles3/storage/config.cpp +++ b/drivers/gles3/storage/config.cpp @@ -92,14 +92,49 @@ Config::Config() { anisotropic_level = MIN(float(1 << int(GLOBAL_GET("rendering/textures/default_filters/anisotropic_filtering_level"))), anisotropic_level); } + glGetIntegerv(GL_MAX_SAMPLES, &msaa_max_samples); +#ifdef WEB_ENABLED + msaa_supported = (msaa_max_samples > 0); +#else + msaa_supported = extensions.has("GL_EXT_framebuffer_multisample"); +#endif +#ifndef IOS_ENABLED + msaa_multiview_supported = extensions.has("GL_EXT_multiview_texture_multisample"); multiview_supported = extensions.has("GL_OVR_multiview2") || extensions.has("GL_OVR_multiview"); +#endif + #ifdef ANDROID_ENABLED + // These are GLES only + rt_msaa_supported = extensions.has("GL_EXT_multisampled_render_to_texture"); + rt_msaa_multiview_supported = extensions.has("GL_OVR_multiview_multisampled_render_to_texture"); + if (multiview_supported) { eglFramebufferTextureMultiviewOVR = (PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)eglGetProcAddress("glFramebufferTextureMultiviewOVR"); if (eglFramebufferTextureMultiviewOVR == nullptr) { multiview_supported = false; } } + + if (msaa_multiview_supported) { + eglTexStorage3DMultisample = (PFNGLTEXSTORAGE3DMULTISAMPLEPROC)eglGetProcAddress("glTexStorage3DMultisample"); + if (eglTexStorage3DMultisample == nullptr) { + msaa_multiview_supported = false; + } + } + + if (rt_msaa_supported) { + eglFramebufferTexture2DMultisampleEXT = (PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC)eglGetProcAddress("glFramebufferTexture2DMultisampleEXT"); + if (eglFramebufferTexture2DMultisampleEXT == nullptr) { + rt_msaa_supported = false; + } + } + + if (rt_msaa_multiview_supported) { + eglFramebufferTextureMultisampleMultiviewOVR = (PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC)eglGetProcAddress("glFramebufferTextureMultisampleMultiviewOVR"); + if (eglFramebufferTextureMultisampleMultiviewOVR == nullptr) { + rt_msaa_multiview_supported = false; + } + } #endif force_vertex_shading = false; //GLOBAL_GET("rendering/quality/shading/force_vertex_shading"); diff --git a/drivers/gles3/storage/config.h b/drivers/gles3/storage/config.h index b2dd98f025..1c0a5178bd 100644 --- a/drivers/gles3/storage/config.h +++ b/drivers/gles3/storage/config.h @@ -42,6 +42,9 @@ #ifdef ANDROID_ENABLED typedef void (*PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)(GLenum, GLenum, GLuint, GLint, GLint, GLsizei); +typedef void (*PFNGLTEXSTORAGE3DMULTISAMPLEPROC)(GLenum, GLsizei, GLenum, GLsizei, GLsizei, GLsizei, GLboolean); +typedef void (*PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC)(GLenum, GLenum, GLenum, GLuint, GLint, GLsizei); +typedef void (*PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC)(GLenum, GLenum, GLuint, GLint, GLsizei, GLint, GLsizei); #endif namespace GLES3 { @@ -63,8 +66,7 @@ public: int64_t max_renderable_lights = 0; int64_t max_lights_per_object = 0; - // TODO implement wireframe in OpenGL - // bool generate_wireframes; + bool generate_wireframes = false; HashSet<String> extensions; @@ -82,9 +84,18 @@ public: bool support_anisotropic_filter = false; float anisotropic_level = 0.0f; + GLint msaa_max_samples = 0; + bool msaa_supported = false; + bool msaa_multiview_supported = false; + bool rt_msaa_supported = false; + bool rt_msaa_multiview_supported = false; bool multiview_supported = false; + #ifdef ANDROID_ENABLED PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC eglFramebufferTextureMultiviewOVR = nullptr; + PFNGLTEXSTORAGE3DMULTISAMPLEPROC eglTexStorage3DMultisample = nullptr; + PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC eglFramebufferTexture2DMultisampleEXT = nullptr; + PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC eglFramebufferTextureMultisampleMultiviewOVR = nullptr; #endif static Config *get_singleton() { return singleton; }; diff --git a/drivers/gles3/storage/light_storage.cpp b/drivers/gles3/storage/light_storage.cpp index 6d4d23bd10..af8d629e5a 100644 --- a/drivers/gles3/storage/light_storage.cpp +++ b/drivers/gles3/storage/light_storage.cpp @@ -569,69 +569,171 @@ void LightStorage::lightmap_initialize(RID p_rid) { void LightStorage::lightmap_free(RID p_rid) { Lightmap *lightmap = lightmap_owner.get_or_null(p_rid); + ERR_FAIL_NULL(lightmap); lightmap->dependency.deleted_notify(p_rid); lightmap_owner.free(p_rid); } void LightStorage::lightmap_set_textures(RID p_lightmap, RID p_light, bool p_uses_spherical_haromics) { + Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL(lightmap); + lightmap->light_texture = p_light; + lightmap->uses_spherical_harmonics = p_uses_spherical_haromics; + + GLuint tex = GLES3::TextureStorage::get_singleton()->texture_get_texid(lightmap->light_texture); + glBindTexture(GL_TEXTURE_2D_ARRAY, tex); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glBindTexture(GL_TEXTURE_2D_ARRAY, 0); } void LightStorage::lightmap_set_probe_bounds(RID p_lightmap, const AABB &p_bounds) { + Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL(lightmap); + lightmap->bounds = p_bounds; } void LightStorage::lightmap_set_probe_interior(RID p_lightmap, bool p_interior) { + Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL(lightmap); + lightmap->interior = p_interior; } void LightStorage::lightmap_set_probe_capture_data(RID p_lightmap, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree) { + Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL(lightmap); + + if (p_points.size()) { + ERR_FAIL_COND(p_points.size() * 9 != p_point_sh.size()); + ERR_FAIL_COND((p_tetrahedra.size() % 4) != 0); + ERR_FAIL_COND((p_bsp_tree.size() % 6) != 0); + } + + lightmap->points = p_points; + lightmap->point_sh = p_point_sh; + lightmap->tetrahedra = p_tetrahedra; + lightmap->bsp_tree = p_bsp_tree; } void LightStorage::lightmap_set_baked_exposure_normalization(RID p_lightmap, float p_exposure) { + Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL(lightmap); + + lightmap->baked_exposure = p_exposure; } PackedVector3Array LightStorage::lightmap_get_probe_capture_points(RID p_lightmap) const { - return PackedVector3Array(); + Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL_V(lightmap, PackedVector3Array()); + return lightmap->points; } PackedColorArray LightStorage::lightmap_get_probe_capture_sh(RID p_lightmap) const { - return PackedColorArray(); + Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL_V(lightmap, PackedColorArray()); + return lightmap->point_sh; } PackedInt32Array LightStorage::lightmap_get_probe_capture_tetrahedra(RID p_lightmap) const { - return PackedInt32Array(); + Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL_V(lightmap, PackedInt32Array()); + return lightmap->tetrahedra; } PackedInt32Array LightStorage::lightmap_get_probe_capture_bsp_tree(RID p_lightmap) const { - return PackedInt32Array(); + Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL_V(lightmap, PackedInt32Array()); + return lightmap->bsp_tree; } AABB LightStorage::lightmap_get_aabb(RID p_lightmap) const { - return AABB(); + Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL_V(lightmap, AABB()); + return lightmap->bounds; } void LightStorage::lightmap_tap_sh_light(RID p_lightmap, const Vector3 &p_point, Color *r_sh) { + Lightmap *lm = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL(lm); + + for (int i = 0; i < 9; i++) { + r_sh[i] = Color(0, 0, 0, 0); + } + + if (!lm->points.size() || !lm->bsp_tree.size() || !lm->tetrahedra.size()) { + return; + } + + static_assert(sizeof(Lightmap::BSP) == 24); + + const Lightmap::BSP *bsp = (const Lightmap::BSP *)lm->bsp_tree.ptr(); + int32_t node = 0; + while (node >= 0) { + if (Plane(bsp[node].plane[0], bsp[node].plane[1], bsp[node].plane[2], bsp[node].plane[3]).is_point_over(p_point)) { +#ifdef DEBUG_ENABLED + ERR_FAIL_COND(bsp[node].over >= 0 && bsp[node].over < node); +#endif + + node = bsp[node].over; + } else { +#ifdef DEBUG_ENABLED + ERR_FAIL_COND(bsp[node].under >= 0 && bsp[node].under < node); +#endif + node = bsp[node].under; + } + } + + if (node == Lightmap::BSP::EMPTY_LEAF) { + return; // Nothing could be done. + } + + node = ABS(node) - 1; + + uint32_t *tetrahedron = (uint32_t *)&lm->tetrahedra[node * 4]; + Vector3 points[4] = { lm->points[tetrahedron[0]], lm->points[tetrahedron[1]], lm->points[tetrahedron[2]], lm->points[tetrahedron[3]] }; + const Color *sh_colors[4]{ &lm->point_sh[tetrahedron[0] * 9], &lm->point_sh[tetrahedron[1] * 9], &lm->point_sh[tetrahedron[2] * 9], &lm->point_sh[tetrahedron[3] * 9] }; + Color barycentric = Geometry3D::tetrahedron_get_barycentric_coords(points[0], points[1], points[2], points[3], p_point); + + for (int i = 0; i < 4; i++) { + float c = CLAMP(barycentric[i], 0.0, 1.0); + for (int j = 0; j < 9; j++) { + r_sh[j] += sh_colors[i][j] * c; + } + } } bool LightStorage::lightmap_is_interior(RID p_lightmap) const { - return false; + Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL_V(lightmap, false); + return lightmap->interior; } void LightStorage::lightmap_set_probe_capture_update_speed(float p_speed) { + lightmap_probe_capture_update_speed = p_speed; } float LightStorage::lightmap_get_probe_capture_update_speed() const { - return 0; + return lightmap_probe_capture_update_speed; } /* LIGHTMAP INSTANCE */ RID LightStorage::lightmap_instance_create(RID p_lightmap) { - return RID(); + LightmapInstance li; + li.lightmap = p_lightmap; + return lightmap_instance_owner.make_rid(li); } void LightStorage::lightmap_instance_free(RID p_lightmap) { + lightmap_instance_owner.free(p_lightmap); } void LightStorage::lightmap_instance_set_transform(RID p_lightmap, const Transform3D &p_transform) { + LightmapInstance *li = lightmap_instance_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL(li); + li->transform = p_transform; } /* SHADOW ATLAS API */ diff --git a/drivers/gles3/storage/light_storage.h b/drivers/gles3/storage/light_storage.h index 2fb4dcaeca..7ab0286098 100644 --- a/drivers/gles3/storage/light_storage.h +++ b/drivers/gles3/storage/light_storage.h @@ -155,6 +155,11 @@ struct Lightmap { Dependency dependency; }; +struct LightmapInstance { + RID lightmap; + Transform3D transform; +}; + class LightStorage : public RendererLightStorage { public: enum ShadowAtlastQuadrant { @@ -179,9 +184,14 @@ private: /* LIGHTMAP */ Vector<RID> lightmap_textures; + float lightmap_probe_capture_update_speed = 4; mutable RID_Owner<Lightmap, true> lightmap_owner; + /* LIGHTMAP INSTANCE */ + + mutable RID_Owner<LightmapInstance> lightmap_instance_owner; + /* SHADOW ATLAS */ // Note: The ShadowAtlas in the OpenGL is virtual. Each light gets assigned its @@ -622,6 +632,9 @@ public: /* LIGHTMAP INSTANCE */ + LightmapInstance *get_lightmap_instance(RID p_rid) { return lightmap_instance_owner.get_or_null(p_rid); }; + bool owns_lightmap_instance(RID p_rid) { return lightmap_instance_owner.owns(p_rid); }; + virtual RID lightmap_instance_create(RID p_lightmap) override; virtual void lightmap_instance_free(RID p_lightmap) override; virtual void lightmap_instance_set_transform(RID p_lightmap, const Transform3D &p_transform) override; diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp index b7a42141fd..13ab05c0a0 100644 --- a/drivers/gles3/storage/material_storage.cpp +++ b/drivers/gles3/storage/material_storage.cpp @@ -2968,17 +2968,17 @@ void SceneShaderData::set_code(const String &p_code) { depth_test = DepthTest(depth_testi); cull_mode = Cull(cull_modei); - vertex_input_mask = uint64_t(uses_normal); - vertex_input_mask |= uses_tangent << 1; - vertex_input_mask |= uses_color << 2; - vertex_input_mask |= uses_uv << 3; - vertex_input_mask |= uses_uv2 << 4; - vertex_input_mask |= uses_custom0 << 5; - vertex_input_mask |= uses_custom1 << 6; - vertex_input_mask |= uses_custom2 << 7; - vertex_input_mask |= uses_custom3 << 8; - vertex_input_mask |= uses_bones << 9; - vertex_input_mask |= uses_weights << 10; + vertex_input_mask = RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_NORMAL; // We can always read vertices and normals. + vertex_input_mask |= uses_tangent << RS::ARRAY_TANGENT; + vertex_input_mask |= uses_color << RS::ARRAY_COLOR; + vertex_input_mask |= uses_uv << RS::ARRAY_TEX_UV; + vertex_input_mask |= uses_uv2 << RS::ARRAY_TEX_UV2; + vertex_input_mask |= uses_custom0 << RS::ARRAY_CUSTOM0; + vertex_input_mask |= uses_custom1 << RS::ARRAY_CUSTOM1; + vertex_input_mask |= uses_custom2 << RS::ARRAY_CUSTOM2; + vertex_input_mask |= uses_custom3 << RS::ARRAY_CUSTOM3; + vertex_input_mask |= uses_bones << RS::ARRAY_BONES; + vertex_input_mask |= uses_weights << RS::ARRAY_WEIGHTS; uses_screen_texture = gen_code.uses_screen_texture; uses_screen_texture_mipmaps = gen_code.uses_screen_texture_mipmaps; @@ -3000,10 +3000,6 @@ void SceneShaderData::set_code(const String &p_code) { WARN_PRINT_ONCE_ED("Transmittance is only available when using the Forward+ rendering backend."); } - if (uses_depth_texture) { - WARN_PRINT_ONCE_ED("Reading from the depth texture is not supported when using the GL Compatibility backend yet. Support will be added in a future release."); - } - if (uses_normal_texture) { WARN_PRINT_ONCE_ED("Reading from the normal-roughness texture is only available when using the Forward+ or Mobile rendering backends."); } diff --git a/drivers/gles3/storage/mesh_storage.cpp b/drivers/gles3/storage/mesh_storage.cpp index 88ee749ed6..25e020b892 100644 --- a/drivers/gles3/storage/mesh_storage.cpp +++ b/drivers/gles3/storage/mesh_storage.cpp @@ -31,6 +31,7 @@ #ifdef GLES3_ENABLED #include "mesh_storage.h" +#include "config.h" #include "material_storage.h" #include "utilities.h" @@ -285,6 +286,69 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface) ERR_FAIL_COND_MSG(!new_surface.index_count && !new_surface.vertex_count, "Meshes must contain a vertex array, an index array, or both"); + if (GLES3::Config::get_singleton()->generate_wireframes && s->primitive == RS::PRIMITIVE_TRIANGLES) { + // Generate wireframes. This is mostly used by the editor. + s->wireframe = memnew(Mesh::Surface::Wireframe); + Vector<uint32_t> wf_indices; + uint32_t &wf_index_count = s->wireframe->index_count; + uint32_t *wr = nullptr; + + if (new_surface.format & RS::ARRAY_FORMAT_INDEX) { + wf_index_count = s->index_count * 2; + wf_indices.resize(wf_index_count); + + Vector<uint8_t> ir = new_surface.index_data; + wr = wf_indices.ptrw(); + + if (new_surface.vertex_count < (1 << 16)) { + // Read 16 bit indices. + const uint16_t *src_idx = (const uint16_t *)ir.ptr(); + for (uint32_t i = 0; i + 5 < wf_index_count; i += 6) { + // We use GL_LINES instead of GL_TRIANGLES for drawing these primitives later, + // so we need double the indices for each triangle. + wr[i + 0] = src_idx[i / 2]; + wr[i + 1] = src_idx[i / 2 + 1]; + wr[i + 2] = src_idx[i / 2 + 1]; + wr[i + 3] = src_idx[i / 2 + 2]; + wr[i + 4] = src_idx[i / 2 + 2]; + wr[i + 5] = src_idx[i / 2]; + } + + } else { + // Read 32 bit indices. + const uint32_t *src_idx = (const uint32_t *)ir.ptr(); + for (uint32_t i = 0; i + 5 < wf_index_count; i += 6) { + wr[i + 0] = src_idx[i / 2]; + wr[i + 1] = src_idx[i / 2 + 1]; + wr[i + 2] = src_idx[i / 2 + 1]; + wr[i + 3] = src_idx[i / 2 + 2]; + wr[i + 4] = src_idx[i / 2 + 2]; + wr[i + 5] = src_idx[i / 2]; + } + } + } else { + // Not using indices. + wf_index_count = s->vertex_count * 2; + wf_indices.resize(wf_index_count); + wr = wf_indices.ptrw(); + + for (uint32_t i = 0; i + 5 < wf_index_count; i += 6) { + wr[i + 0] = i / 2; + wr[i + 1] = i / 2 + 1; + wr[i + 2] = i / 2 + 1; + wr[i + 3] = i / 2 + 2; + wr[i + 4] = i / 2 + 2; + wr[i + 5] = i / 2; + } + } + + s->wireframe->index_buffer_size = wf_index_count * sizeof(uint32_t); + glGenBuffers(1, &s->wireframe->index_buffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s->wireframe->index_buffer); + GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ELEMENT_ARRAY_BUFFER, s->wireframe->index_buffer, s->wireframe->index_buffer_size, wr, GL_STATIC_DRAW, "Mesh wireframe index buffer"); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); // unbind + } + s->aabb = new_surface.aabb; s->bone_aabbs = new_surface.bone_aabbs; //only really useful for returning them. @@ -712,6 +776,11 @@ void MeshStorage::mesh_clear(RID p_mesh) { memfree(s.versions); //reallocs, so free with memfree. } + if (s.wireframe) { + GLES3::Utilities::get_singleton()->buffer_free_data(s.wireframe->index_buffer); + memdelete(s.wireframe); + } + if (s.lod_count) { for (uint32_t j = 0; j < s.lod_count; j++) { if (s.lods[j].index_buffer != 0) { @@ -764,14 +833,17 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V int skin_stride = 0; for (int i = 0; i < RS::ARRAY_INDEX; i++) { + attribs[i].enabled = false; + attribs[i].integer = false; if (!(s->format & (1ULL << i))) { - attribs[i].enabled = false; - attribs[i].integer = false; continue; } - attribs[i].enabled = true; - attribs[i].integer = false; + if ((p_input_mask & (1ULL << i))) { + // Only enable if it matches input mask. + // Iterate over all anyway, so we can calculate stride. + attribs[i].enabled = true; + } switch (i) { case RS::ARRAY_VERTEX: { @@ -1108,8 +1180,6 @@ void MeshStorage::_blend_shape_bind_mesh_instance_buffer(MeshInstance *p_mi, uin } void MeshStorage::_compute_skeleton(MeshInstance *p_mi, Skeleton *p_sk, uint32_t p_surface) { - glBindBuffer(GL_ARRAY_BUFFER, 0); - // Add in the bones and weights. glBindBuffer(GL_ARRAY_BUFFER, p_mi->mesh->surfaces[p_surface]->skin_buffer); @@ -1200,9 +1270,8 @@ void MeshStorage::update_mesh_instances() { glBindBuffer(GL_ARRAY_BUFFER, 0); GLuint vertex_array_gl = 0; - uint64_t mask = ((1 << 10) - 1) << 3; // Mask from ARRAY_FORMAT_COLOR to ARRAY_FORMAT_INDEX. - mask = ~mask; - uint64_t format = mi->surfaces[i].format_cache & mask; // Format should only have vertex, normal, tangent (as necessary) + compressions. + uint64_t mask = RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_NORMAL | RS::ARRAY_FORMAT_VERTEX; + uint64_t format = mi->mesh->surfaces[i]->format & mask; // Format should only have vertex, normal, tangent (as necessary). mesh_surface_get_vertex_arrays_and_format(mi->mesh->surfaces[i], format, vertex_array_gl); glBindVertexArray(vertex_array_gl); glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, mi->surfaces[i].vertex_buffers[0]); @@ -1315,9 +1384,8 @@ void MeshStorage::update_mesh_instances() { skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::INVERSE_TRANSFORM_OFFSET, inverse_transform[2], skeleton_shader.shader_version, variant, specialization); GLuint vertex_array_gl = 0; - uint64_t mask = ((1 << 10) - 1) << 3; // Mask from ARRAY_FORMAT_COLOR to ARRAY_FORMAT_INDEX. - mask = ~mask; - uint64_t format = mi->surfaces[i].format_cache & mask; // Format should only have vertex, normal, tangent (as necessary) + compressions. + uint64_t mask = RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_NORMAL | RS::ARRAY_FORMAT_VERTEX; + uint64_t format = mi->mesh->surfaces[i]->format & mask; // Format should only have vertex, normal, tangent (as necessary). mesh_surface_get_vertex_arrays_and_format(mi->mesh->surfaces[i], format, vertex_array_gl); glBindVertexArray(vertex_array_gl); _compute_skeleton(mi, sk, i); diff --git a/drivers/gles3/storage/mesh_storage.h b/drivers/gles3/storage/mesh_storage.h index 25b15ab6a6..f52a60d067 100644 --- a/drivers/gles3/storage/mesh_storage.h +++ b/drivers/gles3/storage/mesh_storage.h @@ -84,6 +84,14 @@ struct Mesh { uint32_t index_count = 0; uint32_t index_buffer_size = 0; + struct Wireframe { + GLuint index_buffer = 0; + uint32_t index_count = 0; + uint32_t index_buffer_size = 0; + }; + + Wireframe *wireframe = nullptr; + struct LOD { float edge_length = 0.0; uint32_t index_count = 0; @@ -376,6 +384,16 @@ public: } } + _FORCE_INLINE_ GLuint mesh_surface_get_index_buffer_wireframe(void *p_surface) const { + Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface); + + if (s->wireframe) { + return s->wireframe->index_buffer; + } + + return 0; + } + _FORCE_INLINE_ GLenum mesh_surface_get_index_type(void *p_surface) const { Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface); diff --git a/drivers/gles3/storage/render_scene_buffers_gles3.cpp b/drivers/gles3/storage/render_scene_buffers_gles3.cpp index 829574cae0..33bc9ab260 100644 --- a/drivers/gles3/storage/render_scene_buffers_gles3.cpp +++ b/drivers/gles3/storage/render_scene_buffers_gles3.cpp @@ -31,30 +31,508 @@ #ifdef GLES3_ENABLED #include "render_scene_buffers_gles3.h" +#include "config.h" #include "texture_storage.h" +#include "utilities.h" + +#ifdef ANDROID_ENABLED +#define glFramebufferTextureMultiviewOVR GLES3::Config::get_singleton()->eglFramebufferTextureMultiviewOVR +#define glTexStorage3DMultisample GLES3::Config::get_singleton()->eglTexStorage3DMultisample +#define glFramebufferTexture2DMultisampleEXT GLES3::Config::get_singleton()->eglFramebufferTexture2DMultisampleEXT +#define glFramebufferTextureMultisampleMultiviewOVR GLES3::Config::get_singleton()->eglFramebufferTextureMultisampleMultiviewOVR +#endif // ANDROID_ENABLED + +// Will only be defined if GLES 3.2 headers are included +#ifndef GL_TEXTURE_2D_MULTISAMPLE_ARRAY +#define GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102 +#endif RenderSceneBuffersGLES3::~RenderSceneBuffersGLES3() { free_render_buffer_data(); } +GLuint RenderSceneBuffersGLES3::_rt_get_cached_fbo(GLuint p_color, GLuint p_depth, GLsizei p_samples, uint32_t p_view_count) { + FBDEF new_fbo; + +#ifdef ANDROID_ENABLED + // There shouldn't be more then 3 entries in this... + for (const FBDEF &cached_fbo : msaa3d.cached_fbos) { + if (cached_fbo.color == p_color && cached_fbo.depth == p_depth) { + return cached_fbo.fbo; + } + } + + new_fbo.color = p_color; + new_fbo.depth = p_depth; + + glGenFramebuffers(1, &new_fbo.fbo); + glBindFramebuffer(GL_FRAMEBUFFER, new_fbo.fbo); + + if (p_view_count > 1) { + glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, p_color, 0, p_samples, 0, p_view_count); + glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, p_depth, 0, p_samples, 0, p_view_count); + } else { + glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, p_color, 0, p_samples); + glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, p_depth, 0, p_samples); + } + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + WARN_PRINT("Could not create 3D MSAA framebuffer, status: " + GLES3::TextureStorage::get_singleton()->get_framebuffer_error(status)); + + glDeleteFramebuffers(1, &new_fbo.fbo); + + new_fbo.fbo = 0; + } else { + // cache it! + msaa3d.cached_fbos.push_back(new_fbo); + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); +#endif + + return new_fbo.fbo; +} + void RenderSceneBuffersGLES3::configure(const RenderSceneBuffersConfiguration *p_config) { - //internal_size.x = p_config->get_internal_size().x; // ignore for now - //internal_size.y = p_config->get_internal_size().y; - width = p_config->get_target_size().x; - height = p_config->get_target_size().y; - //scaling_3d_mode = p_config->get_scaling_3d_mode() + GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); + GLES3::Config *config = GLES3::Config::get_singleton(); + + free_render_buffer_data(); + + internal_size = p_config->get_internal_size(); + target_size = p_config->get_target_size(); + scaling_3d_mode = p_config->get_scaling_3d_mode(); //fsr_sharpness = p_config->get_fsr_sharpness(); //texture_mipmap_bias = p_config->get_texture_mipmap_bias(); render_target = p_config->get_render_target(); - //msaa = p_config->get_msaa_3d(); + msaa3d.mode = p_config->get_msaa_3d(); //screen_space_aa = p_config->get_screen_space_aa(); //use_debanding = p_config->get_use_debanding(); - view_count = p_config->get_view_count(); + view_count = config->multiview_supported ? p_config->get_view_count() : 1; - free_render_buffer_data(); + ERR_FAIL_COND(view_count == 0); + bool use_multiview = view_count > 1; + + // Check our scaling mode + if (scaling_3d_mode != RS::VIEWPORT_SCALING_3D_MODE_OFF && internal_size.x == 0 && internal_size.y == 0) { + // Disable, no size set. + scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_OFF; + } else if (scaling_3d_mode != RS::VIEWPORT_SCALING_3D_MODE_OFF && internal_size == target_size) { + // If size matches, we won't use scaling. + scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_OFF; + } else if (scaling_3d_mode != RS::VIEWPORT_SCALING_3D_MODE_OFF && scaling_3d_mode != RS::VIEWPORT_SCALING_3D_MODE_BILINEAR) { + // We only support bilinear scaling atm. + WARN_PRINT_ONCE("GLES only supports bilinear scaling."); + scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_BILINEAR; + } + + bool use_internal_buffer = scaling_3d_mode != RS::VIEWPORT_SCALING_3D_MODE_OFF; // TODO also need this if doing post processing like glow + if (use_internal_buffer) { + // Setup our internal buffer. + bool is_transparent = texture_storage->render_target_get_transparent(render_target); + GLuint color_internal_format = is_transparent ? GL_RGBA8 : GL_RGB10_A2; + GLuint color_format = GL_RGBA; + GLuint color_type = is_transparent ? GL_UNSIGNED_BYTE : GL_UNSIGNED_INT_2_10_10_10_REV; + + GLenum texture_target = use_multiview ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D; + + // Create our color buffer. + glGenTextures(1, &internal3d.color); + glBindTexture(texture_target, internal3d.color); + + if (use_multiview) { + glTexImage3D(texture_target, 0, color_internal_format, internal_size.x, internal_size.y, view_count, 0, color_format, color_type, nullptr); + } else { + glTexImage2D(texture_target, 0, color_internal_format, internal_size.x, internal_size.y, 0, color_format, color_type, nullptr); + } + + glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + GLES3::Utilities::get_singleton()->texture_allocated_data(internal3d.color, internal_size.x * internal_size.y * view_count * 4, "3D color texture"); + + // Create our depth buffer. + glGenTextures(1, &internal3d.depth); + glBindTexture(texture_target, internal3d.depth); + + if (use_multiview) { + glTexImage3D(texture_target, 0, GL_DEPTH_COMPONENT24, internal_size.x, internal_size.y, view_count, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); + } else { + glTexImage2D(texture_target, 0, GL_DEPTH_COMPONENT24, internal_size.x, internal_size.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); + } + + glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + GLES3::Utilities::get_singleton()->texture_allocated_data(internal3d.depth, internal_size.x * internal_size.y * view_count * 3, "3D depth texture"); + + // Create our internal 3D FBO. + // Note that if MSAA is used and our rt_msaa_* extensions are available, this is only used for blitting and effects. + glGenFramebuffers(1, &internal3d.fbo); + glBindFramebuffer(GL_FRAMEBUFFER, internal3d.fbo); + +#ifndef IOS_ENABLED + if (use_multiview) { + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, internal3d.color, 0, 0, view_count); + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, internal3d.depth, 0, 0, view_count); + } else { +#else + { +#endif + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture_target, internal3d.color, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, texture_target, internal3d.depth, 0); + } + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + _clear_intermediate_buffers(); + WARN_PRINT("Could not create 3D buffers, status: " + texture_storage->get_framebuffer_error(status)); + } + + glBindTexture(texture_target, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + } + + // Check if we support MSAA. + if (msaa3d.mode != RS::VIEWPORT_MSAA_DISABLED && internal_size.x == 0 && internal_size.y == 0) { + // Disable, no size set. + msaa3d.mode = RS::VIEWPORT_MSAA_DISABLED; + } else if (!use_multiview && msaa3d.mode != RS::VIEWPORT_MSAA_DISABLED && !config->msaa_supported && !config->rt_msaa_supported) { + WARN_PRINT_ONCE("MSAA is not supported on this device."); + msaa3d.mode = RS::VIEWPORT_MSAA_DISABLED; + } else if (use_multiview && msaa3d.mode != RS::VIEWPORT_MSAA_DISABLED && !config->msaa_multiview_supported && !config->rt_msaa_multiview_supported) { + WARN_PRINT_ONCE("Multiview MSAA is not supported on this device."); + msaa3d.mode = RS::VIEWPORT_MSAA_DISABLED; + } + + if (msaa3d.mode != RS::VIEWPORT_MSAA_DISABLED) { + // Setup MSAA. + const GLsizei samples[] = { 1, 2, 4, 8 }; + msaa3d.samples = samples[msaa3d.mode]; + + // Constrain by limits of OpenGL driver. + if (msaa3d.samples > config->msaa_max_samples) { + msaa3d.samples = config->msaa_max_samples; + } + + if (!use_multiview && !config->rt_msaa_supported) { + // Render to texture extensions not supported? fall back to MSAA framebuffer through GL_EXT_framebuffer_multisample. + // Note, if 2D MSAA matches 3D MSAA and we're not scaling, it would be ideal if we reuse our 2D MSAA buffer here. + // We can't however because we don't trigger a change in configuration if 2D MSAA changes. + // We'll accept the overhead in this situation. + + msaa3d.needs_resolve = true; + msaa3d.check_fbo_cache = false; + + bool is_transparent = texture_storage->render_target_get_transparent(render_target); + GLuint color_internal_format = is_transparent ? GL_RGBA8 : GL_RGB10_A2; + + // Create our color buffer. + glGenRenderbuffers(1, &msaa3d.color); + glBindRenderbuffer(GL_RENDERBUFFER, msaa3d.color); + + glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaa3d.samples, color_internal_format, internal_size.x, internal_size.y); + GLES3::Utilities::get_singleton()->render_buffer_allocated_data(msaa3d.color, internal_size.x * internal_size.y * view_count * 4 * msaa3d.samples, "MSAA 3D color render buffer"); + + // Create our depth buffer. + glGenRenderbuffers(1, &msaa3d.depth); + glBindRenderbuffer(GL_RENDERBUFFER, msaa3d.depth); + + glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaa3d.samples, GL_DEPTH_COMPONENT24, internal_size.x, internal_size.y); + GLES3::Utilities::get_singleton()->render_buffer_allocated_data(msaa3d.depth, internal_size.x * internal_size.y * view_count * 3 * msaa3d.samples, "MSAA 3D depth render buffer"); + + // Create our MSAA 3D FBO. + glGenFramebuffers(1, &msaa3d.fbo); + glBindFramebuffer(GL_FRAMEBUFFER, msaa3d.fbo); + + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, msaa3d.color); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, msaa3d.depth); + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + _clear_msaa3d_buffers(); + WARN_PRINT("Could not create 3D MSAA buffers, status: " + texture_storage->get_framebuffer_error(status)); + } + + glBindRenderbuffer(GL_RENDERBUFFER, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); +#if !defined(IOS_ENABLED) && !defined(WEB_ENABLED) + } else if (use_multiview && !config->rt_msaa_multiview_supported) { + // Render to texture extensions not supported? fall back to MSAA textures through GL_EXT_multiview_texture_multisample. + msaa3d.needs_resolve = true; + msaa3d.check_fbo_cache = false; + + bool is_transparent = texture_storage->render_target_get_transparent(render_target); + GLuint color_internal_format = is_transparent ? GL_RGBA8 : GL_RGB10_A2; + + // Create our color buffer. + glGenTextures(1, &msaa3d.color); + glBindTexture(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, msaa3d.color); + +#ifdef ANDROID_ENABLED + glTexStorage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, msaa3d.samples, color_internal_format, internal_size.x, internal_size.y, view_count, GL_TRUE); +#else + glTexImage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, msaa3d.samples, color_internal_format, internal_size.x, internal_size.y, view_count, GL_TRUE); +#endif + + GLES3::Utilities::get_singleton()->texture_allocated_data(msaa3d.color, internal_size.x * internal_size.y * view_count * 4 * msaa3d.samples, "MSAA 3D color texture"); + + // Create our depth buffer. + glGenTextures(1, &msaa3d.depth); + glBindTexture(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, msaa3d.depth); + +#ifdef ANDROID_ENABLED + glTexStorage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, msaa3d.samples, GL_DEPTH_COMPONENT24, internal_size.x, internal_size.y, view_count, GL_TRUE); +#else + glTexImage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, msaa3d.samples, GL_DEPTH_COMPONENT24, internal_size.x, internal_size.y, view_count, GL_TRUE); +#endif + + GLES3::Utilities::get_singleton()->texture_allocated_data(msaa3d.depth, internal_size.x * internal_size.y * view_count * msaa3d.samples, "MSAA 3D depth texture"); + + // Create our MSAA 3D FBO. + glGenFramebuffers(1, &msaa3d.fbo); + glBindFramebuffer(GL_FRAMEBUFFER, msaa3d.fbo); + + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, msaa3d.color, 0, 0, view_count); + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, msaa3d.depth, 0, 0, view_count); + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + _clear_msaa3d_buffers(); + WARN_PRINT("Could not create 3D MSAA buffers, status: " + texture_storage->get_framebuffer_error(status)); + } + + glBindTexture(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); +#endif +#if defined(ANDROID_ENABLED) || defined(WEB_ENABLED) // Only supported on OpenGLES! + } else if (!use_internal_buffer) { + // We are going to render directly into our render target textures, + // these can change from frame to frame as we cycle through swapchains, + // hence we'll use our FBO cache here. + msaa3d.needs_resolve = false; + msaa3d.check_fbo_cache = true; +#endif +#ifdef ANDROID_ENABLED + } else if (use_internal_buffer) { + // We can combine MSAA and scaling/effects. + msaa3d.needs_resolve = false; + msaa3d.check_fbo_cache = false; + + // We render to our internal textures, MSAA is only done in tile memory only. + // On mobile this means MSAA never leaves tile memory = efficiency! + glGenFramebuffers(1, &msaa3d.fbo); + glBindFramebuffer(GL_FRAMEBUFFER, msaa3d.fbo); + + if (use_multiview) { + glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, internal3d.color, 0, msaa3d.samples, 0, view_count); + glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, internal3d.depth, 0, msaa3d.samples, 0, view_count); + } else { + glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, internal3d.color, 0, msaa3d.samples); + glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, internal3d.depth, 0, msaa3d.samples); + } + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + _clear_msaa3d_buffers(); + WARN_PRINT("Could not create 3D MSAA framebuffer, status: " + texture_storage->get_framebuffer_error(status)); + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); +#endif + } else { + // HUH? how did we get here? + WARN_PRINT_ONCE("MSAA is not supported on this device."); + msaa3d.mode = RS::VIEWPORT_MSAA_DISABLED; + msaa3d.samples = 1; + msaa3d.check_fbo_cache = false; + } + } else { + msaa3d.samples = 1; + msaa3d.check_fbo_cache = false; + } +} + +void RenderSceneBuffersGLES3::_clear_msaa3d_buffers() { + for (const FBDEF &cached_fbo : msaa3d.cached_fbos) { + GLuint fbo = cached_fbo.fbo; + glDeleteFramebuffers(1, &fbo); + } + msaa3d.cached_fbos.clear(); + + if (msaa3d.fbo) { + glDeleteFramebuffers(1, &msaa3d.fbo); + msaa3d.fbo = 0; + } + + if (msaa3d.color != 0) { + if (view_count == 1) { + GLES3::Utilities::get_singleton()->render_buffer_free_data(msaa3d.color); + } else { + GLES3::Utilities::get_singleton()->texture_free_data(msaa3d.color); + } + msaa3d.color = 0; + } + + if (msaa3d.depth != 0) { + if (view_count == 1) { + GLES3::Utilities::get_singleton()->render_buffer_free_data(msaa3d.depth); + } else { + GLES3::Utilities::get_singleton()->texture_free_data(msaa3d.depth); + } + msaa3d.depth = 0; + } +} + +void RenderSceneBuffersGLES3::_clear_intermediate_buffers() { + if (internal3d.fbo) { + glDeleteFramebuffers(1, &internal3d.fbo); + internal3d.fbo = 0; + } + + if (internal3d.color != 0) { + GLES3::Utilities::get_singleton()->texture_free_data(internal3d.color); + internal3d.color = 0; + } + + if (internal3d.depth != 0) { + GLES3::Utilities::get_singleton()->texture_free_data(internal3d.depth); + internal3d.depth = 0; + } +} + +void RenderSceneBuffersGLES3::check_backbuffer(bool p_need_color, bool p_need_depth) { + GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); + + // Setup our back buffer + + if (backbuffer3d.fbo == 0) { + glGenFramebuffers(1, &backbuffer3d.fbo); + } + + glBindFramebuffer(GL_FRAMEBUFFER, backbuffer3d.fbo); + + bool is_transparent = texture_storage->render_target_get_transparent(render_target); + GLuint color_internal_format = is_transparent ? GL_RGBA8 : GL_RGB10_A2; + GLuint color_format = GL_RGBA; + GLuint color_type = is_transparent ? GL_UNSIGNED_BYTE : GL_UNSIGNED_INT_2_10_10_10_REV; + + bool use_multiview = view_count > 1 && GLES3::Config::get_singleton()->multiview_supported; + GLenum texture_target = use_multiview ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D; + + if (backbuffer3d.color == 0 && p_need_color) { + glGenTextures(1, &backbuffer3d.color); + glBindTexture(texture_target, backbuffer3d.color); + + if (use_multiview) { + glTexImage3D(texture_target, 0, color_internal_format, internal_size.x, internal_size.y, view_count, 0, color_format, color_type, nullptr); + } else { + glTexImage2D(texture_target, 0, color_internal_format, internal_size.x, internal_size.y, 0, color_format, color_type, nullptr); + } + + glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + GLES3::Utilities::get_singleton()->texture_allocated_data(backbuffer3d.color, internal_size.x * internal_size.y * view_count * 4, "3D Back buffer color texture"); + +#ifndef IOS_ENABLED + if (use_multiview) { + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, backbuffer3d.color, 0, 0, view_count); + } else { +#else + { +#endif + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture_target, backbuffer3d.color, 0); + } + } + + if (backbuffer3d.depth == 0 && p_need_depth) { + glGenTextures(1, &backbuffer3d.depth); + glBindTexture(texture_target, backbuffer3d.depth); + + if (use_multiview) { + glTexImage3D(texture_target, 0, GL_DEPTH_COMPONENT24, internal_size.x, internal_size.y, view_count, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); + } else { + glTexImage2D(texture_target, 0, GL_DEPTH_COMPONENT24, internal_size.x, internal_size.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); + } + + glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + GLES3::Utilities::get_singleton()->texture_allocated_data(backbuffer3d.depth, internal_size.x * internal_size.y * view_count * 3, "3D back buffer depth texture"); + +#ifndef IOS_ENABLED + if (use_multiview) { + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, backbuffer3d.depth, 0, 0, view_count); + } else { +#else + { +#endif + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, texture_target, backbuffer3d.depth, 0); + } + } + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + _clear_back_buffers(); + WARN_PRINT("Could not create 3D back buffers, status: " + texture_storage->get_framebuffer_error(status)); + } + + glBindTexture(texture_target, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +void RenderSceneBuffersGLES3::_clear_back_buffers() { + if (backbuffer3d.fbo) { + glDeleteFramebuffers(1, &backbuffer3d.fbo); + backbuffer3d.fbo = 0; + } + + if (backbuffer3d.color != 0) { + GLES3::Utilities::get_singleton()->texture_free_data(backbuffer3d.color); + backbuffer3d.color = 0; + } + + if (backbuffer3d.depth != 0) { + GLES3::Utilities::get_singleton()->texture_free_data(backbuffer3d.depth); + backbuffer3d.depth = 0; + } } void RenderSceneBuffersGLES3::free_render_buffer_data() { + _clear_msaa3d_buffers(); + _clear_intermediate_buffers(); + _clear_back_buffers(); +} + +GLuint RenderSceneBuffersGLES3::get_render_fbo() { + if (msaa3d.check_fbo_cache) { + GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); + + GLuint color = texture_storage->render_target_get_color(render_target); + GLuint depth = texture_storage->render_target_get_depth(render_target); + + return _rt_get_cached_fbo(color, depth, msaa3d.samples, view_count); + } else if (msaa3d.fbo != 0) { + // We have an MSAA fbo, render to our MSAA buffer + return msaa3d.fbo; + } else if (internal3d.fbo != 0) { + // We have an internal buffer, render to our internal buffer! + return internal3d.fbo; + } else { + GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); + + return texture_storage->render_target_get_fbo(render_target); + } } #endif // GLES3_ENABLED diff --git a/drivers/gles3/storage/render_scene_buffers_gles3.h b/drivers/gles3/storage/render_scene_buffers_gles3.h index 543e1aeb15..95097f54d8 100644 --- a/drivers/gles3/storage/render_scene_buffers_gles3.h +++ b/drivers/gles3/storage/render_scene_buffers_gles3.h @@ -41,21 +41,40 @@ class RenderSceneBuffersGLES3 : public RenderSceneBuffers { GDCLASS(RenderSceneBuffersGLES3, RenderSceneBuffers); public: - // Original implementation, need to investigate which ones we'll keep like this and what we'll change... - - int internal_width = 0; - int internal_height = 0; - int width = 0; - int height = 0; + Size2i internal_size; // Size of the buffer we render 3D content to. + Size2i target_size; // Size of our output buffer (render target). + RS::ViewportScaling3DMode scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_OFF; //float fsr_sharpness = 0.2f; - RS::ViewportMSAA msaa = RS::VIEWPORT_MSAA_DISABLED; //RS::ViewportScreenSpaceAA screen_space_aa = RS::VIEWPORT_SCREEN_SPACE_AA_DISABLED; + //bool use_taa = false; //bool use_debanding = false; uint32_t view_count = 1; RID render_target; - //built-in textures used for ping pong image processing and blurring + struct FBDEF { + GLuint color = 0; + GLuint depth = 0; + GLuint fbo = 0; + }; + + struct RTMSAA3D { + RS::ViewportMSAA mode = RS::VIEWPORT_MSAA_DISABLED; + bool needs_resolve = false; + GLsizei samples = 1; + GLuint color = 0; + GLuint depth = 0; + GLuint fbo = 0; + + bool check_fbo_cache = false; + Vector<FBDEF> cached_fbos; + } msaa3d; // MSAA buffers used to render 3D + + FBDEF internal3d; // buffers used to either render 3D (scaled/post) or to resolve MSAA into + + FBDEF backbuffer3d; // our back buffer + + // Built-in textures used for ping pong image processing and blurring. struct Blur { RID texture; @@ -72,6 +91,12 @@ public: Blur blur[2]; //the second one starts from the first mipmap private: + void _clear_msaa3d_buffers(); + void _clear_intermediate_buffers(); + void _clear_back_buffers(); + + GLuint _rt_get_cached_fbo(GLuint p_color, GLuint p_depth, GLsizei p_samples, uint32_t p_view_count); + public: virtual ~RenderSceneBuffersGLES3(); virtual void configure(const RenderSceneBuffersConfiguration *p_config) override; @@ -81,6 +106,33 @@ public: virtual void set_use_debanding(bool p_use_debanding) override{}; void free_render_buffer_data(); + + void check_backbuffer(bool p_need_color, bool p_need_depth); // check if we need to initialise our backbuffer + + GLuint get_render_fbo(); + GLuint get_msaa3d_fbo() const { return msaa3d.fbo; } + GLuint get_msaa3d_color() const { return msaa3d.color; } + GLuint get_msaa3d_depth() const { return msaa3d.depth; } + bool get_msaa_needs_resolve() const { return msaa3d.needs_resolve; } + GLuint get_internal_fbo() const { return internal3d.fbo; } + GLuint get_internal_color() const { return internal3d.color; } + GLuint get_internal_depth() const { return internal3d.depth; } + GLuint get_backbuffer_fbo() const { return backbuffer3d.fbo; } + GLuint get_backbuffer() const { return backbuffer3d.color; } + GLuint get_backbuffer_depth() const { return backbuffer3d.depth; } + + // Getters + + _FORCE_INLINE_ RID get_render_target() const { return render_target; } + _FORCE_INLINE_ uint32_t get_view_count() const { return view_count; } + _FORCE_INLINE_ Size2i get_internal_size() const { return internal_size; } + _FORCE_INLINE_ Size2i get_target_size() const { return target_size; } + _FORCE_INLINE_ RS::ViewportScaling3DMode get_scaling_3d_mode() const { return scaling_3d_mode; } + //_FORCE_INLINE_ float get_fsr_sharpness() const { return fsr_sharpness; } + _FORCE_INLINE_ RS::ViewportMSAA get_msaa_3d() const { return msaa3d.mode; } + //_FORCE_INLINE_ RS::ViewportScreenSpaceAA get_screen_space_aa() const { return screen_space_aa; } + //_FORCE_INLINE_ bool get_use_taa() const { return use_taa; } + //_FORCE_INLINE_ bool get_use_debanding() const { return use_debanding; } }; #endif // GLES3_ENABLED diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp index dec0a3f811..77dd15fa28 100644 --- a/drivers/gles3/storage/texture_storage.cpp +++ b/drivers/gles3/storage/texture_storage.cpp @@ -1732,7 +1732,7 @@ void TextureStorage::_update_render_target(RenderTarget *rt) { #else { #endif - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->color, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture_target, rt->color, 0); } // depth @@ -1765,7 +1765,7 @@ void TextureStorage::_update_render_target(RenderTarget *rt) { #else { #endif - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, rt->depth, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, texture_target, rt->depth, 0); } GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); @@ -1874,7 +1874,7 @@ void TextureStorage::_create_render_target_backbuffer(RenderTarget *rt) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } } -void GLES3::TextureStorage::copy_scene_to_backbuffer(RenderTarget *rt, const bool uses_screen_texture, const bool uses_depth_texture) { +void GLES3::TextureStorage::check_backbuffer(RenderTarget *rt, const bool uses_screen_texture, const bool uses_depth_texture) { if (rt->backbuffer != 0 && rt->backbuffer_depth != 0) { return; } @@ -1935,7 +1935,7 @@ void GLES3::TextureStorage::copy_scene_to_backbuffer(RenderTarget *rt, const boo } } void TextureStorage::_clear_render_target(RenderTarget *rt) { - // there is nothing to clear when DIRECT_TO_SCREEN is used + // there is nothing else to clear when DIRECT_TO_SCREEN is used if (rt->direct_to_screen) { return; } @@ -2229,6 +2229,7 @@ void TextureStorage::render_target_clear_used(RID p_render_target) { void TextureStorage::render_target_set_msaa(RID p_render_target, RS::ViewportMSAA p_msaa) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_NULL(rt); + ERR_FAIL_COND(rt->direct_to_screen); if (p_msaa == rt->msaa) { return; } @@ -2284,6 +2285,41 @@ void TextureStorage::render_target_do_clear_request(RID p_render_target) { glBindFramebuffer(GL_FRAMEBUFFER, system_fbo); } +GLuint TextureStorage::render_target_get_fbo(RID p_render_target) const { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_NULL_V(rt, 0); + + return rt->fbo; +} + +GLuint TextureStorage::render_target_get_color(RID p_render_target) const { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_NULL_V(rt, 0); + + if (rt->overridden.color.is_valid()) { + Texture *texture = get_texture(rt->overridden.color); + ERR_FAIL_NULL_V(texture, 0); + + return texture->tex_id; + } else { + return rt->color; + } +} + +GLuint TextureStorage::render_target_get_depth(RID p_render_target) const { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_NULL_V(rt, 0); + + if (rt->overridden.depth.is_valid()) { + Texture *texture = get_texture(rt->overridden.depth); + ERR_FAIL_NULL_V(texture, 0); + + return texture->tex_id; + } else { + return rt->depth; + } +} + void TextureStorage::render_target_set_sdf_size_and_scale(RID p_render_target, RS::ViewportSDFOversize p_size, RS::ViewportSDFScale p_scale) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_NULL(rt); diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h index 27e358ec31..2ce719eb08 100644 --- a/drivers/gles3/storage/texture_storage.h +++ b/drivers/gles3/storage/texture_storage.h @@ -484,7 +484,7 @@ public: /* Texture API */ - Texture *get_texture(RID p_rid) { + Texture *get_texture(RID p_rid) const { Texture *texture = texture_owner.get_or_null(p_rid); if (texture && texture->is_proxy) { return texture_owner.get_or_null(texture->proxy_to); @@ -602,7 +602,7 @@ public: RenderTarget *get_render_target(RID p_rid) { return render_target_owner.get_or_null(p_rid); }; bool owns_render_target(RID p_rid) { return render_target_owner.owns(p_rid); }; - void copy_scene_to_backbuffer(RenderTarget *rt, const bool uses_screen_texture, const bool uses_depth_texture); + void check_backbuffer(RenderTarget *rt, const bool uses_screen_texture, const bool uses_depth_texture); virtual RID render_target_create() override; virtual void render_target_free(RID p_rid) override; @@ -636,6 +636,10 @@ public: void render_target_disable_clear_request(RID p_render_target) override; void render_target_do_clear_request(RID p_render_target) override; + GLuint render_target_get_fbo(RID p_render_target) const; + GLuint render_target_get_color(RID p_render_target) const; + GLuint render_target_get_depth(RID p_render_target) const; + virtual void render_target_set_sdf_size_and_scale(RID p_render_target, RS::ViewportSDFOversize p_size, RS::ViewportSDFScale p_scale) override; virtual Rect2i render_target_get_sdf_rect(RID p_render_target) const override; GLuint render_target_get_sdf_texture(RID p_render_target); diff --git a/drivers/gles3/storage/utilities.cpp b/drivers/gles3/storage/utilities.cpp index 72bcbe879c..8a9e61c725 100644 --- a/drivers/gles3/storage/utilities.cpp +++ b/drivers/gles3/storage/utilities.cpp @@ -85,6 +85,21 @@ Utilities::~Utilities() { } } + if (render_buffer_mem_cache) { + uint32_t leaked_data_size = 0; + for (const KeyValue<GLuint, ResourceAllocation> &E : render_buffer_allocs_cache) { +#ifdef DEV_ENABLED + ERR_PRINT(E.value.name + ": leaked " + itos(E.value.size) + " bytes."); +#else + ERR_PRINT("Render buffer with GL ID of " + itos(E.key) + ": leaked " + itos(E.value.size) + " bytes."); +#endif + leaked_data_size += E.value.size; + } + if (leaked_data_size < render_buffer_mem_cache) { + ERR_PRINT("Render buffer cache is not empty. There may be an additional render buffer leak of " + itos(render_buffer_mem_cache - leaked_data_size) + " bytes."); + } + } + if (buffer_mem_cache) { uint32_t leaked_data_size = 0; @@ -327,6 +342,8 @@ void Utilities::update_dirty_resources() { } void Utilities::set_debug_generate_wireframes(bool p_generate) { + Config *config = Config::get_singleton(); + config->generate_wireframes = p_generate; } bool Utilities::has_os_feature(const String &p_feature) const { @@ -362,11 +379,11 @@ void Utilities::update_memory_info() { uint64_t Utilities::get_rendering_info(RS::RenderingInfo p_info) { if (p_info == RS::RENDERING_INFO_TEXTURE_MEM_USED) { - return texture_mem_cache; + return texture_mem_cache + render_buffer_mem_cache; // Add render buffer memory to our texture mem. } else if (p_info == RS::RENDERING_INFO_BUFFER_MEM_USED) { return buffer_mem_cache; } else if (p_info == RS::RENDERING_INFO_VIDEO_MEM_USED) { - return texture_mem_cache + buffer_mem_cache; + return texture_mem_cache + buffer_mem_cache + render_buffer_mem_cache; } return 0; } diff --git a/drivers/gles3/storage/utilities.h b/drivers/gles3/storage/utilities.h index 76e4d510de..ea7bf4a4c2 100644 --- a/drivers/gles3/storage/utilities.h +++ b/drivers/gles3/storage/utilities.h @@ -50,9 +50,11 @@ private: uint32_t size = 0; }; HashMap<GLuint, ResourceAllocation> buffer_allocs_cache; + HashMap<GLuint, ResourceAllocation> render_buffer_allocs_cache; HashMap<GLuint, ResourceAllocation> texture_allocs_cache; uint64_t buffer_mem_cache = 0; + uint64_t render_buffer_mem_cache = 0; uint64_t texture_mem_cache = 0; public: @@ -88,6 +90,26 @@ public: buffer_allocs_cache.erase(p_id); } + _FORCE_INLINE_ void render_buffer_allocated_data(GLuint p_id, uint32_t p_size, String p_name = "") { + render_buffer_mem_cache += p_size; +#ifdef DEV_ENABLED + ERR_FAIL_COND_MSG(render_buffer_allocs_cache.has(p_id), "trying to allocate render buffer with name " + p_name + " but ID already used by " + render_buffer_allocs_cache[p_id].name); +#endif + ResourceAllocation resource_allocation; + resource_allocation.size = p_size; +#ifdef DEV_ENABLED + resource_allocation.name = p_name + ": " + itos((uint64_t)p_id); +#endif + render_buffer_allocs_cache[p_id] = resource_allocation; + } + + _FORCE_INLINE_ void render_buffer_free_data(GLuint p_id) { + ERR_FAIL_COND(!render_buffer_allocs_cache.has(p_id)); + glDeleteRenderbuffers(1, &p_id); + render_buffer_mem_cache -= render_buffer_allocs_cache[p_id].size; + render_buffer_allocs_cache.erase(p_id); + } + // Records that data was allocated for state tracking purposes. _FORCE_INLINE_ void texture_allocated_data(GLuint p_id, uint32_t p_size, String p_name = "") { texture_mem_cache += p_size; diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp index c3a365a3b8..04aeac2bfc 100644 --- a/drivers/vulkan/rendering_device_vulkan.cpp +++ b/drivers/vulkan/rendering_device_vulkan.cpp @@ -1365,6 +1365,9 @@ Error RenderingDeviceVulkan::_buffer_allocate(Buffer *p_buffer, uint32_t p_size, allocInfo.memoryTypeBits = 0; allocInfo.pool = nullptr; allocInfo.pUserData = nullptr; + if (p_mem_usage == VMA_MEMORY_USAGE_AUTO_PREFER_HOST) { + allocInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; + } if (p_size <= SMALL_ALLOCATION_MAX_SIZE) { uint32_t mem_type_index = 0; vmaFindMemoryTypeIndexForBufferInfo(allocator, &bufferInfo, &allocInfo, &mem_type_index); @@ -1410,7 +1413,7 @@ Error RenderingDeviceVulkan::_insert_staging_block() { VmaAllocationCreateInfo allocInfo; allocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; allocInfo.usage = VMA_MEMORY_USAGE_AUTO_PREFER_HOST; - allocInfo.requiredFlags = 0; + allocInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; allocInfo.preferredFlags = 0; allocInfo.memoryTypeBits = 0; allocInfo.pool = nullptr; @@ -5995,7 +5998,7 @@ Error RenderingDeviceVulkan::buffer_update(RID p_buffer, uint32_t p_offset, uint // No barrier should be needed here. // _buffer_memory_barrier(buffer->buffer, p_offset, p_size, dst_stage_mask, VK_PIPELINE_STAGE_TRANSFER_BIT, dst_access, VK_ACCESS_TRANSFER_WRITE_BIT, true); - Error err = _buffer_update(buffer, p_offset, (uint8_t *)p_data, p_size, p_post_barrier); + Error err = _buffer_update(buffer, p_offset, (uint8_t *)p_data, p_size, true); if (err) { return err; } @@ -6841,24 +6844,24 @@ Error RenderingDeviceVulkan::_draw_list_setup_framebuffer(Framebuffer *p_framebu return OK; } -Error RenderingDeviceVulkan::_draw_list_render_pass_begin(Framebuffer *framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_colors, float p_clear_depth, uint32_t p_clear_stencil, Point2i viewport_offset, Point2i viewport_size, VkFramebuffer vkframebuffer, VkRenderPass render_pass, VkCommandBuffer command_buffer, VkSubpassContents subpass_contents, const Vector<RID> &p_storage_textures) { +Error RenderingDeviceVulkan::_draw_list_render_pass_begin(Framebuffer *framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_colors, float p_clear_depth, uint32_t p_clear_stencil, Point2i viewport_offset, Point2i viewport_size, VkFramebuffer vkframebuffer, VkRenderPass render_pass, VkCommandBuffer command_buffer, VkSubpassContents subpass_contents, const Vector<RID> &p_storage_textures, bool p_constrained_to_region) { VkRenderPassBeginInfo render_pass_begin; render_pass_begin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; render_pass_begin.pNext = nullptr; render_pass_begin.renderPass = render_pass; render_pass_begin.framebuffer = vkframebuffer; - /* - * Given how API works, it makes sense to always fully operate on the whole framebuffer. - * This allows better continue operations for operations like shadowmapping. - render_pass_begin.renderArea.extent.width = viewport_size.width; - render_pass_begin.renderArea.extent.height = viewport_size.height; - render_pass_begin.renderArea.offset.x = viewport_offset.x; - render_pass_begin.renderArea.offset.y = viewport_offset.y; - */ - render_pass_begin.renderArea.extent.width = framebuffer->size.width; - render_pass_begin.renderArea.extent.height = framebuffer->size.height; - render_pass_begin.renderArea.offset.x = 0; - render_pass_begin.renderArea.offset.y = 0; + + if (p_constrained_to_region) { + render_pass_begin.renderArea.extent.width = viewport_size.width; + render_pass_begin.renderArea.extent.height = viewport_size.height; + render_pass_begin.renderArea.offset.x = viewport_offset.x; + render_pass_begin.renderArea.offset.y = viewport_offset.y; + } else { + render_pass_begin.renderArea.extent.width = framebuffer->size.width; + render_pass_begin.renderArea.extent.height = framebuffer->size.height; + render_pass_begin.renderArea.offset.x = 0; + render_pass_begin.renderArea.offset.y = 0; + } Vector<VkClearValue> clear_values; clear_values.resize(framebuffer->texture_ids.size()); @@ -7008,6 +7011,7 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin(RID p_framebu Point2i viewport_offset; Point2i viewport_size = framebuffer->size; + bool constrained_to_region = false; bool needs_clear_color = false; bool needs_clear_depth = false; @@ -7022,21 +7026,30 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin(RID p_framebu viewport_offset = regioni.position; viewport_size = regioni.size; - if (p_initial_color_action == INITIAL_ACTION_CLEAR_REGION_CONTINUE) { - needs_clear_color = true; - p_initial_color_action = INITIAL_ACTION_CONTINUE; - } - if (p_initial_depth_action == INITIAL_ACTION_CLEAR_REGION_CONTINUE) { - needs_clear_depth = true; - p_initial_depth_action = INITIAL_ACTION_CONTINUE; - } - if (p_initial_color_action == INITIAL_ACTION_CLEAR_REGION) { - needs_clear_color = true; - p_initial_color_action = INITIAL_ACTION_KEEP; - } - if (p_initial_depth_action == INITIAL_ACTION_CLEAR_REGION) { - needs_clear_depth = true; - p_initial_depth_action = INITIAL_ACTION_KEEP; + + // If clearing regions both in color and depth, we can switch to a fast path where we let Vulkan to the clears + // and we constrain the render area to the region. + if (p_initial_color_action == INITIAL_ACTION_CLEAR_REGION && p_initial_depth_action == INITIAL_ACTION_CLEAR_REGION) { + constrained_to_region = true; + p_initial_color_action = INITIAL_ACTION_CLEAR; + p_initial_depth_action = INITIAL_ACTION_CLEAR; + } else { + if (p_initial_color_action == INITIAL_ACTION_CLEAR_REGION_CONTINUE) { + needs_clear_color = true; + p_initial_color_action = INITIAL_ACTION_CONTINUE; + } + if (p_initial_depth_action == INITIAL_ACTION_CLEAR_REGION_CONTINUE) { + needs_clear_depth = true; + p_initial_depth_action = INITIAL_ACTION_CONTINUE; + } + if (p_initial_color_action == INITIAL_ACTION_CLEAR_REGION) { + needs_clear_color = true; + p_initial_color_action = INITIAL_ACTION_KEEP; + } + if (p_initial_depth_action == INITIAL_ACTION_CLEAR_REGION) { + needs_clear_depth = true; + p_initial_depth_action = INITIAL_ACTION_KEEP; + } } } @@ -7063,7 +7076,7 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin(RID p_framebu ERR_FAIL_COND_V(err != OK, INVALID_ID); VkCommandBuffer command_buffer = frames[frame].draw_command_buffer; - err = _draw_list_render_pass_begin(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, viewport_offset, viewport_size, vkframebuffer, render_pass, command_buffer, VK_SUBPASS_CONTENTS_INLINE, p_storage_textures); + err = _draw_list_render_pass_begin(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, viewport_offset, viewport_size, vkframebuffer, render_pass, command_buffer, VK_SUBPASS_CONTENTS_INLINE, p_storage_textures, constrained_to_region); if (err != OK) { return INVALID_ID; @@ -7079,6 +7092,7 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin(RID p_framebu draw_list_current_subpass = 0; if (needs_clear_color || needs_clear_depth) { + DEV_ASSERT(!constrained_to_region); _draw_list_insert_clear_region(draw_list, framebuffer, viewport_offset, viewport_size, needs_clear_color, p_clear_color_values, needs_clear_depth, p_clear_depth, p_clear_stencil); } @@ -7117,6 +7131,7 @@ Error RenderingDeviceVulkan::draw_list_begin_split(RID p_framebuffer, uint32_t p Point2i viewport_offset; Point2i viewport_size = framebuffer->size; + bool constrained_to_region = false; bool needs_clear_color = false; bool needs_clear_depth = false; @@ -7132,13 +7147,29 @@ Error RenderingDeviceVulkan::draw_list_begin_split(RID p_framebuffer, uint32_t p viewport_offset = regioni.position; viewport_size = regioni.size; - if (p_initial_color_action == INITIAL_ACTION_CLEAR_REGION) { - needs_clear_color = true; - p_initial_color_action = INITIAL_ACTION_KEEP; - } - if (p_initial_depth_action == INITIAL_ACTION_CLEAR_REGION) { - needs_clear_depth = true; - p_initial_depth_action = INITIAL_ACTION_KEEP; + // If clearing regions both in color and depth, we can switch to a fast path where we let Vulkan to the clears + // and we constrain the render area to the region. + if (p_initial_color_action == INITIAL_ACTION_CLEAR_REGION && p_initial_depth_action == INITIAL_ACTION_CLEAR_REGION) { + constrained_to_region = true; + p_initial_color_action = INITIAL_ACTION_CLEAR; + p_initial_depth_action = INITIAL_ACTION_CLEAR; + } else { + if (p_initial_color_action == INITIAL_ACTION_CLEAR_REGION_CONTINUE) { + needs_clear_color = true; + p_initial_color_action = INITIAL_ACTION_CONTINUE; + } + if (p_initial_depth_action == INITIAL_ACTION_CLEAR_REGION_CONTINUE) { + needs_clear_depth = true; + p_initial_depth_action = INITIAL_ACTION_CONTINUE; + } + if (p_initial_color_action == INITIAL_ACTION_CLEAR_REGION) { + needs_clear_color = true; + p_initial_color_action = INITIAL_ACTION_KEEP; + } + if (p_initial_depth_action == INITIAL_ACTION_CLEAR_REGION) { + needs_clear_depth = true; + p_initial_depth_action = INITIAL_ACTION_KEEP; + } } } @@ -7164,7 +7195,7 @@ Error RenderingDeviceVulkan::draw_list_begin_split(RID p_framebuffer, uint32_t p ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); VkCommandBuffer frame_command_buffer = frames[frame].draw_command_buffer; - err = _draw_list_render_pass_begin(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, viewport_offset, viewport_size, vkframebuffer, render_pass, frame_command_buffer, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS, p_storage_textures); + err = _draw_list_render_pass_begin(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, viewport_offset, viewport_size, vkframebuffer, render_pass, frame_command_buffer, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS, p_storage_textures, constrained_to_region); if (err != OK) { return ERR_CANT_CREATE; @@ -7184,6 +7215,7 @@ Error RenderingDeviceVulkan::draw_list_begin_split(RID p_framebuffer, uint32_t p } if (needs_clear_color || needs_clear_depth) { + DEV_ASSERT(!constrained_to_region); _draw_list_insert_clear_region(&draw_list[0], framebuffer, viewport_offset, viewport_size, needs_clear_color, p_clear_color_values, needs_clear_depth, p_clear_depth, p_clear_stencil); } diff --git a/drivers/vulkan/rendering_device_vulkan.h b/drivers/vulkan/rendering_device_vulkan.h index 7c514c11f8..e8ad0e4f45 100644 --- a/drivers/vulkan/rendering_device_vulkan.h +++ b/drivers/vulkan/rendering_device_vulkan.h @@ -43,7 +43,7 @@ #define _DEBUG #endif #endif -#include "vk_mem_alloc.h" +#include "thirdparty/vulkan/vk_mem_alloc.h" #ifdef USE_VOLK #include <volk.h> @@ -159,7 +159,7 @@ class RenderingDeviceVulkan : public RenderingDevice { RID owner; }; - RID_Owner<Texture, true> texture_owner; + RID_Owner<Texture> texture_owner; uint32_t texture_upload_region_size_px = 0; Vector<uint8_t> _texture_get_data_from_image(Texture *tex, VkImage p_image, VmaAllocation p_allocation, uint32_t p_layer, bool p_2d = false); @@ -409,7 +409,7 @@ class RenderingDeviceVulkan : public RenderingDevice { uint32_t view_count; }; - RID_Owner<Framebuffer, true> framebuffer_owner; + RID_Owner<Framebuffer> framebuffer_owner; /***********************/ /**** VERTEX BUFFER ****/ @@ -424,7 +424,7 @@ class RenderingDeviceVulkan : public RenderingDevice { // This mapping is done here internally, and it's not // exposed. - RID_Owner<Buffer, true> vertex_buffer_owner; + RID_Owner<Buffer> vertex_buffer_owner; struct VertexDescriptionKey { Vector<VertexAttribute> vertex_formats; @@ -506,7 +506,7 @@ class RenderingDeviceVulkan : public RenderingDevice { Vector<VkDeviceSize> offsets; }; - RID_Owner<VertexArray, true> vertex_array_owner; + RID_Owner<VertexArray> vertex_array_owner; struct IndexBuffer : public Buffer { uint32_t max_index = 0; // Used for validation. @@ -515,7 +515,7 @@ class RenderingDeviceVulkan : public RenderingDevice { bool supports_restart_indices = false; }; - RID_Owner<IndexBuffer, true> index_buffer_owner; + RID_Owner<IndexBuffer> index_buffer_owner; struct IndexArray { uint32_t max_index = 0; // Remember the maximum index here too, for validation. @@ -526,7 +526,7 @@ class RenderingDeviceVulkan : public RenderingDevice { bool supports_restart_indices = false; }; - RID_Owner<IndexArray, true> index_array_owner; + RID_Owner<IndexArray> index_array_owner; /****************/ /**** SHADER ****/ @@ -649,7 +649,7 @@ class RenderingDeviceVulkan : public RenderingDevice { String _shader_uniform_debug(RID p_shader, int p_set = -1); - RID_Owner<Shader, true> shader_owner; + RID_Owner<Shader> shader_owner; /******************/ /**** UNIFORMS ****/ @@ -713,8 +713,8 @@ class RenderingDeviceVulkan : public RenderingDevice { DescriptorPool *_descriptor_pool_allocate(const DescriptorPoolKey &p_key); void _descriptor_pool_free(const DescriptorPoolKey &p_key, DescriptorPool *p_pool); - RID_Owner<Buffer, true> uniform_buffer_owner; - RID_Owner<Buffer, true> storage_buffer_owner; + RID_Owner<Buffer> uniform_buffer_owner; + RID_Owner<Buffer> storage_buffer_owner; // Texture buffer needs a view. struct TextureBuffer { @@ -722,7 +722,7 @@ class RenderingDeviceVulkan : public RenderingDevice { VkBufferView view = VK_NULL_HANDLE; }; - RID_Owner<TextureBuffer, true> texture_buffer_owner; + RID_Owner<TextureBuffer> texture_buffer_owner; // This structure contains the descriptor set. They _need_ to be allocated // for a shader (and will be erased when this shader is erased), but should @@ -752,7 +752,7 @@ class RenderingDeviceVulkan : public RenderingDevice { void *invalidated_callback_userdata = nullptr; }; - RID_Owner<UniformSet, true> uniform_set_owner; + RID_Owner<UniformSet> uniform_set_owner; /*******************/ /**** PIPELINES ****/ @@ -791,7 +791,7 @@ class RenderingDeviceVulkan : public RenderingDevice { uint32_t push_constant_stages_mask = 0; }; - RID_Owner<RenderPipeline, true> render_pipeline_owner; + RID_Owner<RenderPipeline> render_pipeline_owner; struct PipelineCacheHeader { uint32_t magic; @@ -830,7 +830,7 @@ class RenderingDeviceVulkan : public RenderingDevice { uint32_t local_group_size[3] = { 0, 0, 0 }; }; - RID_Owner<ComputePipeline, true> compute_pipeline_owner; + RID_Owner<ComputePipeline> compute_pipeline_owner; /*******************/ /**** DRAW LIST ****/ @@ -931,7 +931,7 @@ class RenderingDeviceVulkan : public RenderingDevice { void _draw_list_insert_clear_region(DrawList *p_draw_list, Framebuffer *p_framebuffer, Point2i p_viewport_offset, Point2i p_viewport_size, bool p_clear_color, const Vector<Color> &p_clear_colors, bool p_clear_depth, float p_depth, uint32_t p_stencil); Error _draw_list_setup_framebuffer(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, VkFramebuffer *r_framebuffer, VkRenderPass *r_render_pass, uint32_t *r_subpass_count); - Error _draw_list_render_pass_begin(Framebuffer *framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_colors, float p_clear_depth, uint32_t p_clear_stencil, Point2i viewport_offset, Point2i viewport_size, VkFramebuffer vkframebuffer, VkRenderPass render_pass, VkCommandBuffer command_buffer, VkSubpassContents subpass_contents, const Vector<RID> &p_storage_textures); + Error _draw_list_render_pass_begin(Framebuffer *framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_colors, float p_clear_depth, uint32_t p_clear_stencil, Point2i viewport_offset, Point2i viewport_size, VkFramebuffer vkframebuffer, VkRenderPass render_pass, VkCommandBuffer command_buffer, VkSubpassContents subpass_contents, const Vector<RID> &p_storage_textures, bool p_constrained_to_region); _FORCE_INLINE_ DrawList *_get_draw_list_ptr(DrawListID p_id); Buffer *_get_buffer_from_owner(RID p_buffer, VkPipelineStageFlags &dst_stage_mask, VkAccessFlags &dst_access, BitField<BarrierMask> p_post_barrier); Error _draw_list_allocate(const Rect2i &p_viewport, uint32_t p_splits, uint32_t p_subpass); @@ -1015,8 +1015,13 @@ class RenderingDeviceVulkan : public RenderingDevice { List<ComputePipeline> compute_pipelines_to_dispose_of; VkCommandPool command_pool = VK_NULL_HANDLE; - VkCommandBuffer setup_command_buffer = VK_NULL_HANDLE; // Used at the beginning of every frame for set-up. - VkCommandBuffer draw_command_buffer = VK_NULL_HANDLE; // Used at the beginning of every frame for set-up. + // Used for filling up newly created buffers with data provided on creation. + // Primarily intended to be accessed by worker threads. + // Ideally this cmd buffer should use an async transfer queue. + VkCommandBuffer setup_command_buffer = VK_NULL_HANDLE; + // The main cmd buffer for drawing and compute. + // Primarily intended to be used by the main thread to do most stuff. + VkCommandBuffer draw_command_buffer = VK_NULL_HANDLE; struct Timestamp { String description; diff --git a/editor/editor_about.cpp b/editor/editor_about.cpp index f6360db569..672697bab0 100644 --- a/editor/editor_about.cpp +++ b/editor/editor_about.cpp @@ -39,29 +39,25 @@ // The metadata key used to store and retrieve the version text to copy to the clipboard. const String EditorAbout::META_TEXT_TO_COPY = "text_to_copy"; -void EditorAbout::_theme_changed() { - const Ref<Font> font = get_theme_font(SNAME("source"), EditorStringName(EditorFonts)); - const int font_size = get_theme_font_size(SNAME("source_size"), EditorStringName(EditorFonts)); - - _tpl_text->begin_bulk_theme_override(); - _tpl_text->add_theme_font_override("normal_font", font); - _tpl_text->add_theme_font_size_override("normal_font_size", font_size); - _tpl_text->add_theme_constant_override("line_separation", 4 * EDSCALE); - _tpl_text->end_bulk_theme_override(); - - _license_text->begin_bulk_theme_override(); - _license_text->add_theme_font_override("normal_font", font); - _license_text->add_theme_font_size_override("normal_font_size", font_size); - _license_text->add_theme_constant_override("line_separation", 4 * EDSCALE); - _license_text->end_bulk_theme_override(); - - _logo->set_texture(get_editor_theme_icon(SNAME("Logo"))); -} - void EditorAbout::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: { - _theme_changed(); + case NOTIFICATION_THEME_CHANGED: { + const Ref<Font> font = get_theme_font(SNAME("source"), EditorStringName(EditorFonts)); + const int font_size = get_theme_font_size(SNAME("source_size"), EditorStringName(EditorFonts)); + + _tpl_text->begin_bulk_theme_override(); + _tpl_text->add_theme_font_override("normal_font", font); + _tpl_text->add_theme_font_size_override("normal_font_size", font_size); + _tpl_text->add_theme_constant_override("line_separation", 4 * EDSCALE); + _tpl_text->end_bulk_theme_override(); + + _license_text->begin_bulk_theme_override(); + _license_text->add_theme_font_override("normal_font", font); + _license_text->add_theme_font_size_override("normal_font_size", font_size); + _license_text->add_theme_constant_override("line_separation", 4 * EDSCALE); + _license_text->end_bulk_theme_override(); + + _logo->set_texture(get_editor_theme_icon(SNAME("Logo"))); } break; } } @@ -128,12 +124,12 @@ EditorAbout::EditorAbout() { set_hide_on_ok(true); VBoxContainer *vbc = memnew(VBoxContainer); - vbc->connect("theme_changed", callable_mp(this, &EditorAbout::_theme_changed)); + add_child(vbc); + HBoxContainer *hbc = memnew(HBoxContainer); hbc->set_h_size_flags(Control::SIZE_EXPAND_FILL); hbc->set_alignment(BoxContainer::ALIGNMENT_CENTER); hbc->add_theme_constant_override("separation", 30 * EDSCALE); - add_child(vbc); vbc->add_child(hbc); _logo = memnew(TextureRect); diff --git a/editor/editor_about.h b/editor/editor_about.h index 0bc863e3c1..22b5f3ded0 100644 --- a/editor/editor_about.h +++ b/editor/editor_about.h @@ -64,8 +64,6 @@ private: RichTextLabel *_tpl_text = nullptr; TextureRect *_logo = nullptr; - void _theme_changed(); - protected: void _notification(int p_what); static void _bind_methods(); diff --git a/editor/editor_autoload_settings.cpp b/editor/editor_autoload_settings.cpp index be05bfea68..204b717296 100644 --- a/editor/editor_autoload_settings.cpp +++ b/editor/editor_autoload_settings.cpp @@ -407,31 +407,30 @@ Node *EditorAutoloadSettings::_create_autoload(const String &p_path) { scn.instantiate(); scn->set_path(p_path); scn->reload_from_file(); - ERR_FAIL_COND_V_MSG(!scn.is_valid(), nullptr, vformat("Can't autoload: %s.", p_path)); + ERR_FAIL_COND_V_MSG(!scn.is_valid(), nullptr, vformat("Failed to create an autoload, can't load from path: %s.", p_path)); if (scn.is_valid()) { n = scn->instantiate(); } } else { Ref<Resource> res = ResourceLoader::load(p_path); - ERR_FAIL_COND_V_MSG(res.is_null(), nullptr, vformat("Can't autoload: %s.", p_path)); + ERR_FAIL_COND_V_MSG(res.is_null(), nullptr, vformat("Failed to create an autoload, can't load from path: %s.", p_path)); Ref<Script> scr = res; if (scr.is_valid()) { StringName ibt = scr->get_instance_base_type(); bool valid_type = ClassDB::is_parent_class(ibt, "Node"); - ERR_FAIL_COND_V_MSG(!valid_type, nullptr, vformat("Script does not inherit from Node: %s.", p_path)); + ERR_FAIL_COND_V_MSG(!valid_type, nullptr, vformat("Failed to create an autoload, script '%s' does not inherit from 'Node'.", p_path)); Object *obj = ClassDB::instantiate(ibt); - - ERR_FAIL_NULL_V_MSG(obj, nullptr, vformat("Cannot instance script for Autoload, expected 'Node' inheritance, got: %s.", ibt)); + ERR_FAIL_NULL_V_MSG(obj, nullptr, vformat("Failed to create an autoload, cannot instantiate '%s'.", ibt)); n = Object::cast_to<Node>(obj); n->set_script(scr); } } - ERR_FAIL_NULL_V_MSG(n, nullptr, vformat("Path in Autoload not a node or script: %s.", p_path)); + ERR_FAIL_NULL_V_MSG(n, nullptr, vformat("Failed to create an autoload, path is not pointing to a scene or a script: %s.", p_path)); return n; } diff --git a/editor/editor_command_palette.cpp b/editor/editor_command_palette.cpp index da970980eb..6626251ee6 100644 --- a/editor/editor_command_palette.cpp +++ b/editor/editor_command_palette.cpp @@ -129,8 +129,8 @@ void EditorCommandPalette::_update_command_search(const String &search_text) { section->set_text(0, item_name); section->set_selectable(0, false); section->set_selectable(1, false); - section->set_custom_bg_color(0, search_options->get_theme_color(SNAME("prop_subsection"), EditorStringName(Editor))); - section->set_custom_bg_color(1, search_options->get_theme_color(SNAME("prop_subsection"), EditorStringName(Editor))); + section->set_custom_bg_color(0, get_theme_color(SNAME("prop_subsection"), EditorStringName(Editor))); + section->set_custom_bg_color(1, get_theme_color(SNAME("prop_subsection"), EditorStringName(Editor))); sections[section_name] = section; } @@ -164,6 +164,10 @@ void EditorCommandPalette::_notification(int p_what) { was_showed = true; } } break; + + case NOTIFICATION_THEME_CHANGED: { + command_search_box->set_right_icon(get_editor_theme_icon(SNAME("Search"))); + } break; } } @@ -303,10 +307,6 @@ Ref<Shortcut> EditorCommandPalette::add_shortcut_command(const String &p_command return p_shortcut; } -void EditorCommandPalette::_theme_changed() { - command_search_box->set_right_icon(search_options->get_editor_theme_icon(SNAME("Search"))); -} - void EditorCommandPalette::_save_history() const { Dictionary command_history; @@ -330,7 +330,6 @@ EditorCommandPalette::EditorCommandPalette() { connect("confirmed", callable_mp(this, &EditorCommandPalette::_confirmed)); VBoxContainer *vbc = memnew(VBoxContainer); - vbc->connect("theme_changed", callable_mp(this, &EditorCommandPalette::_theme_changed)); add_child(vbc); command_search_box = memnew(LineEdit); diff --git a/editor/editor_command_palette.h b/editor/editor_command_palette.h index d2f25f6ec5..01fa6896a9 100644 --- a/editor/editor_command_palette.h +++ b/editor/editor_command_palette.h @@ -83,10 +83,9 @@ class EditorCommandPalette : public ConfirmationDialog { float _score_path(const String &p_search, const String &p_path); void _sbox_input(const Ref<InputEvent> &p_ie); void _confirmed(); - void _update_command_keys(); void _add_command(String p_command_name, String p_key_name, Callable p_binded_action, String p_shortcut_text = "None"); - void _theme_changed(); void _save_history() const; + EditorCommandPalette(); protected: diff --git a/editor/editor_fonts.cpp b/editor/editor_fonts.cpp index 6c51be8361..1e1dff61b9 100644 --- a/editor/editor_fonts.cpp +++ b/editor/editor_fonts.cpp @@ -107,7 +107,7 @@ Ref<FontVariation> make_bold_font(const Ref<Font> &p_font, double p_embolden, Ty } void editor_register_fonts(Ref<Theme> p_theme) { - OS::get_singleton()->benchmark_begin_measure("editor_register_fonts"); + OS::get_singleton()->benchmark_begin_measure("EditorTheme", "Register Fonts"); Ref<DirAccess> dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); TextServer::FontAntialiasing font_antialiasing = (TextServer::FontAntialiasing)(int)EDITOR_GET("interface/editor/font_antialiasing"); @@ -445,5 +445,5 @@ void editor_register_fonts(Ref<Theme> p_theme) { p_theme->set_font_size("status_source_size", EditorStringName(EditorFonts), default_font_size); p_theme->set_font("status_source", EditorStringName(EditorFonts), mono_other_fc); - OS::get_singleton()->benchmark_end_measure("editor_register_fonts"); + OS::get_singleton()->benchmark_end_measure("EditorTheme", "Register Fonts"); } diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index 10ab733c2b..2b1ab4ca02 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -2360,6 +2360,7 @@ void EditorHelp::_add_text(const String &p_bbcode) { _add_text_to_rt(p_bbcode, class_desc, this, edited_class); } +int EditorHelp::doc_generation_count = 0; String EditorHelp::doc_version_hash; Thread EditorHelp::worker_thread; @@ -2392,6 +2393,8 @@ void EditorHelp::_load_doc_thread(void *p_udata) { // We have to go back to the main thread to start from scratch, bypassing any possibly existing cache. callable_mp_static(&EditorHelp::generate_doc).bind(false).call_deferred(); } + + OS::get_singleton()->benchmark_end_measure("EditorHelp", vformat("Generate Documentation (Run %d)", doc_generation_count)); } void EditorHelp::_gen_doc_thread(void *p_udata) { @@ -2417,6 +2420,8 @@ void EditorHelp::_gen_doc_thread(void *p_udata) { if (err) { ERR_PRINT("Cannot save editor help cache (" + get_cache_full_path() + ")."); } + + OS::get_singleton()->benchmark_end_measure("EditorHelp", vformat("Generate Documentation (Run %d)", doc_generation_count)); } void EditorHelp::_gen_extensions_docs() { @@ -2424,7 +2429,8 @@ void EditorHelp::_gen_extensions_docs() { } void EditorHelp::generate_doc(bool p_use_cache) { - OS::get_singleton()->benchmark_begin_measure("EditorHelp::generate_doc"); + doc_generation_count++; + OS::get_singleton()->benchmark_begin_measure("EditorHelp", vformat("Generate Documentation (Run %d)", doc_generation_count)); // In case not the first attempt. _wait_for_thread(); @@ -2444,8 +2450,6 @@ void EditorHelp::generate_doc(bool p_use_cache) { doc->generate(); worker_thread.start(_gen_doc_thread, nullptr); } - - OS::get_singleton()->benchmark_end_measure("EditorHelp::generate_doc"); } void EditorHelp::_toggle_scripts_pressed() { diff --git a/editor/editor_help.h b/editor/editor_help.h index d2d05a8603..ff440a679a 100644 --- a/editor/editor_help.h +++ b/editor/editor_help.h @@ -187,6 +187,7 @@ class EditorHelp : public VBoxContainer { String _fix_constant(const String &p_constant) const; void _toggle_scripts_pressed(); + static int doc_generation_count; static String doc_version_hash; static Thread worker_thread; diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index b2a65063a7..79cc059900 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -627,7 +627,7 @@ void EditorNode::_notification(int p_what) { if (requested_first_scan) { requested_first_scan = false; - OS::get_singleton()->benchmark_begin_measure("editor_scan_and_import"); + OS::get_singleton()->benchmark_begin_measure("Editor", "First Scan"); if (run_surface_upgrade_tool) { run_surface_upgrade_tool = false; @@ -974,6 +974,9 @@ void EditorNode::_fs_changed() { } else { // Normal project export. String config_error; bool missing_templates; + if (export_defer.android_build_template) { + export_template_manager->install_android_template(); + } if (!platform->can_export(export_preset, config_error, missing_templates, export_defer.debug)) { ERR_PRINT(vformat("Cannot export project with preset \"%s\" due to configuration errors:\n%s", preset_name, config_error)); err = missing_templates ? ERR_FILE_NOT_FOUND : ERR_UNCONFIGURED; @@ -1041,7 +1044,7 @@ void EditorNode::_sources_changed(bool p_exist) { if (waiting_for_first_scan) { waiting_for_first_scan = false; - OS::get_singleton()->benchmark_end_measure("editor_scan_and_import"); + OS::get_singleton()->benchmark_end_measure("Editor", "First Scan"); // Reload the global shader variables, but this time // loading textures, as they are now properly imported. @@ -1050,11 +1053,12 @@ void EditorNode::_sources_changed(bool p_exist) { _load_editor_layout(); if (!defer_load_scene.is_empty()) { - OS::get_singleton()->benchmark_begin_measure("editor_load_scene"); + OS::get_singleton()->benchmark_begin_measure("Editor", "Load Scene"); + load_scene(defer_load_scene); defer_load_scene = ""; - OS::get_singleton()->benchmark_end_measure("editor_load_scene"); + OS::get_singleton()->benchmark_end_measure("Editor", "Load Scene"); OS::get_singleton()->benchmark_dump(); } @@ -3459,13 +3463,13 @@ void EditorNode::set_addon_plugin_enabled(const String &p_addon, bool p_enabled, } // Plugin init scripts must inherit from EditorPlugin and be tools. - if (String(scr->get_instance_base_type()) != "EditorPlugin") { - show_warning(vformat(TTR("Unable to load addon script from path: '%s' Base type is not EditorPlugin."), script_path)); + if (!ClassDB::is_parent_class(scr->get_instance_base_type(), "EditorPlugin")) { + show_warning(vformat(TTR("Unable to load addon script from path: '%s'. Base type is not 'EditorPlugin'."), script_path)); return; } if (!scr->is_tool()) { - show_warning(vformat(TTR("Unable to load addon script from path: '%s' Script is not in tool mode."), script_path)); + show_warning(vformat(TTR("Unable to load addon script from path: '%s'. Script is not in tool mode."), script_path)); return; } } @@ -4665,7 +4669,7 @@ void EditorNode::_begin_first_scan() { // In headless mode, scan right away. // This allows users to continue using `godot --headless --editor --quit` to prepare a project. if (!DisplayServer::get_singleton()->window_can_draw()) { - OS::get_singleton()->benchmark_begin_measure("editor_scan_and_import"); + OS::get_singleton()->benchmark_begin_measure("Editor", "First Scan"); EditorFileSystem::get_singleton()->scan(); return; } @@ -4676,11 +4680,12 @@ void EditorNode::_begin_first_scan() { requested_first_scan = true; } -Error EditorNode::export_preset(const String &p_preset, const String &p_path, bool p_debug, bool p_pack_only) { +Error EditorNode::export_preset(const String &p_preset, const String &p_path, bool p_debug, bool p_pack_only, bool p_android_build_template) { export_defer.preset = p_preset; export_defer.path = p_path; export_defer.debug = p_debug; export_defer.pack_only = p_pack_only; + export_defer.android_build_template = p_android_build_template; cmdline_export_mode = true; return OK; } @@ -4728,14 +4733,15 @@ void EditorNode::_dock_floating_close_request(WindowWrapper *p_wrapper) { // Give back the dock to the original owner. Control *dock = p_wrapper->release_wrapped_control(); + int target_index = MIN(dock_slot_index, dock_slot[dock_slot_num]->get_tab_count()); dock_slot[dock_slot_num]->add_child(dock); - dock_slot[dock_slot_num]->move_child(dock, MIN(dock_slot_index, dock_slot[dock_slot_num]->get_tab_count())); - dock_slot[dock_slot_num]->set_current_tab(dock_slot_index); + dock_slot[dock_slot_num]->move_child(dock, target_index); + dock_slot[dock_slot_num]->set_current_tab(target_index); floating_docks.erase(p_wrapper); p_wrapper->queue_free(); - _update_dock_containers(); + _update_dock_slots_visibility(true); _edit_current(); } @@ -4779,38 +4785,13 @@ void EditorNode::_dock_make_float(Control *p_dock, int p_slot_index, bool p_show wrapper->restore_window(Rect2i(dock_screen_pos, dock_size), get_window()->get_current_screen()); } - _update_dock_containers(); + _update_dock_slots_visibility(true); floating_docks.push_back(wrapper); _edit_current(); } -void EditorNode::_update_dock_containers() { - for (int i = 0; i < DOCK_SLOT_MAX; i++) { - if (dock_slot[i]->get_tab_count() == 0 && dock_slot[i]->is_visible()) { - dock_slot[i]->hide(); - } - if (dock_slot[i]->get_tab_count() > 0 && !dock_slot[i]->is_visible()) { - dock_slot[i]->show(); - } - } - for (int i = 0; i < vsplits.size(); i++) { - bool in_use = dock_slot[i * 2 + 0]->get_tab_count() || dock_slot[i * 2 + 1]->get_tab_count(); - if (in_use) { - vsplits[i]->show(); - } else { - vsplits[i]->hide(); - } - } - - if (right_l_vsplit->is_visible() || right_r_vsplit->is_visible()) { - right_hsplit->show(); - } else { - right_hsplit->hide(); - } -} - void EditorNode::_dock_select_input(const Ref<InputEvent> &p_input) { Ref<InputEventMouse> me = p_input; @@ -4849,7 +4830,7 @@ void EditorNode::_dock_select_input(const Ref<InputEvent> &p_input) { dock_slot[nrect]->show(); dock_select->queue_redraw(); - _update_dock_containers(); + _update_dock_slots_visibility(true); _edit_current(); _save_editor_layout(); @@ -5139,83 +5120,44 @@ void EditorNode::_update_dock_slots_visibility(bool p_keep_selected_tabs) { right_hsplit->hide(); } else { for (int i = 0; i < DOCK_SLOT_MAX; i++) { - int tabs_visible = 0; + int first_tab_visible = -1; for (int j = 0; j < dock_slot[i]->get_tab_count(); j++) { if (!dock_slot[i]->is_tab_hidden(j)) { - tabs_visible++; + first_tab_visible = j; + break; } } - if (tabs_visible) { + if (first_tab_visible >= 0) { dock_slot[i]->show(); + if (p_keep_selected_tabs) { + int current_tab = dock_slot[i]->get_current_tab(); + if (dock_slot[i]->is_tab_hidden(current_tab)) { + dock_slot[i]->set_block_signals(true); + dock_slot[i]->select_next_available(); + dock_slot[i]->set_block_signals(false); + } + } else { + dock_slot[i]->set_block_signals(true); + dock_slot[i]->set_current_tab(first_tab_visible); + dock_slot[i]->set_block_signals(false); + } } else { dock_slot[i]->hide(); } } for (int i = 0; i < vsplits.size(); i++) { - bool in_use = dock_slot[i * 2 + 0]->get_tab_count() || dock_slot[i * 2 + 1]->get_tab_count(); - if (in_use) { - vsplits[i]->show(); - } else { - vsplits[i]->hide(); - } - } - - if (!p_keep_selected_tabs) { - for (int i = 0; i < DOCK_SLOT_MAX; i++) { - if (dock_slot[i]->is_visible() && dock_slot[i]->get_tab_count()) { - dock_slot[i]->set_current_tab(0); - } - } + bool in_use = dock_slot[i * 2 + 0]->is_visible() || dock_slot[i * 2 + 1]->is_visible(); + vsplits[i]->set_visible(in_use); } - if (right_l_vsplit->is_visible() || right_r_vsplit->is_visible()) { - right_hsplit->show(); - } else { - right_hsplit->hide(); - } + right_hsplit->set_visible(right_l_vsplit->is_visible() || right_r_vsplit->is_visible()); } } void EditorNode::_dock_tab_changed(int p_tab) { // Update visibility but don't set current tab. - - if (!docks_visible) { - for (int i = 0; i < DOCK_SLOT_MAX; i++) { - dock_slot[i]->hide(); - } - - for (int i = 0; i < vsplits.size(); i++) { - vsplits[i]->hide(); - } - - right_hsplit->hide(); - bottom_panel->hide(); - } else { - for (int i = 0; i < DOCK_SLOT_MAX; i++) { - if (dock_slot[i]->get_tab_count()) { - dock_slot[i]->show(); - } else { - dock_slot[i]->hide(); - } - } - - for (int i = 0; i < vsplits.size(); i++) { - bool in_use = dock_slot[i * 2 + 0]->get_tab_count() || dock_slot[i * 2 + 1]->get_tab_count(); - if (in_use) { - vsplits[i]->show(); - } else { - vsplits[i]->hide(); - } - } - bottom_panel->show(); - - if (right_l_vsplit->is_visible() || right_r_vsplit->is_visible()) { - right_hsplit->show(); - } else { - right_hsplit->hide(); - } - } + _update_dock_slots_visibility(true); } void EditorNode::_restore_floating_dock(const Dictionary &p_dock_dump, Control *p_dock, int p_slot_index) { @@ -5284,20 +5226,14 @@ void EditorNode::_load_docks_from_config(Ref<ConfigFile> p_layout, const String if (atidx == i) { dock_slot[i]->move_child(node, 0); } else if (atidx != -1) { - dock_slot[atidx]->remove_child(node); - - if (dock_slot[atidx]->get_tab_count() == 0) { - dock_slot[atidx]->hide(); - } - dock_slot[i]->add_child(node); - dock_slot[i]->move_child(node, 0); - dock_slot[i]->set_tab_title(0, TTRGET(node->get_name())); - dock_slot[i]->show(); + dock_slot[i]->move_tab_from_tab_container(dock_slot[atidx], dock_slot[atidx]->get_tab_idx_from_control(node), 0); } WindowWrapper *wrapper = Object::cast_to<WindowWrapper>(node); if (restore_window_on_load && floating_docks_dump.has(name)) { - _restore_floating_dock(floating_docks_dump[name], node, i); + if (!dock_slot[i]->is_tab_hidden(dock_slot[i]->get_tab_idx_from_control(node))) { + _restore_floating_dock(floating_docks_dump[name], node, i); + } } else if (wrapper) { wrapper->set_window_enabled(false); } @@ -5330,26 +5266,7 @@ void EditorNode::_load_docks_from_config(Ref<ConfigFile> p_layout, const String hsplits[i]->set_split_offset(ofs); } - for (int i = 0; i < vsplits.size(); i++) { - bool in_use = dock_slot[i * 2 + 0]->get_tab_count() || dock_slot[i * 2 + 1]->get_tab_count(); - if (in_use) { - vsplits[i]->show(); - } else { - vsplits[i]->hide(); - } - } - - if (right_l_vsplit->is_visible() || right_r_vsplit->is_visible()) { - right_hsplit->show(); - } else { - right_hsplit->hide(); - } - - for (int i = 0; i < DOCK_SLOT_MAX; i++) { - if (dock_slot[i]->is_visible() && dock_slot[i]->get_tab_count()) { - dock_slot[i]->set_current_tab(0); - } - } + _update_dock_slots_visibility(false); // FileSystemDock. @@ -6234,8 +6151,7 @@ void EditorNode::reload_instances_with_path_in_edited_scenes(const String &p_ins if (edited_scene_map.size() > 0) { // Reload the new instance. Error err; - Ref<PackedScene> instance_scene_packed_scene = ResourceLoader::load(p_instance_path, "", ResourceFormatLoader::CACHE_MODE_IGNORE, &err); - instance_scene_packed_scene->set_path(p_instance_path, true); + Ref<PackedScene> instance_scene_packed_scene = ResourceLoader::load(p_instance_path, "", ResourceFormatLoader::CACHE_MODE_REPLACE, &err); ERR_FAIL_COND(err != OK); ERR_FAIL_COND(instance_scene_packed_scene.is_null()); @@ -6342,8 +6258,7 @@ void EditorNode::reload_instances_with_path_in_edited_scenes(const String &p_ins // be properly updated. for (String path : required_load_paths) { if (!local_scene_cache.find(path)) { - current_packed_scene = ResourceLoader::load(path, "", ResourceFormatLoader::CACHE_MODE_IGNORE, &err); - current_packed_scene->set_path(path, true); + current_packed_scene = ResourceLoader::load(path, "", ResourceFormatLoader::CACHE_MODE_REPLACE, &err); local_scene_cache[path] = current_packed_scene; } else { current_packed_scene = local_scene_cache[path]; @@ -6676,6 +6591,10 @@ void EditorNode::_resource_loaded(Ref<Resource> p_resource, const String &p_path void EditorNode::_feature_profile_changed() { Ref<EditorFeatureProfile> profile = feature_profile_manager->get_current_profile(); + // FIXME: Close all floating docks to avoid crash. + for (WindowWrapper *wrapper : floating_docks) { + wrapper->set_window_enabled(false); + } TabContainer *import_tabs = cast_to<TabContainer>(ImportDock::get_singleton()->get_parent()); TabContainer *node_tabs = cast_to<TabContainer>(NodeDock::get_singleton()->get_parent()); TabContainer *fs_tabs = cast_to<TabContainer>(FileSystemDock::get_singleton()->get_parent()); diff --git a/editor/editor_node.h b/editor/editor_node.h index a50ea5c69e..917feed4ee 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -264,6 +264,7 @@ private: String path; bool debug = false; bool pack_only = false; + bool android_build_template = false; } export_defer; static EditorNode *singleton; @@ -631,8 +632,6 @@ private: bool _find_scene_in_use(Node *p_node, const String &p_path) const; - void _update_dock_containers(); - void _dock_select_input(const Ref<InputEvent> &p_input); void _dock_move_left(); void _dock_move_right(); @@ -882,7 +881,7 @@ public: void _copy_warning(const String &p_str); - Error export_preset(const String &p_preset, const String &p_path, bool p_debug, bool p_pack_only); + Error export_preset(const String &p_preset, const String &p_path, bool p_debug, bool p_pack_only, bool p_android_build_template); bool is_project_exporting() const; Control *get_gui_base() { return gui_base; } diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 2d35d4e9ac..f891bfbff7 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -56,6 +56,7 @@ #include "scene/resources/font.h" #include "scene/resources/mesh.h" #include "scene/resources/packed_scene.h" +#include "scene/resources/visual_shader_nodes.h" ///////////////////// Nil ///////////////////////// @@ -2929,6 +2930,14 @@ bool EditorPropertyNodePath::is_drop_valid(const Dictionary &p_drag_data) const if (dropped_node->is_class(E) || EditorNode::get_singleton()->is_object_of_custom_type(dropped_node, E)) { return true; + } else { + Ref<Script> dropped_node_script = dropped_node->get_script(); + while (dropped_node_script.is_valid()) { + if (dropped_node_script->get_path() == E) { + return true; + } + dropped_node_script = dropped_node_script->get_base_script(); + } } } @@ -3196,6 +3205,13 @@ void EditorPropertyResource::_resource_changed(const Ref<Resource> &p_resource) Ref<ViewportTexture> vpt = p_resource; if (vpt.is_valid()) { r = Object::cast_to<Resource>(get_edited_object()); + if (Object::cast_to<VisualShaderNodeTexture>(r)) { + EditorNode::get_singleton()->show_warning(TTR("Can't create a ViewportTexture in a Texture2D node because the texture will not be bound to a scene.\nUse a Texture2DParameter node instead and set the texture in the \"Shader Parameters\" tab.")); + emit_changed(get_edited_property(), Ref<Resource>()); + update_property(); + return; + } + if (r && r->get_path().is_resource_file()) { EditorNode::get_singleton()->show_warning(TTR("Can't create a ViewportTexture on resources saved as a file.\nResource needs to belong to a scene.")); emit_changed(get_edited_property(), Ref<Resource>()); diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp index 3fad85c95c..0a9d35fe64 100644 --- a/editor/editor_properties_array_dict.cpp +++ b/editor/editor_properties_array_dict.cpp @@ -198,6 +198,10 @@ void EditorPropertyArray::_property_changed(const String &p_property, Variant p_ return; } + if (p_value.get_type() == Variant::OBJECT && p_value.is_null()) { + p_value = Variant(); // `EditorResourcePicker` resets to `Ref<Resource>()`. See GH-82716. + } + int index; if (p_property.begins_with("metadata/")) { index = p_property.get_slice("/", 2).to_int(); @@ -726,6 +730,10 @@ EditorPropertyArray::EditorPropertyArray() { ///////////////////// DICTIONARY /////////////////////////// void EditorPropertyDictionary::_property_changed(const String &p_property, Variant p_value, const String &p_name, bool p_changing) { + if (p_value.get_type() == Variant::OBJECT && p_value.is_null()) { + p_value = Variant(); // `EditorResourcePicker` resets to `Ref<Resource>()`. See GH-82716. + } + if (p_property == "new_item_key") { object->set_new_item_key(p_value); } else if (p_property == "new_item_value") { diff --git a/editor/editor_quick_open.cpp b/editor/editor_quick_open.cpp index 82313c12e2..965d0269d6 100644 --- a/editor/editor_quick_open.cpp +++ b/editor/editor_quick_open.cpp @@ -250,23 +250,22 @@ void EditorQuickOpen::_notification(int p_what) { } } break; + case NOTIFICATION_THEME_CHANGED: { + search_box->set_right_icon(get_editor_theme_icon(SNAME("Search"))); + } break; + case NOTIFICATION_EXIT_TREE: { disconnect("confirmed", callable_mp(this, &EditorQuickOpen::_confirmed)); } break; } } -void EditorQuickOpen::_theme_changed() { - search_box->set_right_icon(search_options->get_editor_theme_icon(SNAME("Search"))); -} - void EditorQuickOpen::_bind_methods() { ADD_SIGNAL(MethodInfo("quick_open")); } EditorQuickOpen::EditorQuickOpen() { VBoxContainer *vbc = memnew(VBoxContainer); - vbc->connect("theme_changed", callable_mp(this, &EditorQuickOpen::_theme_changed)); add_child(vbc); search_box = memnew(LineEdit); diff --git a/editor/editor_quick_open.h b/editor/editor_quick_open.h index 2b64efb151..ec8ce0175e 100644 --- a/editor/editor_quick_open.h +++ b/editor/editor_quick_open.h @@ -72,8 +72,6 @@ class EditorQuickOpen : public ConfirmationDialog { void _sbox_input(const Ref<InputEvent> &p_ie); void _text_changed(const String &p_newtext); - void _theme_changed(); - protected: void _notification(int p_what); static void _bind_methods(); diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index 0dd787f0ea..96d2abf202 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -403,7 +403,9 @@ float get_gizmo_handle_scale(const String &gizmo_handle_name = "") { } void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme, float p_icon_saturation, int p_thumb_size, bool p_only_thumbs = false) { - OS::get_singleton()->benchmark_begin_measure("editor_register_and_generate_icons_" + String((p_only_thumbs ? "with_only_thumbs" : "all"))); + const String benchmark_key = vformat("Generate Icons (%s)", (p_only_thumbs ? "Only Thumbs" : "All")); + OS::get_singleton()->benchmark_begin_measure("EditorTheme", benchmark_key); + // Before we register the icons, we adjust their colors and saturation. // Most icons follow the standard rules for color conversion to follow the editor // theme's polarity (dark/light). We also adjust the saturation for most icons, @@ -531,11 +533,11 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme, f p_theme->set_icon(editor_icons_names[index], EditorStringName(EditorIcons), icon); } } - OS::get_singleton()->benchmark_end_measure("editor_register_and_generate_icons_" + String((p_only_thumbs ? "with_only_thumbs" : "all"))); + OS::get_singleton()->benchmark_end_measure("EditorTheme", benchmark_key); } Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { - OS::get_singleton()->benchmark_begin_measure("create_editor_theme"); + OS::get_singleton()->benchmark_begin_measure("EditorTheme", "Create Editor Theme"); Ref<EditorTheme> theme = memnew(EditorTheme); // Controls may rely on the scale for their internal drawing logic. @@ -2360,15 +2362,16 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { theme->set_color("search_result_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/search_result_color")); theme->set_color("search_result_border_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/search_result_border_color")); - OS::get_singleton()->benchmark_end_measure("create_editor_theme"); + OS::get_singleton()->benchmark_end_measure("EditorTheme", "Create Editor Theme"); return theme; } Ref<Theme> create_custom_theme(const Ref<Theme> p_theme) { - OS::get_singleton()->benchmark_begin_measure("create_custom_theme"); Ref<Theme> theme = create_editor_theme(p_theme); + OS::get_singleton()->benchmark_begin_measure("EditorTheme", "Create Custom Theme"); + const String custom_theme_path = EDITOR_GET("interface/theme/custom_theme"); if (!custom_theme_path.is_empty()) { Ref<Theme> custom_theme = ResourceLoader::load(custom_theme_path); @@ -2377,7 +2380,7 @@ Ref<Theme> create_custom_theme(const Ref<Theme> p_theme) { } } - OS::get_singleton()->benchmark_end_measure("create_custom_theme"); + OS::get_singleton()->benchmark_end_measure("EditorTheme", "Create Custom Theme"); return theme; } diff --git a/editor/export/editor_export_platform.cpp b/editor/export/editor_export_platform.cpp index c492589e63..ab2ada9ae4 100644 --- a/editor/export/editor_export_platform.cpp +++ b/editor/export/editor_export_platform.cpp @@ -93,7 +93,7 @@ bool EditorExportPlatform::fill_log_messages(RichTextLabel *p_log, Error p_err) } p_log->add_newline(); - if (msg_count) { + if (msg_count > 0) { p_log->push_table(2); p_log->set_table_column_expand(0, false); p_log->set_table_column_expand(1, true); @@ -133,8 +133,37 @@ bool EditorExportPlatform::fill_log_messages(RichTextLabel *p_log, Error p_err) } p_log->pop(); p_log->add_newline(); + } else if (p_err != OK) { + // We failed but don't show any user-facing messages. This is bad and should not + // be allowed, but just in case this happens, let's give the user something at least. + p_log->push_table(2); + p_log->set_table_column_expand(0, false); + p_log->set_table_column_expand(1, true); + + { + Color color = p_log->get_theme_color(SNAME("error_color"), EditorStringName(Editor)); + Ref<Texture> icon = p_log->get_editor_theme_icon(SNAME("Error")); + + p_log->push_cell(); + p_log->add_text("\t"); + if (icon.is_valid()) { + p_log->add_image(icon); + } + p_log->pop(); + + p_log->push_cell(); + p_log->push_color(color); + p_log->add_text(vformat("[%s]: %s", TTR("Unknown Error"), vformat(TTR("Export failed with error code %d."), p_err))); + p_log->pop(); + p_log->pop(); + } + + p_log->pop(); + p_log->add_newline(); } + p_log->add_newline(); + return has_messages; } @@ -1431,7 +1460,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()); @@ -1475,7 +1504,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()) { diff --git a/editor/export/editor_export_platform_pc.cpp b/editor/export/editor_export_platform_pc.cpp index ec34ffd1df..42df0d93f6 100644 --- a/editor/export/editor_export_platform_pc.cpp +++ b/editor/export/editor_export_platform_pc.cpp @@ -161,6 +161,7 @@ Error EditorExportPlatformPC::prepare_template(const Ref<EditorExportPreset> &p_ } if (err != OK) { add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Template"), TTR("Failed to copy export template.")); + return err; } return err; diff --git a/editor/export/project_export.cpp b/editor/export/project_export.cpp index 719c3114f4..f2c5eeb2ed 100644 --- a/editor/export/project_export.cpp +++ b/editor/export/project_export.cpp @@ -90,11 +90,6 @@ ProjectExportTextureFormatError::ProjectExportTextureFormatError() { fix_texture_format_button->connect("pressed", callable_mp(this, &ProjectExportTextureFormatError::_on_fix_texture_format_pressed)); } -void ProjectExportDialog::_theme_changed() { - duplicate_preset->set_icon(presets->get_editor_theme_icon(SNAME("Duplicate"))); - delete_preset->set_icon(presets->get_editor_theme_icon(SNAME("Remove"))); -} - void ProjectExportDialog::_notification(int p_what) { switch (p_what) { case NOTIFICATION_VISIBILITY_CHANGED: { @@ -103,6 +98,11 @@ void ProjectExportDialog::_notification(int p_what) { } } break; + case NOTIFICATION_THEME_CHANGED: { + duplicate_preset->set_icon(presets->get_editor_theme_icon(SNAME("Duplicate"))); + delete_preset->set_icon(presets->get_editor_theme_icon(SNAME("Remove"))); + } break; + case NOTIFICATION_READY: { duplicate_preset->set_icon(presets->get_editor_theme_icon(SNAME("Duplicate"))); delete_preset->set_icon(presets->get_editor_theme_icon(SNAME("Remove"))); @@ -1109,37 +1109,40 @@ void ProjectExportDialog::_export_all_dialog_action(const String &p_str) { } void ProjectExportDialog::_export_all(bool p_debug) { - String export_target = p_debug ? TTR("Debug") : TTR("Release"); - EditorProgress ep("exportall", TTR("Exporting All") + " " + export_target, EditorExport::get_singleton()->get_export_preset_count(), true); - exporting = true; - bool show_dialog = false; - result_dialog_log->clear(); - for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) { - Ref<EditorExportPreset> preset = EditorExport::get_singleton()->get_export_preset(i); - if (preset.is_null()) { - exporting = false; - ERR_FAIL_MSG("Failed to start the export: one of the presets is invalid."); - } - Ref<EditorExportPlatform> platform = preset->get_platform(); - if (platform.is_null()) { - exporting = false; - ERR_FAIL_MSG("Failed to start the export: one of the presets has no valid platform."); - } + { // Scope for the editor progress, we must free it before showing the dialog at the end. + String export_target = p_debug ? TTR("Debug") : TTR("Release"); + EditorProgress ep("exportall", TTR("Exporting All") + " " + export_target, EditorExport::get_singleton()->get_export_preset_count(), true); - ep.step(preset->get_name(), i); + result_dialog_log->clear(); + for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) { + Ref<EditorExportPreset> preset = EditorExport::get_singleton()->get_export_preset(i); + if (preset.is_null()) { + exporting = false; + ERR_FAIL_MSG("Failed to start the export: one of the presets is invalid."); + } - platform->clear_messages(); - Error err = platform->export_project(preset, p_debug, preset->get_export_path(), 0); - if (err == ERR_SKIP) { - exporting = false; - return; + Ref<EditorExportPlatform> platform = preset->get_platform(); + if (platform.is_null()) { + exporting = false; + ERR_FAIL_MSG("Failed to start the export: one of the presets has no valid platform."); + } + + ep.step(preset->get_name(), i); + + platform->clear_messages(); + Error err = platform->export_project(preset, p_debug, preset->get_export_path(), 0); + if (err == ERR_SKIP) { + exporting = false; + return; + } + bool has_messages = platform->fill_log_messages(result_dialog_log, err); + show_dialog = show_dialog || has_messages; } - bool has_messages = platform->fill_log_messages(result_dialog_log, err); - show_dialog = show_dialog || has_messages; } + if (show_dialog) { result_dialog->popup_centered_ratio(0.5); } @@ -1148,7 +1151,6 @@ void ProjectExportDialog::_export_all(bool p_debug) { } void ProjectExportDialog::_bind_methods() { - ClassDB::bind_method("_export_all", &ProjectExportDialog::_export_all); ClassDB::bind_method("set_export_path", &ProjectExportDialog::set_export_path); ClassDB::bind_method("get_export_path", &ProjectExportDialog::get_export_path); ClassDB::bind_method("get_current_preset", &ProjectExportDialog::get_current_preset); @@ -1161,8 +1163,8 @@ ProjectExportDialog::ProjectExportDialog() { set_clamp_to_embedder(true); VBoxContainer *main_vb = memnew(VBoxContainer); - main_vb->connect("theme_changed", callable_mp(this, &ProjectExportDialog::_theme_changed)); add_child(main_vb); + HSplitContainer *hbox = memnew(HSplitContainer); main_vb->add_child(hbox); hbox->set_v_size_flags(Control::SIZE_EXPAND_FILL); diff --git a/editor/export/project_export.h b/editor/export/project_export.h index 30f6812acb..1a359b08da 100644 --- a/editor/export/project_export.h +++ b/editor/export/project_export.h @@ -188,7 +188,6 @@ class ProjectExportDialog : public ConfirmationDialog { void _tab_changed(int); protected: - void _theme_changed(); void _notification(int p_what); static void _bind_methods(); diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index 944da47242..a47bbd321d 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -1595,6 +1595,9 @@ void FileSystemDock::_update_dependencies_after_move(const HashMap<String, Strin // The following code assumes that the following holds: // 1) EditorFileSystem contains the old paths/folder structure from before the rename/move. // 2) ResourceLoader can use the new paths without needing to call rescan. + + // The currently edited scene should be reloaded first, so get it's path (GH-82652). + const String &edited_scene_path = EditorNode::get_editor_data().get_scene_path(EditorNode::get_editor_data().get_edited_scene()); List<String> scenes_to_reload; for (const String &E : p_file_owners) { // Because we haven't called a rescan yet the found remap might still be an old path itself. @@ -1604,7 +1607,11 @@ void FileSystemDock::_update_dependencies_after_move(const HashMap<String, Strin const Error err = ResourceLoader::rename_dependencies(file, p_renames); if (err == OK) { if (ResourceLoader::get_resource_type(file) == "PackedScene") { - scenes_to_reload.push_back(file); + if (file == edited_scene_path) { + scenes_to_reload.push_front(file); + } else { + scenes_to_reload.push_back(file); + } } } else { EditorNode::get_singleton()->add_io_error(TTR("Unable to update dependencies for:") + "\n" + E + "\n"); diff --git a/editor/gui/scene_tree_editor.cpp b/editor/gui/scene_tree_editor.cpp index 056e4f8d9d..ba4fa994b7 100644 --- a/editor/gui/scene_tree_editor.cpp +++ b/editor/gui/scene_tree_editor.cpp @@ -50,7 +50,7 @@ #include "scene/main/window.h" #include "scene/resources/packed_scene.h" -Node *SceneTreeEditor::get_scene_node() { +Node *SceneTreeEditor::get_scene_node() const { ERR_FAIL_COND_V(!is_inside_tree(), nullptr); return get_tree()->get_edited_scene_root(); @@ -498,6 +498,18 @@ void SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) { EditorNode::get_singleton()->is_object_of_custom_type(p_node, E)) { valid = true; break; + } else { + Ref<Script> node_script = p_node->get_script(); + while (node_script.is_valid()) { + if (node_script->get_path() == E) { + valid = true; + break; + } + node_script = node_script->get_base_script(); + } + if (valid) { + break; + } } } @@ -656,6 +668,18 @@ bool SceneTreeEditor::_update_filter(TreeItem *p_parent, bool p_scroll_to_select EditorNode::get_singleton()->is_object_of_custom_type(n, E)) { selectable = true; break; + } else { + Ref<Script> node_script = n->get_script(); + while (node_script.is_valid()) { + if (node_script->get_path() == E) { + selectable = true; + break; + } + node_script = node_script->get_base_script(); + } + if (selectable) { + break; + } } } } @@ -1218,11 +1242,8 @@ Variant SceneTreeEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from Node *n = get_node(np); if (n) { - // Only allow selection if not part of an instantiated scene. - if (!n->get_owner() || n->get_owner() == get_scene_node() || n->get_owner()->get_scene_file_path().is_empty()) { - selected_nodes.push_back(n); - icons.push_back(next->get_icon(0)); - } + selected_nodes.push_back(n); + icons.push_back(next->get_icon(0)); } next = tree->get_next_selected(next); } @@ -1336,7 +1357,21 @@ bool SceneTreeEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_d } } - return String(d["type"]) == "nodes" && filter.is_empty(); + if (filter.is_empty() && String(d["type"]) == "nodes") { + Array nodes = d["nodes"]; + + for (int i = 0; i < nodes.size(); i++) { + Node *n = get_node(nodes[i]); + // Nodes from an instantiated scene can't be rearranged. + if (n && n->get_owner() && n->get_owner() != get_scene_node() && !n->get_owner()->get_scene_file_path().is_empty()) { + return false; + } + } + + return true; + } + + return false; } void SceneTreeEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) { @@ -1548,16 +1583,29 @@ void SceneTreeDialog::set_valid_types(const Vector<StringName> &p_valid) { HBoxContainer *hb = memnew(HBoxContainer); hflow->add_child(hb); + // Attempt to get the correct name and icon for script path types. + String name = type; + Ref<Texture2D> icon = EditorNode::get_singleton()->get_class_icon(type); + + // If we can't find a global class icon, try to find one for the script. + if (icon.is_null() && ResourceLoader::exists(type, "Script")) { + Ref<Script> node_script = ResourceLoader::load(type); + if (node_script.is_valid()) { + name = name.get_file(); + icon = EditorNode::get_singleton()->get_object_icon(node_script.ptr()); + } + } + TextureRect *trect = memnew(TextureRect); hb->add_child(trect); trect->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE); trect->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED); - trect->set_meta("type", type); + trect->set_meta("icon", icon); valid_type_icons.push_back(trect); Label *label = memnew(Label); hb->add_child(label); - label->set_text(type); + label->set_text(name); label->set_auto_translate(false); } @@ -1583,7 +1631,7 @@ void SceneTreeDialog::_notification(int p_what) { filter->set_right_icon(get_editor_theme_icon(SNAME("Search"))); for (TextureRect *trect : valid_type_icons) { trect->set_custom_minimum_size(Vector2(get_theme_constant(SNAME("class_icon_size"), EditorStringName(Editor)), 0)); - trect->set_texture(EditorNode::get_singleton()->get_class_icon(trect->get_meta("type"))); + trect->set_texture(trect->get_meta("icon")); } } break; diff --git a/editor/gui/scene_tree_editor.h b/editor/gui/scene_tree_editor.h index 0df0c3a1c3..96d6d065f6 100644 --- a/editor/gui/scene_tree_editor.h +++ b/editor/gui/scene_tree_editor.h @@ -120,7 +120,7 @@ class SceneTreeEditor : public Control { void _set_item_custom_color(TreeItem *p_item, Color p_color); void _selection_changed(); - Node *get_scene_node(); + Node *get_scene_node() const; Variant get_drag_data_fw(const Point2 &p_point, Control *p_from); bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const; diff --git a/editor/icons/SnapGrid.svg b/editor/icons/SnapGrid.svg index feb4206e81..070a842a4e 100644 --- a/editor/icons/SnapGrid.svg +++ b/editor/icons/SnapGrid.svg @@ -1 +1 @@ -<svg height="16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 0v2H0v1h2v4H0v1h2v4H0v1h2v2h1v-2h3v-1H3V8h4l1-1V3h4v3h1V3h2V2h-2V0h-1v2H8V0H7v2H3V0zm1 3h4v4H3zm4 10v2h2v-2zm6 0v2h2v-2z" fill="#def"/><path d="M7 13h2v-2a2 2 0 0 1 4 0v2h2v-2a4 4 0 0 0-8 0z" fill="#def" fill-opacity=".8"/></svg> +<svg height="16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 0v2H0v1h2v4H0v1h2v4H0v1h2v2h1v-2h3v-1H3V8h4l1-1V3h4v3h1V3h2V2h-2V0h-1v2H8V0H7v2H3V0zm1 3h4v4H3zm4 10v2h2v-2zm6 0v2h2v-2z" fill="#e0e0e0"/><path d="M7 13h2v-2a2 2 0 0 1 4 0v2h2v-2a4 4 0 0 0-8 0z" fill="#e0e0e0" fill-opacity=".8"/></svg> diff --git a/editor/icons/Texture2D.svg b/editor/icons/Texture2D.svg new file mode 100644 index 0000000000..635547ba03 --- /dev/null +++ b/editor/icons/Texture2D.svg @@ -0,0 +1 @@ +<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill="#808080"><path d="M2 1a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zm1 2h10v8H3z"/><path d="M3.5 3.5h9v7h-9" fill-opacity=".2"/></g></svg> diff --git a/editor/icons/Texture3D.svg b/editor/icons/Texture3D.svg new file mode 100644 index 0000000000..208fa41b61 --- /dev/null +++ b/editor/icons/Texture3D.svg @@ -0,0 +1 @@ +<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill="gray"><path d="M1 14a1 1 0 0 0 1 1h9.5a1 1 0 0 0 .707-.293l2.5-2.5A1 1 0 0 0 15 11.5V2a1 1 0 0 0-1-1H4.5a1 1 0 0 0-.707.293l-2.5 2.5A1 1 0 0 0 1 4.5zm1.25-9H11v7H2.25zm10 6.25v-6.5L14 3v6.5zm-1-7.5H3L4.75 2H13z"/><path d="M2.75 5.5h7.75v6H2.75" fill-opacity=".2"/></g></svg> diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp index c895c2f6c5..a06ed69cd0 100644 --- a/editor/import/resource_importer_scene.cpp +++ b/editor/import/resource_importer_scene.cpp @@ -1505,6 +1505,7 @@ void ResourceImporterScene::_create_slices(AnimationPlayer *ap, Ref<Animation> a new_anim->add_track(anim->track_get_type(j)); dtrack = new_anim->get_track_count() - 1; new_anim->track_set_path(dtrack, anim->track_get_path(j)); + new_anim->track_set_imported(dtrack, true); if (kt > (from + 0.01) && k > 0) { if (anim->track_get_type(j) == Animation::TYPE_POSITION_3D) { @@ -1580,6 +1581,7 @@ void ResourceImporterScene::_create_slices(AnimationPlayer *ap, Ref<Animation> a new_anim->add_track(anim->track_get_type(j)); dtrack = new_anim->get_track_count() - 1; new_anim->track_set_path(dtrack, anim->track_get_path(j)); + new_anim->track_set_imported(dtrack, true); if (anim->track_get_type(j) == Animation::TYPE_POSITION_3D) { Vector3 p; anim->try_position_track_interpolate(j, from, &p); diff --git a/editor/import/resource_importer_wav.cpp b/editor/import/resource_importer_wav.cpp index f3cb5bda32..c97b6a7579 100644 --- a/editor/import/resource_importer_wav.cpp +++ b/editor/import/resource_importer_wav.cpp @@ -292,7 +292,9 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s loop_end = file->get_32(); } } - file->seek(file_pos + chunksize); + // Move to the start of the next chunk. Note that RIFF requires a padding byte for odd + // chunk sizes. + file->seek(file_pos + chunksize + (chunksize & 1)); } // STEP 2, APPLY CONVERSIONS diff --git a/editor/input_event_configuration_dialog.cpp b/editor/input_event_configuration_dialog.cpp index 724ad9370b..a0d2662045 100644 --- a/editor/input_event_configuration_dialog.cpp +++ b/editor/input_event_configuration_dialog.cpp @@ -553,9 +553,8 @@ void InputEventConfigurationDialog::_notification(int p_what) { event_listener->grab_focus(); } break; - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { - input_list_search->set_right_icon(input_list_search->get_editor_theme_icon(SNAME("Search"))); + input_list_search->set_right_icon(get_editor_theme_icon(SNAME("Search"))); key_mode->set_item_icon(KEYMODE_KEYCODE, get_editor_theme_icon(SNAME("Keyboard"))); key_mode->set_item_icon(KEYMODE_PHY_KEYCODE, get_editor_theme_icon(SNAME("KeyboardPhysical"))); @@ -613,12 +612,13 @@ InputEventConfigurationDialog::InputEventConfigurationDialog() { allowed_input_types = INPUT_KEY | INPUT_MOUSE_BUTTON | INPUT_JOY_BUTTON | INPUT_JOY_MOTION; set_title(TTR("Event Configuration")); - set_min_size(Size2i(550 * EDSCALE, 0)); // Min width + set_min_size(Size2i(550, 0) * EDSCALE); VBoxContainer *main_vbox = memnew(VBoxContainer); add_child(main_vbox); event_as_text = memnew(Label); + event_as_text->set_custom_minimum_size(Size2(500, 0) * EDSCALE); event_as_text->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART); event_as_text->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER); event_as_text->add_theme_font_size_override("font_size", 18 * EDSCALE); diff --git a/editor/node_dock.cpp b/editor/node_dock.cpp index 6521730473..680e77fe42 100644 --- a/editor/node_dock.cpp +++ b/editor/node_dock.cpp @@ -104,7 +104,7 @@ NodeDock::NodeDock() { mode_hb->hide(); connections_button = memnew(Button); - connections_button->set_flat(true); + connections_button->set_theme_type_variation("FlatButton"); connections_button->set_text(TTR("Signals")); connections_button->set_toggle_mode(true); connections_button->set_pressed(true); @@ -114,7 +114,7 @@ NodeDock::NodeDock() { connections_button->connect("pressed", callable_mp(this, &NodeDock::show_connections)); groups_button = memnew(Button); - groups_button->set_flat(true); + groups_button->set_theme_type_variation("FlatButton"); groups_button->set_text(TTR("Groups")); groups_button->set_toggle_mode(true); groups_button->set_pressed(false); diff --git a/editor/plugins/gpu_particles_3d_editor_plugin.cpp b/editor/plugins/gpu_particles_3d_editor_plugin.cpp index e47af62b5b..88c9df7103 100644 --- a/editor/plugins/gpu_particles_3d_editor_plugin.cpp +++ b/editor/plugins/gpu_particles_3d_editor_plugin.cpp @@ -223,6 +223,9 @@ GPUParticles3DEditorBase::GPUParticles3DEditorBase() { emission_dialog->connect("confirmed", callable_mp(this, &GPUParticles3DEditorBase::_generate_emission_points)); emission_tree_dialog = memnew(SceneTreeDialog); + Vector<StringName> valid_types; + valid_types.push_back("MeshInstance3D"); + emission_tree_dialog->set_valid_types(valid_types); add_child(emission_tree_dialog); emission_tree_dialog->connect("selected", callable_mp(this, &GPUParticles3DEditorBase::_node_selected)); } diff --git a/editor/plugins/multimesh_editor_plugin.cpp b/editor/plugins/multimesh_editor_plugin.cpp index da1abc2af1..370c423b40 100644 --- a/editor/plugins/multimesh_editor_plugin.cpp +++ b/editor/plugins/multimesh_editor_plugin.cpp @@ -354,6 +354,9 @@ MultiMeshEditor::MultiMeshEditor() { populate_dialog->get_ok_button()->connect("pressed", callable_mp(this, &MultiMeshEditor::_populate)); std = memnew(SceneTreeDialog); + Vector<StringName> valid_types; + valid_types.push_back("MeshInstance3D"); + std->set_valid_types(valid_types); populate_dialog->add_child(std); std->connect("selected", callable_mp(this, &MultiMeshEditor::_browsed)); diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 8ab35d150e..ad7ef2b6ef 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -4152,6 +4152,7 @@ Node *Node3DEditorViewport::_sanitize_preview_node(Node *p_node) const { } void Node3DEditorViewport::_create_preview_node(const Vector<String> &files) const { + bool add_preview = false; for (int i = 0; i < files.size(); i++) { String path = files[i]; Ref<Resource> res = ResourceLoader::load(path); @@ -4172,9 +4173,13 @@ void Node3DEditorViewport::_create_preview_node(const Vector<String> &files) con } } } - EditorNode::get_singleton()->get_scene_root()->add_child(preview_node); + add_preview = true; } } + if (add_preview) { + EditorNode::get_singleton()->get_scene_root()->add_child(preview_node); + } + *preview_bounds = _calculate_spatial_bounds(preview_node); } diff --git a/editor/plugins/path_3d_editor_plugin.cpp b/editor/plugins/path_3d_editor_plugin.cpp index 9d66e606b0..6fe1949382 100644 --- a/editor/plugins/path_3d_editor_plugin.cpp +++ b/editor/plugins/path_3d_editor_plugin.cpp @@ -303,9 +303,19 @@ void Path3DGizmo::redraw() { } const Transform3D *r = frames.ptr(); + Vector<Vector3> _collision_segments; + _collision_segments.resize((sample_count - 1) * 2); + Vector3 *_collisions_ptr = _collision_segments.ptrw(); + Vector<Vector3> bones; + bones.resize(sample_count * 4); + Vector3 *bones_ptr = bones.ptrw(); + Vector<Vector3> ribbon; + ribbon.resize(sample_count); + Vector3 *ribbon_ptr = ribbon.ptrw(); + for (int i = 0; i < sample_count; i++) { const Vector3 p1 = r[i].origin; const Vector3 side = r[i].basis.get_column(0); @@ -313,23 +323,25 @@ void Path3DGizmo::redraw() { const Vector3 forward = r[i].basis.get_column(2); // Collision segments. - if (i != sample_count) { + if (i != sample_count - 1) { const Vector3 p2 = r[i + 1].origin; - _collision_segments.push_back(p1); - _collision_segments.push_back(p2); + _collisions_ptr[(i * 2)] = p1; + _collisions_ptr[(i * 2) + 1] = p2; } // Path3D as a ribbon. - ribbon.push_back(p1); + ribbon_ptr[i] = p1; // Fish Bone. const Vector3 p_left = p1 + (side + forward - up * 0.3) * 0.06; const Vector3 p_right = p1 + (-side + forward - up * 0.3) * 0.06; - bones.push_back(p1); - bones.push_back(p_left); - bones.push_back(p1); - bones.push_back(p_right); + const int bone_idx = i * 4; + + bones_ptr[bone_idx] = p1; + bones_ptr[bone_idx + 1] = p_left; + bones_ptr[bone_idx + 2] = p1; + bones_ptr[bone_idx + 3] = p_right; } add_collision_segments(_collision_segments); @@ -713,6 +725,9 @@ void Path3DEditorPlugin::_notification(int p_what) { } break; case NOTIFICATION_READY: { + // FIXME: This can trigger theme updates when the nodes that we want to update are not yet available. + // The toolbar should be extracted to a dedicated control and theme updates should be handled through + // the notification. Node3DEditor::get_singleton()->connect("theme_changed", callable_mp(this, &Path3DEditorPlugin::_update_theme)); } break; } diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp index 8141d18341..f8e6c71a4c 100644 --- a/editor/plugins/polygon_2d_editor_plugin.cpp +++ b/editor/plugins/polygon_2d_editor_plugin.cpp @@ -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) { @@ -989,9 +990,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); @@ -1357,10 +1385,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); @@ -1503,8 +1540,6 @@ Polygon2DEditor::Polygon2DEditor() { error = memnew(AcceptDialog); add_child(error); - - uv_edit_draw->set_clip_contents(true); } Polygon2DEditorPlugin::Polygon2DEditorPlugin() : diff --git a/editor/plugins/polygon_2d_editor_plugin.h b/editor/plugins/polygon_2d_editor_plugin.h index 3547c03120..8c52984b59 100644 --- a/editor/plugins/polygon_2d_editor_plugin.h +++ b/editor/plugins/polygon_2d_editor_plugin.h @@ -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; diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index 9ed997dca7..04eda502d2 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -1650,7 +1650,7 @@ void ScriptTextEditor::reload(bool p_soft) { return; } scr->set_source_code(te->get_text()); - bool soft = p_soft || scr->get_instance_base_type() == "EditorPlugin"; // Always soft-reload editor plugins. + bool soft = p_soft || ClassDB::is_parent_class(scr->get_instance_base_type(), "EditorPlugin"); // Always soft-reload editor plugins. scr->get_language()->reload_tool_script(scr, soft); } diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp index 6bee3660cf..a6de74a1d2 100644 --- a/editor/plugins/tiles/tile_map_editor.cpp +++ b/editor/plugins/tiles/tile_map_editor.cpp @@ -2373,7 +2373,9 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() { // --- Bottom panel tiles --- tiles_bottom_panel = memnew(VBoxContainer); - tiles_bottom_panel->connect("tree_entered", callable_mp(this, &TileMapEditorTilesPlugin::_update_theme)); + // FIXME: This can trigger theme updates when the nodes that we want to update are not yet available. + // The toolbar should be extracted to a dedicated control and theme updates should be handled through + // the notification. tiles_bottom_panel->connect("theme_changed", callable_mp(this, &TileMapEditorTilesPlugin::_update_theme)); tiles_bottom_panel->connect("visibility_changed", callable_mp(this, &TileMapEditorTilesPlugin::_stop_dragging)); tiles_bottom_panel->connect("visibility_changed", callable_mp(this, &TileMapEditorTilesPlugin::_tab_changed)); @@ -3536,7 +3538,9 @@ void TileMapEditorTerrainsPlugin::edit(ObjectID p_tile_map_id, int p_tile_map_la TileMapEditorTerrainsPlugin::TileMapEditorTerrainsPlugin() { main_vbox_container = memnew(VBoxContainer); - main_vbox_container->connect("tree_entered", callable_mp(this, &TileMapEditorTerrainsPlugin::_update_theme)); + // FIXME: This can trigger theme updates when the nodes that we want to update are not yet available. + // The toolbar should be extracted to a dedicated control and theme updates should be handled through + // the notification. main_vbox_container->connect("theme_changed", callable_mp(this, &TileMapEditorTerrainsPlugin::_update_theme)); main_vbox_container->set_name(TTR("Terrains")); diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index ed01187947..291484600c 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -101,12 +101,6 @@ void ProjectDialog::_set_message(const String &p_msg, MessageType p_type, InputT } else if (current_install_icon != new_icon && input_type == INSTALL_PATH) { install_status_rect->set_texture(new_icon); } - - Size2i window_size = get_size(); - Size2 contents_min_size = get_contents_minimum_size(); - if (window_size.x < contents_min_size.x || window_size.y < contents_min_size.y) { - set_size(window_size.max(contents_min_size)); - } } String ProjectDialog::_test_path() { @@ -868,6 +862,7 @@ ProjectDialog::ProjectDialog() { msg = memnew(Label); msg->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER); + msg->set_custom_minimum_size(Size2(200, 0) * EDSCALE); vb->add_child(msg); // Renderer selection. diff --git a/editor/register_editor_types.cpp b/editor/register_editor_types.cpp index 9a667d5775..075c856c1c 100644 --- a/editor/register_editor_types.cpp +++ b/editor/register_editor_types.cpp @@ -129,7 +129,7 @@ #include "editor/register_exporters.h" void register_editor_types() { - OS::get_singleton()->benchmark_begin_measure("register_editor_types"); + OS::get_singleton()->benchmark_begin_measure("Editor", "Register Types"); ResourceLoader::set_timestamp_on_load(true); ResourceSaver::set_timestamp_on_save(true); @@ -282,11 +282,11 @@ void register_editor_types() { ei_singleton.editor_only = true; Engine::get_singleton()->add_singleton(ei_singleton); - OS::get_singleton()->benchmark_end_measure("register_editor_types"); + OS::get_singleton()->benchmark_end_measure("Editor", "Register Types"); } void unregister_editor_types() { - OS::get_singleton()->benchmark_begin_measure("unregister_editor_types"); + OS::get_singleton()->benchmark_begin_measure("Editor", "Unregister Types"); EditorNode::cleanup(); EditorInterface::free(); @@ -296,5 +296,5 @@ void unregister_editor_types() { } EditorStringNames::free(); - OS::get_singleton()->benchmark_end_measure("unregister_editor_types"); + OS::get_singleton()->benchmark_end_measure("Editor", "Unregister Types"); } diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 98c16f5c59..4e62ed4667 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -2179,6 +2179,7 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V } int child_pos = node->get_index(false); + bool reparented_to_container = Object::cast_to<Container>(new_parent) && Object::cast_to<Control>(node); undo_redo->add_undo_method(node->get_parent(), "add_child", node, true); undo_redo->add_undo_method(node->get_parent(), "move_child", node, child_pos); @@ -2194,10 +2195,14 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V if (Object::cast_to<Node3D>(node)) { undo_redo->add_undo_method(node, "set_transform", Object::cast_to<Node3D>(node)->get_transform()); } - if (Object::cast_to<Control>(node)) { + if (!reparented_to_container && Object::cast_to<Control>(node)) { undo_redo->add_undo_method(node, "set_position", Object::cast_to<Control>(node)->get_position()); } } + + if (reparented_to_container) { + undo_redo->add_undo_method(node, "_edit_set_state", Object::cast_to<Control>(node)->_edit_get_state()); + } } perform_node_renames(nullptr, &path_renames); @@ -4034,21 +4039,21 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec vbc->add_child(button_hb); edit_remote = memnew(Button); - edit_remote->set_flat(true); - button_hb->add_child(edit_remote); + edit_remote->set_theme_type_variation("FlatButton"); edit_remote->set_h_size_flags(SIZE_EXPAND_FILL); edit_remote->set_text(TTR("Remote")); edit_remote->set_toggle_mode(true); edit_remote->set_tooltip_text(TTR("If selected, the Remote scene tree dock will cause the project to stutter every time it updates.\nSwitch back to the Local scene tree dock to improve performance.")); + button_hb->add_child(edit_remote); edit_remote->connect("pressed", callable_mp(this, &SceneTreeDock::_remote_tree_selected)); edit_local = memnew(Button); - edit_local->set_flat(true); - button_hb->add_child(edit_local); + edit_local->set_theme_type_variation("FlatButton"); edit_local->set_h_size_flags(SIZE_EXPAND_FILL); edit_local->set_text(TTR("Local")); edit_local->set_toggle_mode(true); edit_local->set_pressed(true); + button_hb->add_child(edit_local); edit_local->connect("pressed", callable_mp(this, &SceneTreeDock::_local_tree_selected)); remote_tree = nullptr; diff --git a/editor/shader_create_dialog.cpp b/editor/shader_create_dialog.cpp index 28776cbddc..fb91d139a7 100644 --- a/editor/shader_create_dialog.cpp +++ b/editor/shader_create_dialog.cpp @@ -48,8 +48,6 @@ enum ShaderType { void ShaderCreateDialog::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { - _update_theme(); - String last_lang = EditorSettings::get_singleton()->get_project_metadata("shader_setup", "last_selected_language", ""); if (!last_lang.is_empty()) { for (int i = 0; i < type_menu->get_item_count(); i++) { @@ -68,28 +66,17 @@ void ShaderCreateDialog::_notification(int p_what) { } break; case NOTIFICATION_THEME_CHANGED: { - _update_theme(); - } break; - } -} - -void ShaderCreateDialog::_update_theme() { - Ref<Texture2D> shader_icon = gc->get_editor_theme_icon(SNAME("Shader")); - if (shader_icon.is_valid()) { - type_menu->set_item_icon(0, shader_icon); - } - - Ref<Texture2D> visual_shader_icon = gc->get_editor_theme_icon(SNAME("VisualShader")); - if (visual_shader_icon.is_valid()) { - type_menu->set_item_icon(1, visual_shader_icon); - } + static const char *shader_types[3] = { "Shader", "VisualShader", "TextFile" }; + for (int i = 0; i < 3; i++) { + Ref<Texture2D> icon = get_editor_theme_icon(shader_types[i]); + if (icon.is_valid()) { + type_menu->set_item_icon(i, icon); + } + } - Ref<Texture2D> include_icon = gc->get_editor_theme_icon(SNAME("TextFile")); - if (include_icon.is_valid()) { - type_menu->set_item_icon(2, include_icon); + path_button->set_icon(get_editor_theme_icon(SNAME("Folder"))); + } break; } - - path_button->set_icon(get_editor_theme_icon(SNAME("Folder"))); } void ShaderCreateDialog::_update_language_info() { @@ -175,9 +162,10 @@ void fragment() { // Called for every pixel the material is visible on. } -void light() { +//void light() { // Called for every pixel for every light affecting the material. -} + // Uncomment to replace the default light processing function with this one. +//} )"; break; case Shader::MODE_CANVAS_ITEM: @@ -190,9 +178,10 @@ void fragment() { // Called for every pixel the material is visible on. } -void light() { +//void light() { // Called for every pixel for every light affecting the CanvasItem. -} + // Uncomment to replace the default light processing function with this one. +//} )"; break; case Shader::MODE_PARTICLES: diff --git a/editor/shader_create_dialog.h b/editor/shader_create_dialog.h index d6d9f10020..5240842110 100644 --- a/editor/shader_create_dialog.h +++ b/editor/shader_create_dialog.h @@ -101,7 +101,6 @@ class ShaderCreateDialog : public ConfirmationDialog { void _update_dialog(); protected: - void _update_theme(); void _notification(int p_what); static void _bind_methods(); diff --git a/editor/translations/editor/ar.po b/editor/translations/editor/ar.po index dfde71e44e..dfea8dffdf 100644 --- a/editor/translations/editor/ar.po +++ b/editor/translations/editor/ar.po @@ -3458,17 +3458,6 @@ msgstr "" "تعطيل الإضافة في '%s' لتجنب الأخطاء لاحقاً." msgid "" -"Unable to load addon script from path: '%s' Base type is not EditorPlugin." -msgstr "" -"غير قادر علي تحميل النص البرمجي الإضافي من المسار: '%s' النوع الأساسي ليس " -"إضافة المُعدل." - -msgid "Unable to load addon script from path: '%s' Script is not in tool mode." -msgstr "" -"غير قادر علي تحميل النص البرمجي الإضافي من المسار: '%s' النص البرمجي ليس في " -"وضع الأداة." - -msgid "" "Scene '%s' was automatically imported, so it can't be modified.\n" "To make changes to it, a new inherited scene can be created." msgstr "" diff --git a/editor/translations/editor/bg.po b/editor/translations/editor/bg.po index 1ec4b77c84..8b1972675d 100644 --- a/editor/translations/editor/bg.po +++ b/editor/translations/editor/bg.po @@ -1679,17 +1679,6 @@ msgstr "" "Добавката „%s“ ще бъде изключена, за да се предотвратят последващи проблеми." msgid "" -"Unable to load addon script from path: '%s' Base type is not EditorPlugin." -msgstr "" -"Не може да се зареди добавката-скрипт от: „%s“. Базовият тип не е " -"„EditorPlugin“." - -msgid "Unable to load addon script from path: '%s' Script is not in tool mode." -msgstr "" -"Не може да се зареди добавката-скрипт от: „%s“. Скриптът не е в режим на " -"„инструмент“." - -msgid "" "Scene '%s' was automatically imported, so it can't be modified.\n" "To make changes to it, a new inherited scene can be created." msgstr "" diff --git a/editor/translations/editor/ca.po b/editor/translations/editor/ca.po index 80b3779866..7f665d811d 100644 --- a/editor/translations/editor/ca.po +++ b/editor/translations/editor/ca.po @@ -2308,17 +2308,6 @@ msgstr "" "Es desactivarà el complement (addon) a '% s' per a evitar més errors." msgid "" -"Unable to load addon script from path: '%s' Base type is not EditorPlugin." -msgstr "" -"No es pot carregar l'Script del complement: El tipus base de '%s' no és pas " -"EditorPlugin." - -msgid "Unable to load addon script from path: '%s' Script is not in tool mode." -msgstr "" -"No s'ha carregat l'Script d'addon des del camí: L'Script '%s' no és en el " -"mode d'Eina." - -msgid "" "Scene '%s' was automatically imported, so it can't be modified.\n" "To make changes to it, a new inherited scene can be created." msgstr "" diff --git a/editor/translations/editor/cs.po b/editor/translations/editor/cs.po index 7a82bceae1..4e239229e5 100644 --- a/editor/translations/editor/cs.po +++ b/editor/translations/editor/cs.po @@ -46,8 +46,8 @@ msgstr "" "Project-Id-Version: Godot Engine editor interface\n" "Report-Msgid-Bugs-To: https://github.com/godotengine/godot\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2023-09-25 08:36+0000\n" -"Last-Translator: Marek Douběta <mrocklon420@gmail.com>\n" +"PO-Revision-Date: 2023-12-07 07:04+0000\n" +"Last-Translator: Vojtěch Šamla <auzkok@seznam.cz>\n" "Language-Team: Czech <https://hosted.weblate.org/projects/godot-engine/godot/" "cs/>\n" "Language: cs\n" @@ -55,7 +55,10 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" -"X-Generator: Weblate 5.1-dev\n" +"X-Generator: Weblate 5.3-dev\n" + +msgid "Main Thread" +msgstr "Hlavní vlákno" msgid "Unset" msgstr "Nenastaveno" @@ -563,6 +566,9 @@ msgstr "Změnit cestu stopy" msgid "Toggle this track on/off." msgstr "Aktivovat/Deaktivovat tuto stopu." +msgid "Use Blend" +msgstr "Použít prolnutí" + msgid "Update Mode (How this property is set)" msgstr "Režim aktualizace (jak je tato vlastnost nastavena)" @@ -756,6 +762,9 @@ msgid "Select an AnimationPlayer node to create and edit animations." msgstr "" "Pro přidání a úpravu animací vyberte ze stromu scény uzel AnimationPlayer." +msgid "Imported Scene" +msgstr "Importovaná scéna" + msgid "Warning: Editing imported animation" msgstr "Upozornění: Upravuje se importovaná animace" @@ -813,6 +822,15 @@ msgstr "Přejít k předchozímu kroku" msgid "Apply Reset" msgstr "Resetovat" +msgid "Bake Animation" +msgstr "Zapéct animaci" + +msgid "Optimize Animation (no undo)" +msgstr "Optimalizovat animaci (nelze vrátit zpět)" + +msgid "Clean-Up Animation (no undo)" +msgstr "Pročistit animaci (nelze vrátit zpět)" + msgid "Use Bezier Curves" msgstr "Použít Bézierovy křivky" @@ -835,7 +853,7 @@ msgid "Clean-up all animations" msgstr "Pročistit všechny animace" msgid "Clean-Up Animation(s) (NO UNDO!)" -msgstr "Pročistit animaci (NELZE VZÍT ZPĚT!)" +msgstr "Pročistit animace (NELZE VRÁTIT ZPĚT!)" msgid "Clean-Up" msgstr "Pročistit" @@ -893,6 +911,9 @@ msgstr "Číslo řádku:" msgid "%d replaced." msgstr "%d nahrazeno." +msgid "No match" +msgstr "Žádná shoda" + msgid "%d match" msgid_plural "%d matches" msgstr[0] "%d shoda" @@ -901,9 +922,9 @@ msgstr[2] "%d shod" msgid "%d of %d match" msgid_plural "%d of %d matches" -msgstr[0] "%d shoda" -msgstr[1] "%d shody" -msgstr[2] "%d shod" +msgstr[0] "%d z %d shody" +msgstr[1] "%d ze %d shod" +msgstr[2] "%d z %d shod" msgid "Match Case" msgstr "Rozlišovat malá/velká" @@ -1180,6 +1201,12 @@ msgstr "Čas" msgid "Calls" msgstr "Volání" +msgid "CPU" +msgstr "CPU" + +msgid "GPU" +msgstr "GPU" + msgid "Bytes:" msgstr "Bajtů:" @@ -1189,12 +1216,30 @@ msgstr "Varování:" msgid "Error:" msgstr "Chyba:" +msgid "%s Error" +msgstr "Chyba %s" + msgid "%s Error:" msgstr "Chyba %s:" +msgid "%s Source" +msgstr "Zdroj %s" + +msgid "%s Source:" +msgstr "Zdroj %s:" + msgid "Stack Trace" msgstr "Trasování zásobníku" +msgid "Stack Trace:" +msgstr "Trasování zásobníku:" + +msgid "Debug session started." +msgstr "Ladící sezení zahájeno." + +msgid "Debug session closed." +msgstr "Ladící sezení ukončeno." + msgid "Line %d" msgstr "Řádek %d" @@ -1234,6 +1279,9 @@ msgstr "Přerušit" msgid "Continue" msgstr "Pokračovat" +msgid "Thread:" +msgstr "Vlákno:" + msgid "Stack Frames" msgstr "Rámce zásobníku" @@ -1384,7 +1432,7 @@ msgid "Errors loading!" msgstr "Chyby při načítání!" msgid "Permanently delete %d item(s)? (No undo!)" -msgstr "Permanentně smazat %d položek? (nelze vrátit zpět!)" +msgstr "Permanentně smazat %d položek? (Nelze vrátit zpět!)" msgid "Show Dependencies" msgstr "Zobrazit závislosti" @@ -1491,6 +1539,12 @@ msgstr "Balíček \"%s\" byl úspěšně nainstalován!" msgid "Success!" msgstr "Úspěch!" +msgid "No files conflict with your project" +msgstr "Žádné soubory nejsou v konfliktu s vaším projektem" + +msgid "Installation preview:" +msgstr "Náhled instalace:" + msgid "Install" msgstr "Instalovat" @@ -1665,6 +1719,15 @@ msgstr "Název uzlu:" msgid "Global Variable" msgstr "Globální proměnná" +msgid "3D Engine" +msgstr "3D Engine" + +msgid "2D Physics" +msgstr "2D Fyzika" + +msgid "3D Physics" +msgstr "3D Fyzika" + msgid "Navigation" msgstr "Navigace" @@ -1674,6 +1737,9 @@ msgstr "OpenGL" msgid "Vulkan" msgstr "Vulkan" +msgid "Navigation, both 2D and 3D." +msgstr "Navigace, jak 2D, tak i 3D." + msgid "File saving failed." msgstr "Ukládání souboru selhalo." @@ -1692,6 +1758,9 @@ msgstr "Nový" msgid "Save" msgstr "Uložit" +msgid "Profile:" +msgstr "Profil:" + msgid "Reset to Defaults" msgstr "Obnovit výchozí" @@ -1704,6 +1773,13 @@ msgstr "Načíst profil" msgid "Export Profile" msgstr "Exportovat profil" +msgid "" +"Failed to execute command \"%s\":\n" +"%s." +msgstr "" +"Nepodařilo se vykonat příkaz \"%s\":\n" +"%s." + msgid "Filter Commands" msgstr "Filtrovat příkazy" @@ -1880,12 +1956,21 @@ msgstr "(Re)Importování assetů" msgid "Import resources of type: %s" msgstr "Importovat zdroje typu: %s" +msgid "No return value." +msgstr "Žádná návratová hodnota." + msgid "Experimental" msgstr "Experimentální" msgid "Error codes returned:" msgstr "Vráceny chybové kódy:" +msgid "There is currently no description for this method." +msgstr "Momentálně zde není žádný popis této metody." + +msgid "There is currently no description for this constructor." +msgstr "Momentálně zde není žádný popis tohoto konstruktoru." + msgid "Top" msgstr "Horní" @@ -1911,6 +1996,9 @@ msgstr "Online návody" msgid "Properties" msgstr "Vlastnosti" +msgid "overrides %s:" +msgstr "přepisuje %s:" + msgid "default:" msgstr "výchozí:" @@ -1969,12 +2057,24 @@ msgstr "Popisy metod" msgid "Operator Descriptions" msgstr "Popisy operátorů" +msgid "Metadata:" +msgstr "Metadata:" + msgid "Property:" msgstr "Vlastnost:" +msgid "Method:" +msgstr "Metoda:" + msgid "Signal:" msgstr "Signál:" +msgid "Theme Item:" +msgstr "Položka motivu:" + +msgid "No description available." +msgstr "Není dostupný popis." + msgid "%d match." msgstr "%d shoda." @@ -1999,6 +2099,9 @@ msgstr "Pouze třídy" msgid "Methods Only" msgstr "Pouze metody" +msgid "Operators Only" +msgstr "Pouze operátory" + msgid "Signals Only" msgstr "Pouze signály" @@ -2045,21 +2148,45 @@ msgstr[0] "(%d změna)" msgstr[1] "(%d změny)" msgstr[2] "(%d změn)" +msgid "Add element to property array with prefix %s." +msgstr "Přidat prvek do pole vlastností s prefixem %s." + +msgid "Remove element %d from property array with prefix %s." +msgstr "Odstranit prvek %d z pole vlastností s prefixem %s." + +msgid "Move element %d to position %d in property array with prefix %s." +msgstr "Přesunout prvek %d na pozici %d v poli vlastností s prefixem %s." + +msgid "Element %d: %s%d*" +msgstr "Prvek %d: %s%d*" + msgid "Move Up" msgstr "Přesunout nahoru" msgid "Move Down" msgstr "Přesunout dolů" +msgid "Resize Array..." +msgstr "Změnit velikost pole..." + +msgid "Add Element" +msgstr "Přidat prvek" + msgid "Resize Array" msgstr "Změnit velikost pole" +msgid "Element %s" +msgstr "Prvek %s" + msgid "Add Metadata" msgstr "Přidat metadata" msgid "Set %s" msgstr "Nastav %s" +msgid "Set Multiple: %s" +msgstr "Nastavit více: %s" + msgid "Remove metadata %s" msgstr "Odstranit metadata %s" @@ -2078,6 +2205,15 @@ msgstr "Název metadat nemůže být prázdný." msgid "Name:" msgstr "Jméno:" +msgid "Add Metadata Property for \"%s\"" +msgstr "Přidat vlastnost metadat pro \"%s\"" + +msgid "Copy Value" +msgstr "Zkopírovat hodnotu" + +msgid "Paste Value" +msgstr "Vložit hodnotu" + msgid "Creating Mesh Previews" msgstr "Vytváření náhledu modelu" @@ -2096,6 +2232,9 @@ msgstr "Změněn režim filtru pro nastavení jazyka" msgid "[Default]" msgstr "[Výchozí]" +msgid "Select a Locale" +msgstr "Vybrat jazyk" + msgid "Show All Locales" msgstr "Zobrazit všechny jazyky" @@ -2312,7 +2451,7 @@ msgid "Can't reload a scene that was never saved." msgstr "Nelze načíst scénu, která nebyla nikdy uložena." msgid "Reload Saved Scene" -msgstr "Znovunačíst uloženou scénu" +msgstr "Znovu načíst uloženou scénu" msgid "" "The current scene has unsaved changes.\n" @@ -2321,9 +2460,18 @@ msgstr "" "Aktuální scéna obsahuje neuložené změny.\n" "Přesto znovu načíst? Tuto akci nelze vrátit zpět." +msgid "Save & Reload" +msgstr "Uložit a znovu načíst" + +msgid "Save modified resources before reloading?" +msgstr "Uložit změněné zdroje před opětovným načtením?" + msgid "Save & Quit" msgstr "Uložit a ukončit" +msgid "Save modified resources before closing?" +msgstr "Uložit změněné zdroje před zavřením?" + msgid "Save changes to the following scene(s) before quitting?" msgstr "Uložit změny následujících scén před ukončením?" @@ -2366,17 +2514,6 @@ msgstr "" "Deaktivujte rozšíření '%s' abyste předešli dalším chybám." msgid "" -"Unable to load addon script from path: '%s' Base type is not EditorPlugin." -msgstr "" -"Nepodařilo se načíst addon skript z cesty: '%s'. Základní typ není " -"EditorPlugin." - -msgid "Unable to load addon script from path: '%s' Script is not in tool mode." -msgstr "" -"Nelze načíst skript rozšíření z cesty: '%s'. Skript není v režimu nástroje " -"(tool)." - -msgid "" "Scene '%s' was automatically imported, so it can't be modified.\n" "To make changes to it, a new inherited scene can be created." msgstr "" @@ -2438,6 +2575,9 @@ msgstr "Výchozí" msgid "Save & Close" msgstr "Uložit a zavřít" +msgid "Save before closing?" +msgstr "Uložit před zavřením?" + msgid "%d more files or folders" msgstr "%d více souborů nebo složek" @@ -2474,6 +2614,12 @@ msgstr "Možností scén." msgid "Copy Text" msgstr "Kopírovat text" +msgid "Next Scene Tab" +msgstr "Příští karta scény" + +msgid "Previous Scene Tab" +msgstr "Předchozí karta scény" + msgid "New Scene" msgstr "Nová scéna" @@ -2492,6 +2638,9 @@ msgstr "Otevřít nedávné" msgid "Save Scene" msgstr "Uložit scénu" +msgid "Export As..." +msgstr "Exportovat jako..." + msgid "MeshLibrary..." msgstr "Knihovna modelů..." @@ -2507,6 +2656,9 @@ msgstr "Projekt" msgid "Project Settings..." msgstr "Nastavení projektu..." +msgid "Project Settings" +msgstr "Nastavení projektu" + msgid "Version Control" msgstr "Správa verzí" @@ -2534,6 +2686,9 @@ msgstr "Editor" msgid "Editor Settings..." msgstr "Nastavení editoru..." +msgid "Command Palette..." +msgstr "Paleta příkazů..." + msgid "Editor Layout" msgstr "Rozložení editoru" @@ -2576,6 +2731,9 @@ msgstr "Otázky & odpovědi" msgid "Community" msgstr "Komunita" +msgid "Copy System Info" +msgstr "Zkopírovat informace o systému" + msgid "Report a Bug" msgstr "Nahlásit chybu" @@ -2836,6 +2994,12 @@ msgstr "Napište svůj kód v _run() metodě." msgid "There is an edited scene already." msgstr "Nějaka scéna už je upravována." +msgid "Edit Built-in Action: %s" +msgstr "Upravit vestavěnou funkci: %s" + +msgid "Edit Shortcut: %s" +msgstr "Upravit zkratku: %s" + msgid "Common" msgstr "Společný" @@ -2860,6 +3024,9 @@ msgstr "Vazba" msgid "Unicode" msgstr "Unicode" +msgid "Joypad Axis %d %s (%s)" +msgstr "Osa joypadu %d %s (%s)" + msgid "All Devices" msgstr "Všechna zařízení" @@ -2872,12 +3039,18 @@ msgstr "Filtrovat podle události..." msgid "Project export for platform:" msgstr "Exportovat projekt pro platformu:" +msgid "Completed with warnings." +msgstr "Dokončeno s varováními." + msgid "Completed successfully." msgstr "Úspěšně dokončeno." msgid "Failed." msgstr "Selhalo." +msgid "Storing File: %s" +msgstr "Ukládám soubor: %s" + msgid "Storing File:" msgstr "Ukládám soubor:" @@ -2890,6 +3063,15 @@ msgstr "Nelze otevřít soubor pro čtení z cesty \"%s\"." msgid "Packing" msgstr "Balím" +msgid "Cannot create file \"%s\"." +msgstr "Nelze vytvořit soubor \"%s\"." + +msgid "Failed to export project files." +msgstr "Export souborů projektu selhal." + +msgid "Can't open encrypted file to write." +msgstr "Nelze otevřít zašifrovaný soubor pro zápis." + msgid "Custom debug template not found." msgstr "Vlastní ladící šablona nebyla nalezena." @@ -2902,6 +3084,9 @@ msgstr "Zadaná cesta pro export neexistuje." msgid "Template file not found: \"%s\"." msgstr "Soubor šablony nenalezen: \"%s\"." +msgid "Failed to copy export template." +msgstr "Nepodařilo se zkopírovat exportní šablonu." + msgid "On 32-bit exports the embedded PCK cannot be bigger than 4 GiB." msgstr "Při 32-bitovým exportu vestavěné PCK nemůže být větší než 4 GiB." @@ -3099,6 +3284,12 @@ msgstr "" "Šablony se budou stahovat i nadále.\n" "Po dokončení může dojít ke krátkému zamrznutí editoru." +msgid "" +"Target platform requires '%s' texture compression. Enable 'Import %s' to fix." +msgstr "" +"Cílová platforma vyžaduje kompresi textur '%s'. Povolte 'Import %s' v " +"nastaveních projektu." + msgid "Runnable" msgstr "Spustitelný" @@ -3108,6 +3299,12 @@ msgstr "Odstranit předvolbu '%s'?" msgid "Resources to export:" msgstr "Zdroje k exportu:" +msgid "(Inherited)" +msgstr "(zděděno)" + +msgid "%s Export" +msgstr "Export pro %s" + msgid "Release" msgstr "Vydání" @@ -3177,9 +3374,21 @@ msgstr "Vlastní (oddělené čárkou):" msgid "Feature List:" msgstr "Seznam funkcí:" +msgid "Export PCK/ZIP..." +msgstr "Exportovat PCK/Zip..." + +msgid "Export Project..." +msgstr "Exportovat projekt..." + msgid "Export All" msgstr "Exportovat vše" +msgid "Choose an export mode:" +msgstr "Zvolte mód exportu:" + +msgid "Export All..." +msgstr "Exportovat vše..." + msgid "ZIP File" msgstr "Soubor ZIP" @@ -3229,6 +3438,12 @@ msgstr "Chyba přesouvání:" msgid "Error duplicating:" msgstr "Chyba duplikování:" +msgid "Failed to save resource at %s: %s" +msgstr "Selhalo uložení zdroje do %s: %s" + +msgid "Failed to load resource at %s: %s" +msgstr "Selhalo nahrání zdroje z %s: %s" + msgid "" "This filename begins with a dot rendering the file invisible to the editor.\n" "If you want to rename it anyway, use your operating system's file manager." @@ -3276,6 +3491,9 @@ msgstr "Upravit závislosti..." msgid "View Owners..." msgstr "Zobrazit vlastníky..." +msgid "Create New" +msgstr "Vytvořit nový" + msgid "Add to Favorites" msgstr "Přidat do oblíbených" @@ -3371,9 +3589,21 @@ msgstr "Najít..." msgid "Replace..." msgstr "Nahradit..." +msgid "Replace all (no undo)" +msgstr "Nahradit všechny (nelze vrátit zpět)" + msgid "Searching..." msgstr "Hledám..." +msgid "%d match in %d file" +msgstr "%d shoda v %d souboru" + +msgid "%d matches in %d file" +msgstr "%d shod v %d souboru" + +msgid "%d matches in %d files" +msgstr "%d shod v %d souborech" + msgid "Add to Group" msgstr "Přidat do skupiny" @@ -3515,6 +3745,12 @@ msgstr "Spustit projekt." msgid "Play the edited scene." msgstr "Spustit upravenou scénu." +msgid "Play a custom scene." +msgstr "Přehrát vlastní scénu." + +msgid "Reload the played scene." +msgstr "Znovu načíst přehrávanou scénu." + msgid "Quick Run Scene..." msgstr "Rychle spustit scénu..." @@ -3626,6 +3862,9 @@ msgstr "Offset(Posun):" msgid "Loop:" msgstr "Smyčka:" +msgid "Configuration:" +msgstr "Konfigurace:" + msgid "Importing Scene..." msgstr "Importuji scénu..." @@ -3653,6 +3892,48 @@ msgstr "2D" msgid "3D" msgstr "3D" +msgid "<Unnamed Material>" +msgstr "<Nepojmenovaný materiál>" + +msgid "Error opening scene" +msgstr "Chyba při otevírání scény" + +msgid "Warning: File exists" +msgstr "Varování: Soubor již existuje" + +msgid "Will create new file" +msgstr "Vytvoří nový soubor" + +msgid "Extract" +msgstr "Extrahovat" + +msgid "Will save to new file" +msgstr "Uloží do nového souboru" + +msgid "Actions..." +msgstr "Akce..." + +msgid "Extract Materials" +msgstr "Extrahovat materiály" + +msgid "Materials" +msgstr "Materiály" + +msgid "Save Extension:" +msgstr "Uložit rozšíření:" + +msgid "Text: *.tres" +msgstr "Textový: *.tres" + +msgid "Binary: *.res" +msgstr "Binární: *.res" + +msgid "Text Resource" +msgstr "Textový zdroj" + +msgid "Binary Resource" +msgstr "Binární zdroj" + msgid "Importer:" msgstr "Importér:" @@ -3685,6 +3966,9 @@ msgstr "Importovat jako:" msgid "Preset" msgstr "Předvolba" +msgid "Advanced..." +msgstr "Pokročilé..." + msgid "" "Select a resource file in the filesystem or in the inspector to adjust import " "settings." @@ -3692,6 +3976,9 @@ msgstr "" "Vyberte zdrojový soubor v prohlížeči souboru nebo v inspektoru k úpravě " "nastavení importu." +msgid "Joypad Axes" +msgstr "Osy gamepadu" + msgid "Device:" msgstr "Zařízení:" @@ -3758,6 +4045,9 @@ msgstr "Odebrat přemapování zdroje" msgid "Remove Resource Remap Option" msgstr "Odebrat možnost přemapování zdroje" +msgid "%s cannot be found." +msgstr "Nelze nalézt %s." + msgid "Translations" msgstr "Překlady" @@ -3779,9 +4069,15 @@ msgstr "Jazyky" msgid "Set %s on %d nodes" msgstr "Nastavit %s na %d uzlech" +msgid "%s (%d Selected)" +msgstr "%s (%d vybráno)" + msgid "Select a single node to edit its signals and groups." msgstr "Zvolte vybraný uzel pro editaci jeho signálů a skupin." +msgid "Subfolder name is not a valid folder name." +msgstr "Jméno podsložky není platné jméno pro složku." + msgid "Edit a Plugin" msgstr "Editovat plugin" @@ -3886,6 +4182,9 @@ msgstr "Zvolte a přesuňte body. Nové uzly vytvořte pomocí RMB." msgid "Enable snap and show grid." msgstr "Aktivovat přichytávání a zobrazit mřížku." +msgid "Sync:" +msgstr "Synchronizovat:" + msgid "Blend:" msgstr "Prolínání:" @@ -3928,6 +4227,9 @@ msgstr "Odstranit body a trojúhelníky." msgid "Generate blend triangles automatically (instead of manually)" msgstr "Vygenerovat blend trojúhelníky automaticky (ne manuálně)" +msgid "Parameter Changed: %s" +msgstr "Změněný parametr: %s" + msgid "Output node can't be added to the blend tree." msgstr "Výstupní uzly nemohou být přidané do blend stromu." @@ -3989,15 +4291,45 @@ msgstr "Přidat uzel..." msgid "Enable Filtering" msgstr "Povolit filtrování" +msgid "Library Name:" +msgstr "Název knihovny:" + msgid "Animation name can't be empty." msgstr "Název animace nemůže být prázdný." +msgid "Animation with the same name already exists." +msgstr "Akce se stejným názvem již existuje." + +msgid "Add Animation to Library: %s" +msgstr "Přidat animaci do knihovny: %s" + +msgid "Add Animation Library: %s" +msgstr "Přidat knihovnu animací: %s" + msgid "Load Animation" msgstr "Načíst animaci" msgid "Invalid AnimationLibrary file." msgstr "Neplatný soubor AnimationLibrary." +msgid "Invalid Animation file." +msgstr "Neplatný soubor animace." + +msgid "Load Animation into Library: %s" +msgstr "Načíst animaci do knihovny: %s" + +msgid "Save Animation to File: %s" +msgstr "Uložit animaci do souboru: %s" + +msgid "Rename Animation Library: %s" +msgstr "Přejmenovat knihovnu animací: %s" + +msgid "[Global]" +msgstr "[Globální]" + +msgid "Rename Animation: %s" +msgstr "Přejmenovat animaci: %s" + msgid "Animation Name:" msgstr "Jméno animace:" @@ -4007,12 +4339,27 @@ msgstr "Vložená animace" msgid "Open in Inspector" msgstr "Otevřít v inspektoru" +msgid "Remove Animation Library: %s" +msgstr "Odstranit knihovnu animací: %s" + +msgid "Remove Animation from Library: %s" +msgstr "Odstranit animaci z knihovny: %s" + +msgid "Paste Animation to Library from clipboard" +msgstr "Vložit animaci ze schránky do knihovny" + +msgid "Save animation library to resource on disk" +msgstr "Uložit knihovnu animací do zdroje na disku" + msgid "Copy animation to clipboard" msgstr "Zkopírovat animaci do schránky" msgid "Save animation to resource on disk" msgstr "Uložit animaci do zdroje na disku" +msgid "Edit Animation Libraries" +msgstr "Upravit knihovny animací" + msgid "Toggle Autoplay" msgstr "Zapnout Autoplay" @@ -4028,12 +4375,18 @@ msgstr "Přejmenovat animaci" msgid "Change Animation Name:" msgstr "Změnit název animace:" +msgid "Delete Animation '%s'?" +msgstr "Smazat animaci '%s'?" + msgid "Remove Animation" msgstr "Smazat animaci" msgid "Invalid animation name!" msgstr "Neplatné jméno animace!" +msgid "Animation '%s' already exists!" +msgstr "Animace '%s' už existuje!" + msgid "Duplicate Animation" msgstr "Duplikovat animaci" @@ -4043,6 +4396,12 @@ msgstr "Upraveno prolnutí na další" msgid "Change Blend Time" msgstr "Změnit Blend Time" +msgid "[Global] (create)" +msgstr "[Globální] (vytvořit)" + +msgid "Duplicated Animation Name:" +msgstr "Název duplikované animace:" + msgid "Play selected animation backwards from current pos. (A)" msgstr "Přehrát zvolenou animaci pozpátku ze současné pozice. (A)" @@ -4067,6 +4426,9 @@ msgstr "Nástroje pro animaci" msgid "Animation" msgstr "Animace" +msgid "Manage Animations..." +msgstr "Spravovat animace..." + msgid "Edit Transitions..." msgstr "Upravit přechody..." @@ -4133,6 +4495,9 @@ msgstr "Přesunout uzel" msgid "Transition exists!" msgstr "Přechod existuje!" +msgid "Edit %s" +msgstr "Upravit %s" + msgid "Add Transition" msgstr "Přidat přechod" @@ -4413,6 +4778,12 @@ msgstr "Zamčeno" msgid "Grouped" msgstr "Seskupené" +msgid "Add Node Here..." +msgstr "Přidat uzel sem..." + +msgid "Instantiate Scene Here..." +msgstr "Instanciovat scénu sem..." + msgid "Scaling:" msgstr "Škálování:" @@ -4619,6 +4990,9 @@ msgstr "Výběr snímku" msgid "Preview Canvas Scale" msgstr "Náhled měřítka plátna" +msgid "Project theme" +msgstr "Motiv projektu" + msgid "Translation mask for inserting keys." msgstr "Offset maska pro vkládání klíčů." @@ -4727,6 +5101,12 @@ msgstr "Vpravo po celé výšce" msgid "Full Rect" msgstr "Celý obdélník" +msgid "Horizontal alignment" +msgstr "Vodorovné zarovnání" + +msgid "Vertical alignment" +msgstr "Svislé zarovnání" + msgid "Load Emission Mask" msgstr "Načíst emisní masku" @@ -4816,6 +5196,9 @@ msgstr "" "když je povolena tato volba, tak lze během hry vidět kolizní tvary a raycast " "uzly (pro 2D a 3D)." +msgid "Visible Paths" +msgstr "Viditelné cesty" + msgid "Visible Navigation" msgstr "Viditelná navigace" @@ -4854,16 +5237,35 @@ msgstr "" "Při vzdáleném použití na zařízení je tato možnost efektivnější, když je " "povolen síťový souborový systém." +msgid "" +"When this option is enabled, the editor debug server will stay open and " +"listen for new sessions started outside of the editor itself." +msgstr "" +"Pokud je tato možnost povolena, ladící server zůstane běžet a poslouchá " +"jestli nezačalo nové ladící sezení mimo editor." + msgid "Run %d Instance" msgid_plural "Run %d Instances" msgstr[0] "Spustit %d instanci" msgstr[1] "Spustit %d instance" msgstr[2] "Spustit %d instancí" +msgid "Size: %s" +msgstr "Velikost: %s" + +msgid "Type: %s" +msgstr "Typ: %s" + +msgid "Overrides (%d)" +msgstr "Přepisuje (%d)" + msgctxt "Locale" msgid "Add Script" msgstr "Přídat Skript" +msgid "No supported features" +msgstr "Nepodporované funkce" + msgid " - Variation" msgstr " - Variace" @@ -5020,6 +5422,12 @@ msgstr "Žádná mesh pro debugování." msgid "Mesh has no UV in layer %d." msgstr "Model nemá ve vrstvě %d žádné UV." +msgid "MeshInstance3D lacks a Mesh." +msgstr "MeshInstance3D nemá Mesh." + +msgid "Mesh has no surface to create outlines from." +msgstr "Mesh nemá povrch, z kterého lze vytvořit obrysy." + msgid "Could not create outline." msgstr "Nelze vytvořit obrys." @@ -5228,6 +5636,9 @@ msgstr "Animační klíč vložen." msgid "Objects: %d\n" msgstr "Objekty: %d\n" +msgid "FPS: %d" +msgstr "FPS: %d" + msgid "Top View." msgstr "Pohled shora." @@ -5267,6 +5678,15 @@ msgstr "Posun:" msgid "Rotating %s degrees." msgstr "Rotuji %s stupňů." +msgid "Translating %s." +msgstr "Posun %s." + +msgid "Rotating %f degrees." +msgstr "Rotace o %f stupňů." + +msgid "Scaling %s." +msgstr "Škálování %s." + msgid "Auto Orthogonal Enabled" msgstr "Auto-ortogonalizace zapnutá" @@ -5288,6 +5708,9 @@ msgstr "Bezestínový pohled" msgid "Normal Buffer" msgstr "Normální vyrovnávací paměť" +msgid "Display Advanced..." +msgstr "Zobrazit pokročilé..." + msgid "View Environment" msgstr "Zobrazit prostředí" @@ -5496,6 +5919,27 @@ msgstr "Před" msgid "Post" msgstr "Po" +msgid "Sun Color" +msgstr "Barva slunce" + +msgid "Sun Energy" +msgstr "Energie slunce" + +msgid "Shadow Max Distance" +msgstr "Maximální vzdálenost stínů" + +msgid "Add Sun to Scene" +msgstr "Přidat slunce do scény" + +msgid "Sky Color" +msgstr "Barva nebe" + +msgid "Ground Color" +msgstr "Barva země" + +msgid "Sky Energy" +msgstr "Energie nebe" + msgid "Remove Point from Curve" msgstr "Odstranit bod z křivky" @@ -6006,6 +6450,9 @@ msgstr "Přejít na další breakpoint" msgid "Go to Previous Breakpoint" msgstr "Přejít na předchozí breakpoint" +msgid "Save File As" +msgstr "Uložit soubor jako" + msgid "This skeleton has no bones, create some children Bone2D nodes." msgstr "Kostra nemá žádné kosti, vytvoř nějaké potomky Bone2D." @@ -6045,6 +6492,9 @@ msgstr "Vytvořit LightOccluder2D" msgid "LightOccluder2D Preview" msgstr "Náhled LightOccluder2D" +msgid "Can't convert a sprite from a foreign scene." +msgstr "Nelze konvertovat sprite z jiné scény." + msgid "Can't convert a sprite using animation frames to mesh." msgstr "Nelze převést sprite pomocí animačních snímků na síť." @@ -6069,6 +6519,9 @@ msgstr "Neplatná geometrie, nelze vytvořit light occluder." msgid "Create LightOccluder2D Sibling" msgstr "Vytvořit sourozence LightOccluder2D" +msgid "Sprite2D" +msgstr "Sprite2D" + msgid "Simplification:" msgstr "Zjednodušení:" @@ -6129,12 +6582,18 @@ msgstr "Smazat animaci" msgid "Animation Frames:" msgstr "Snímky animace:" +msgid "Frame Duration:" +msgstr "Délka snímku:" + msgid "Zoom Reset" msgstr "Obnovení lupy" msgid "Select Frames" msgstr "Vybrat snímky" +msgid "Frame Order" +msgstr "Pořadí snímků" + msgid "Size" msgstr "Velikost" @@ -6144,6 +6603,12 @@ msgstr "Vytvořit rámečky ze Sprite Sheet" msgid "SpriteFrames" msgstr "Snímky spritu" +msgid "%s Mipmaps" +msgstr "%s mipmap" + +msgid "Memory: %s" +msgstr "Paměť: %s" + msgid "Set Region Rect" msgstr "Nastavit oblast textury" @@ -6393,21 +6858,48 @@ msgstr "Převrátit horizontálně" msgid "Flip Vertically" msgstr "Převrátit vertikálně" +msgid "Index: %d" +msgstr "Index: %d" + msgid "Scattering:" msgstr "Rozptyl:" +msgid "Global actions:" +msgstr "Globální akce:" + msgid "Rendering" msgstr "Vykreslování" +msgid "Occlusion Layer %d" +msgstr "Okluzní vrstva %d" + msgid "Physics" msgstr "Fyzika" +msgid "Physics Layer %d" +msgstr "Fyzikální vrstva %d" + +msgid "No physics layers" +msgstr "Žádné fyzikální vrstvy" + +msgid "Navigation Layer %d" +msgstr "Navigační vrstva %d" + +msgid "Custom Data %d" +msgstr "Vlastní data %d" + msgid "Yes" msgstr "Ano" msgid "No" msgstr "Ne" +msgid "ID: %d" +msgstr "ID: %d" + +msgid "Tile properties:" +msgstr "vlastnosti dlaždice:" + msgid "TileSet" msgstr "TileSet (Sada dlaždic)" @@ -6444,6 +6936,9 @@ msgstr "Detekovat nové změny" msgid "Discard all changes" msgstr "Zrušit všechny změny" +msgid "Permanentally delete my changes" +msgstr "Permanentně smazat mé změny" + msgid "Commit Changes" msgstr "Commitnout změny" @@ -6513,6 +7008,9 @@ msgstr "Boolean" msgid "Sampler" msgstr "Vzorkovač" +msgid "[default]" +msgstr "[výchozí]" + msgid "Add Input Port" msgstr "Přidat vstupní port" @@ -6694,6 +7192,9 @@ msgstr "Vrátí arkus tangent parametrů." msgid "Returns the inverse hyperbolic tangent of the parameter." msgstr "Vrátí inverzní hyperbolický tangent parametru." +msgid "Returns the result of bitwise NOT (~a) operation on the integer." +msgstr "Vrátí výsledek bitové operace NOT (~a) na celém čísle (integeru)." + msgid "" "Finds the nearest integer that is greater than or equal to the parameter." msgstr "Nalezne nejbližší celé číslo, které je větší nebo stejné jako parametr." @@ -6828,6 +7329,13 @@ msgstr "Vrátí hyperbolický tangens parametru." msgid "Finds the truncated value of the parameter." msgstr "Vrátí zkrácenou hodnotu parametru." +msgid "Returns the result of bitwise AND (a & b) operation for two integers." +msgstr "" +"Vrátí výsledek bitové operace AND (a & b) mezi dvěma celými čísly (integery)." + +msgid "Returns the result of bitwise OR (a | b) operation for two integers." +msgstr "Vrátí výsledek operace OR (a | b) mezi dvěma celými čísly (integery)." + msgid "Perform the cubic texture lookup." msgstr "Provést vyhledání kubické textury." @@ -7048,6 +7556,16 @@ msgstr "Bylo by dobré pojmenovat váš projekt." msgid "Invalid project path (changed anything?)." msgstr "Neplatná cesta k projektu (něco se změnilo?)." +msgid "" +"Couldn't load project at '%s' (error %d). It may be missing or corrupted." +msgstr "Nelze načíst projekt z '%s' (chyba %d). Může chybět nebo být poškozený." + +msgid "Couldn't save project at '%s' (error %d)." +msgstr "Nelze uložit projekt do '%s' (chyba %d)." + +msgid "Warning: This folder is not empty" +msgstr "Varování: Tato složka není prázdná" + msgid "Couldn't create project.godot in project path." msgstr "Nelze vytvořit project.godot v umístění projektu." @@ -7093,6 +7611,9 @@ msgstr "Instalační cesta k projektu:" msgid "Renderer:" msgstr "Vykreslovač:" +msgid "Git" +msgstr "Git" + msgid "Error: Project is missing on the filesystem." msgstr "Chyba: Projek se nevyskytuje v souborovém systému." @@ -7248,6 +7769,9 @@ msgstr "Vyberte složku pro skenování" msgid "Remove All" msgstr "Odebrat vše" +msgid "Also delete project contents (no undo!)" +msgstr "Také smazat obsah projektu (nelze vrátit zpět!)" + msgid "Can't run project" msgstr "Nelze spustit projekt" @@ -7258,6 +7782,27 @@ msgstr "" "V této chvíli nemáte žádný projekt.\n" "Přejete si prozkoumat oficiální ukázkové projekty v knihovně assetů?" +msgid "Manage Project Tags" +msgstr "Spravovat štítky projektu" + +msgid "Project Tags" +msgstr "Štítky projektu" + +msgid "Click tag to remove it from the project." +msgstr "Klikněte na štítek, abyste ho odebrali z projektu." + +msgid "All Tags" +msgstr "Všechny štítky" + +msgid "Click tag to add it to the project." +msgstr "Klikněte na štítek, abyste ho přidali k projektu." + +msgid "Create New Tag" +msgstr "Vytvořit nový štítek" + +msgid "Tags are capitalized automatically when displayed." +msgstr "Štítky jsou automaticky zobrazeny s kapitalizovaným prvním písmenem." + msgid "Add Project Setting" msgstr "Přidat nastavení projektu" @@ -7279,6 +7824,9 @@ msgstr "Vymazat vstupní akce" msgid "Project Settings (project.godot)" msgstr "Nastavení projektu (project.godot)" +msgid "Select a Setting or Type its Name" +msgstr "Vyberte nastavení nebo zadejte jeho název" + msgid "Input Map" msgstr "Mapování vstupů" @@ -7529,6 +8077,9 @@ msgstr "" msgid "Can't paste root node into the same scene." msgstr "Nelze vložit kořenový uzel do stejné scény." +msgid "<Unnamed> at %s" +msgstr "<Nepojmenovaný> v %s" + msgid "Reparent to New Node" msgstr "Změnit rodiče na nový uzel" @@ -7586,6 +8137,12 @@ msgstr "Otevřít skript / Vybrat umístění" msgid "Open Script" msgstr "Otevřít skript" +msgid "Inherit %s" +msgstr "Zdědit %s" + +msgid "Inherit" +msgstr "Zdědit" + msgid "Invalid path." msgstr "Neplatná cesta." @@ -7675,6 +8232,9 @@ msgstr "Neplatná instance slovníkového formátu (nemohu nahrát skript na @pa msgid "Invalid instance dictionary (invalid subclasses)" msgstr "Neplatná instance slovníku (neplatné podtřídy)" +msgid "Value of type '%s' can't provide a length." +msgstr "Hodnota typu '%s' nemůže poskytnout délku." + msgid "Next Plane" msgstr "Další rovina" @@ -7802,6 +8362,12 @@ msgstr "" "Aby tento uzel mohl fungovat, musí mít nastaven nebo vytvořen zdroj " "NavigationMesh." +msgid "Error saving file %s: %s" +msgstr "Chyba při ukládání souboru %s: %s" + +msgid "Error loading %s: %s." +msgstr "Chyba při načítání %s: %s." + msgid "Package name is missing." msgstr "Chybí jméno balíčku." @@ -7952,9 +8518,15 @@ msgstr "Chybí identifikátor." msgid "The character '%s' is not allowed in Identifier." msgstr "Znak '%s' není dovolen v identifikátoru." +msgid "Could not open file \"%s\"." +msgstr "Nelze otevřít soubor \"%s\"." + msgid "All Files" msgstr "Všechny soubory" +msgid "Failed to create \"%s\" subfolder." +msgstr "Nelze vytvořit podsložku \"%s\"." + msgid "Invalid executable file." msgstr "Neplatný spouštěcí soubor." @@ -7967,6 +8539,18 @@ msgstr "Nelze spustit program xcrun." msgid "Could not start hdiutil executable." msgstr "Nelze spustit program hdiutil." +msgid "Could not create directory: \"%s\"." +msgstr "Nepodařilo se vytvořit adresář: \"%s\"." + +msgid "Could not create directory \"%s\"." +msgstr "Nepodařilo se vytvořit adresář: \"%s\"." + +msgid "Could not created symlink \"%s\" -> \"%s\"." +msgstr "Nelze vytvořit symbolický odkaz \"%s\" -> \"%s\"." + +msgid "Could not open \"%s\"." +msgstr "Nelze otevřít \"%s\"." + msgid "Could not open template for export: \"%s\"." msgstr "Nelze otevřít šablonu pro export: \"%s\"." @@ -7997,9 +8581,15 @@ msgstr "Spustit v prohlížeči" msgid "Run exported HTML in the system's default browser." msgstr "Spustit vyexportované HTML ve výchozím prohlížeči." +msgid "Icon size \"%d\" is missing." +msgstr "Chybí ikona velikosti \"%d\"." + msgid "Failed to rename temporary file \"%s\"." msgstr "Nelze přejmenovat dočasný soubor: \"%s\"." +msgid "Invalid icon file \"%s\"." +msgstr "Neplatný soubor ikony \"%s\"." + msgid "Failed to remove temporary file \"%s\"." msgstr "Nelze odstranit dočasný soubor: \"%s\"." @@ -8175,6 +8765,9 @@ msgstr "Nic není připojeno do vstupu '%s' uzlu '%s'." msgid "No root AnimationNode for the graph is set." msgstr "Není nastaven žádný kořen grafu AnimationNode." +msgid "Copy this constructor in a script." +msgstr "Kopírovat tento konstruktor ve skriptu." + msgid "" "Color: #%s\n" "LMB: Apply color" @@ -8253,6 +8846,12 @@ msgstr "Filtr" msgid "Invalid comparison function for that type." msgstr "Neplatná funkce pro porovnání tohoto typu." +msgid "Invalid arguments for the built-in function: \"%s(%s)\"." +msgstr "Neplatné argumenty pro vestavěnou funkci:\"%s(%s)\"." + +msgid "Invalid assignment of '%s' to '%s'." +msgstr "Neplatné přiřazení '%s' do '%s'." + msgid "Assignment to function." msgstr "Přiřazeno funkci." diff --git a/editor/translations/editor/de.po b/editor/translations/editor/de.po index 293b82c28e..e9e48e99c4 100644 --- a/editor/translations/editor/de.po +++ b/editor/translations/editor/de.po @@ -113,7 +113,7 @@ msgstr "" "Project-Id-Version: Godot Engine editor interface\n" "Report-Msgid-Bugs-To: https://github.com/godotengine/godot\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2023-11-27 01:40+0000\n" +"PO-Revision-Date: 2023-12-11 21:00+0000\n" "Last-Translator: Cerno_b <cerno.b@gmail.com>\n" "Language-Team: German <https://hosted.weblate.org/projects/godot-engine/godot/" "de/>\n" @@ -122,7 +122,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 5.2.1-rc\n" +"X-Generator: Weblate 5.3-dev\n" msgid "Main Thread" msgstr "Hauptthread" @@ -756,7 +756,7 @@ msgid "Use Blend" msgstr "Mischung verwenden" msgid "Update Mode (How this property is set)" -msgstr "Aktualisierungs-Modus (wie Eigenschaften gesetzt werden)" +msgstr "Aktualisierungs-Modus (wie diese Eigenschaft gesetzt wird)" msgid "Interpolation Mode" msgstr "Interpolationsmodus" @@ -953,7 +953,7 @@ msgid "Not possible to add a new track without a root" msgstr "Ohne einen Root kann kein neuer Track hinzugefügt werden" msgid "Invalid track for Bezier (no suitable sub-properties)" -msgstr "Track ungültig als Bezier (keine passenden Unter-Eigenschaften)" +msgstr "Track ungültig als Bezier (keine passenden Untereigenschaften)" msgid "Add Bezier Track" msgstr "Bezier-Track hinzufügen" @@ -1060,8 +1060,8 @@ msgid "" "The dummy player is forced active, non-deterministic and doesn't have the " "root motion track. Furthermore, the original node is inactive temporary." msgstr "" -"Einige Eigenschaften von AnimationPlayerEditor wurden deaktiviert, da dies " -"ein Dummy-AnimationPlayer für die Vorschau ist.\n" +"Einige Optionen von AnimationPlayerEditor wurden deaktiviert, da dies ein " +"Dummy-AnimationPlayer für die Vorschau ist.\n" "\n" "Der Dummy-Player ist zwangsweise aktiviert, nichtdeterministisch und hat " "nicht die Root-Bewegungs-Track. Außerdem ist der Ursprungsnode temporär " @@ -1136,7 +1136,7 @@ msgid "Duplicate Selection" msgstr "Auswahl duplizieren" msgid "Duplicate Transposed" -msgstr "Umgesetztes duplizieren" +msgstr "Transponiert duplizieren" msgid "Delete Selection" msgstr "Auswahl löschen" @@ -3545,15 +3545,15 @@ msgstr "" "Die Erweiterung ‚%s‘ wird deaktiviert, um weitere Fehler zu verhindern." msgid "" -"Unable to load addon script from path: '%s' Base type is not EditorPlugin." +"Unable to load addon script from path: '%s'. Base type is not 'EditorPlugin'." msgstr "" -"Erweiterungsskript konnte nicht geladen werden: ‚%s‘ Basistyp ist nicht " -"EditorPlugin." +"Erweiterungsskript konnte nicht geladen werden: ‚%s‘. Basistyp ist nicht " +"‚EditorPlugin‘." -msgid "Unable to load addon script from path: '%s' Script is not in tool mode." +msgid "Unable to load addon script from path: '%s'. Script is not in tool mode." msgstr "" -"Erweiterungsskript konnte nicht geladen werden: ‚%s‘ Skript ist nicht im Tool-" -"Modus." +"Erweiterungsskript konnte nicht geladen werden: ‚%s‘. Skript ist nicht im " +"Tool-Modus." msgid "" "Scene '%s' was automatically imported, so it can't be modified.\n" @@ -3765,7 +3765,7 @@ msgid "Orphan Resource Explorer..." msgstr "Verwaltung nicht verwendeter Ressourcen …" msgid "Upgrade Mesh Surfaces..." -msgstr "Aktualisiere Mesh-Oberflächen..." +msgstr "Mesh-Oberflächen aktualisieren..." msgid "Reload Current Project" msgstr "Aktuelles Projekt neu laden" @@ -5155,7 +5155,7 @@ msgstr "" "Projekteinstellungen änderbar." msgid "Find..." -msgstr "Finde …" +msgstr "Suche…" msgid "Replace..." msgstr "Ersetzen …" @@ -7528,7 +7528,7 @@ msgid "Copy Pose" msgstr "Pose kopieren" msgid "Clear Pose" -msgstr "Pose/Stellung löschen" +msgstr "Pose zurücksetzen" msgid "Multiply grid step by 2" msgstr "Rasterstufe verdoppeln" @@ -8702,7 +8702,7 @@ msgid "Align Transform with View" msgstr "Transform an Ansicht ausrichten" msgid "Align Rotation with View" -msgstr "Rotation auf Sicht ausrichten" +msgstr "Rotation auf Ansicht ausrichten" msgid "Set Surface %d Override Material" msgstr "Override–Material der Oberfläche %d festlegen" @@ -9054,7 +9054,7 @@ msgid "Focus Origin" msgstr "Auf Ursprung zentrieren" msgid "Focus Selection" -msgstr "Auswahl fokussieren" +msgstr "Auf Auswahl zentrieren" msgid "Toggle Freelook" msgstr "Freie Kamera ein-/ausschalten" @@ -9623,10 +9623,10 @@ msgid "%s Class Reference" msgstr "%s Klassenreferenz" msgid "Find Next" -msgstr "Finde Nächstes" +msgstr "Suche Nächstes" msgid "Find Previous" -msgstr "Finde Vorheriges" +msgstr "Suche Vorheriges" msgid "Filter Scripts" msgstr "Skripte filtern" @@ -9656,7 +9656,7 @@ msgid "Save All" msgstr "Alle speichern" msgid "Soft Reload Tool Script" -msgstr "Tool-Skript für Soft-Nachladen" +msgstr "Soft-Neuladen von Tool-Skript" msgid "Copy Script Path" msgstr "Skriptpfad kopieren" @@ -9850,7 +9850,7 @@ msgid "Toggle Word Wrap" msgstr "Zeilenumbruch ein-/ausschalten" msgid "Trim Trailing Whitespace" -msgstr "Kürze Whitespace am Zeilenende" +msgstr "Whitespace am Zeilenende entfernen" msgid "Convert Indent to Spaces" msgstr "Konvertiere Einrückung zu Leerzeichen" @@ -9892,7 +9892,7 @@ msgid "Toggle Breakpoint" msgstr "Haltepunkt ein-/ausschalten" msgid "Remove All Breakpoints" -msgstr "Lösche alle Haltepunkte" +msgstr "Alle Haltepunkte löschen" msgid "Go to Next Breakpoint" msgstr "Zum nächsten Haltepunkt springen" @@ -14226,14 +14226,66 @@ msgstr "Der Name ‚%s‘ ist ein reserviertes Schlüsselwort der Shader-Sprache msgid "Add Shader Global Parameter" msgstr "Globalen Parameter für Shader hinzufügen" +msgid "" +"This project uses meshes with an outdated mesh format from previous Godot " +"versions. The engine needs to update the format in order to use those meshes. " +"Please use the 'Upgrade Mesh Surfaces' tool from the 'Project > Tools' menu. " +"You can ignore this message and keep using outdated meshes, but keep in mind " +"that this leads to increased load times every time you load the project." +msgstr "" +"Dieses Projekt verwendet Meshes mit einem veralteten Mesh-Format aus vorigen " +"Godot-Versionen. Die Engine muss das Format aktualisieren, um diese Meshes zu " +"verwenden. Bitte verwenden Sie das 'Mesh-Oberflächen aktualisieren'-Tool aus " +"dem 'Projekt > Tools'-Menü. Sie können diese Nachricht ignorieren und weiter " +"die veralteten Meshes verwenden, aber beachten Sie, dass dies bei jedem Laden " +"des Projekts zu erhöhten Ladezeiten führen kann." + +msgid "" +"This project uses meshes with an outdated mesh format. Check the output log." +msgstr "" +"Dieses Projekt verwendet Meshes mit einem veralteten Mesh-Format. Prüfen Sie " +"das Ausgabe-Log." + msgid "Upgrading All Meshes in Project" msgstr "Upgrade alle Meshes im Projekt" msgid "Attempting to re-save " msgstr "Versuche neuzuspeichern " +msgid "Attempting to remove " +msgstr "Versuche, folgendes zu entfernen: " + +msgid "" +"The mesh format has changed in Godot 4.2, which affects both imported meshes " +"and meshes authored inside of Godot. The engine needs to update the format in " +"order to use those meshes.\n" +"\n" +"If your project predates Godot 4.2 and contains meshes, we recommend you run " +"this one time conversion tool. This update will restart the editor and may " +"take several minutes. Upgrading will make the meshes incompatible with " +"previous versions of Godot.\n" +"\n" +"You can still use your existing meshes as is. The engine will update each " +"mesh in memory, but the update will not be saved. Choosing this option will " +"lead to slower load times every time this project is loaded." +msgstr "" +"Das Mesh-Format hat sich in Godot 4.2 geändert, was sich sowohl auf " +"importierte Meshes als auch auf innerhalb von Godot erstellte Meshes " +"auswirkt. Die Engine muss das Format aktualisieren, um diese Meshes verwenden " +"zu können.\n" +"\n" +"Wenn Ihr Projekt älter als Godot 4.2 ist und Meshes enthält, empfehlen wir " +"Ihnen, dieses einmalige Konvertierungstool auszuführen. Diese Aktualisierung " +"wird den Editor neu starten und kann einige Minuten dauern. Durch die " +"Aktualisierung werden die Meshes mit früheren Godot-Versionen inkompatibel.\n" +"\n" +"Sie können Ihre bestehenden Meshes aber weiterhin verwenden. Die Engine wird " +"jedes Mesh im Speicher aktualisieren, aber die Aktualisierung wird nicht " +"gespeichert. Wenn Sie diese Option wählen, führt bei jedem Laden des Projekts " +"zu längeren Ladezeiten ." + msgid "Restart & Upgrade" -msgstr "Neustart & Upgrade" +msgstr "Neustart & Aktualisieren" msgid "Make this panel floating in the screen %d." msgstr "Dieses Panel schwebend machen auf Bildschirm %d." @@ -16643,7 +16695,7 @@ msgid "Pick a color from the application window." msgstr "Eine Farbe innerhalb des Anwendungsfensters auswählen." msgid "Select a picker shape." -msgstr "Auswahl-Shape wählen." +msgstr "Auswahl-Geometrie wählen." msgid "Select a picker mode." msgstr "Auswahlmodus wählen." diff --git a/editor/translations/editor/el.po b/editor/translations/editor/el.po index 3b3a15e751..284abe494a 100644 --- a/editor/translations/editor/el.po +++ b/editor/translations/editor/el.po @@ -2015,17 +2015,6 @@ msgstr "" "Έγινε απενεργοποίηση το προσθέτου '%s' για αποτροπή περαιτέρω προβλημάτων." msgid "" -"Unable to load addon script from path: '%s' Base type is not EditorPlugin." -msgstr "" -"Αδύνατη η φόρτωση δέσμης ενεργειών προσθέτου από τη διαδρομή: '%s'. Ο βασικός " -"τύπος δεν είναι το EditorPlugin." - -msgid "Unable to load addon script from path: '%s' Script is not in tool mode." -msgstr "" -"Αδύνατη η φόρτωση δέσμης ενεργειών προσθέτου από τη διαδρομή: '%s'. Δεν είναι " -"σε λειτουργία tool." - -msgid "" "Scene '%s' was automatically imported, so it can't be modified.\n" "To make changes to it, a new inherited scene can be created." msgstr "" diff --git a/editor/translations/editor/eo.po b/editor/translations/editor/eo.po index 91b351995e..53a70d77e7 100644 --- a/editor/translations/editor/eo.po +++ b/editor/translations/editor/eo.po @@ -1750,17 +1750,6 @@ msgstr "" "Malŝaltas la kromprogramon ĉe '%s' por malebligi pli erarojn." msgid "" -"Unable to load addon script from path: '%s' Base type is not EditorPlugin." -msgstr "" -"Ne eblas ŝargi kromprograman skripton ĉe dosierindiko: '%s'. Baza tipo ne " -"estas EditorPlugin." - -msgid "Unable to load addon script from path: '%s' Script is not in tool mode." -msgstr "" -"Ne eblas ŝargi kromprograman skripton ĉe dosierindiko: '%s'. Skripto ne estas " -"en ila reĝimo." - -msgid "" "Scene '%s' was automatically imported, so it can't be modified.\n" "To make changes to it, a new inherited scene can be created." msgstr "" diff --git a/editor/translations/editor/es.po b/editor/translations/editor/es.po index 796b2a0156..43da30e3c1 100644 --- a/editor/translations/editor/es.po +++ b/editor/translations/editor/es.po @@ -130,7 +130,7 @@ msgstr "" "Project-Id-Version: Godot Engine editor interface\n" "Report-Msgid-Bugs-To: https://github.com/godotengine/godot\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2023-11-29 09:42+0000\n" +"PO-Revision-Date: 2023-11-29 23:13+0000\n" "Last-Translator: Javier Ocampos <xavier.ocampos@gmail.com>\n" "Language-Team: Spanish <https://hosted.weblate.org/projects/godot-engine/" "godot/es/>\n" @@ -3550,17 +3550,6 @@ msgstr "" "Desactivar el addon en '%s' para prevenir mas errores." msgid "" -"Unable to load addon script from path: '%s' Base type is not EditorPlugin." -msgstr "" -"No se pudo cargar el script addon desde la ruta: '%s' El tipo de base no es " -"un EditorPlugin." - -msgid "Unable to load addon script from path: '%s' Script is not in tool mode." -msgstr "" -"No se pudo cargar el script addon desde la ruta: '%s' El script no está en " -"modo tool." - -msgid "" "Scene '%s' was automatically imported, so it can't be modified.\n" "To make changes to it, a new inherited scene can be created." msgstr "" @@ -14225,11 +14214,61 @@ msgstr "El nombre '%s' es una palabra reservada del lenguaje del shader." msgid "Add Shader Global Parameter" msgstr "Añadir Parámetro Global en el Shader" +msgid "" +"This project uses meshes with an outdated mesh format from previous Godot " +"versions. The engine needs to update the format in order to use those meshes. " +"Please use the 'Upgrade Mesh Surfaces' tool from the 'Project > Tools' menu. " +"You can ignore this message and keep using outdated meshes, but keep in mind " +"that this leads to increased load times every time you load the project." +msgstr "" +"Este proyecto utiliza mallas con un formato obsoleto de versiones anteriores " +"de Godot. El motor necesita actualizar el formato para poder usar esas " +"mallas. Utiliza la herramienta ‘Actualizar Superficies de Malla’ del menú " +"‘Proyecto > Herramientas’. Puedes ignorar este mensaje y seguir utilizando " +"mallas obsoletas, pero ten en cuenta que esto conduce a tiempos de carga más " +"largos cada vez que carga el proyecto." + +msgid "" +"This project uses meshes with an outdated mesh format. Check the output log." +msgstr "" +"Este proyecto utiliza mallas con un formato obsoleto. Revisa el registro de " +"salida." + msgid "Upgrading All Meshes in Project" msgstr "Actualizar Todas las Mallas del Proyecto" msgid "Attempting to re-save " -msgstr "Intentar volver a guardar " +msgstr "Intentando volver a guardar " + +msgid "Attempting to remove " +msgstr "Intentando eliminar " + +msgid "" +"The mesh format has changed in Godot 4.2, which affects both imported meshes " +"and meshes authored inside of Godot. The engine needs to update the format in " +"order to use those meshes.\n" +"\n" +"If your project predates Godot 4.2 and contains meshes, we recommend you run " +"this one time conversion tool. This update will restart the editor and may " +"take several minutes. Upgrading will make the meshes incompatible with " +"previous versions of Godot.\n" +"\n" +"You can still use your existing meshes as is. The engine will update each " +"mesh in memory, but the update will not be saved. Choosing this option will " +"lead to slower load times every time this project is loaded." +msgstr "" +"El formato de malla ha cambiado en Godot 4.2, lo que afecta tanto a las " +"mallas importadas como a las mallas creadas dentro de Godot. El motor " +"necesita actualizar el formato para poder usar esas mallas.\n" +"\n" +"Si tu proyecto es anterior a Godot 4.2 y contiene mallas, te recomendamos que " +"ejecutes esta herramienta de conversión única. Esta actualización reiniciará " +"el editor y puede tardar varios minutos. La actualización hará que las mallas " +"sean incompatibles con las versiones anteriores de Godot.\n" +"\n" +"Todavía puedes usar tus mallas existentes tal cual. El motor actualizará cada " +"malla en memoria, pero la actualización no se guardará. Elegir esta opción " +"provocará tiempos de carga más lentos cada vez que se cargue este proyecto." msgid "Restart & Upgrade" msgstr "Reiniciar y Actualizar" diff --git a/editor/translations/editor/es_AR.po b/editor/translations/editor/es_AR.po index 856636c088..46a0ad286a 100644 --- a/editor/translations/editor/es_AR.po +++ b/editor/translations/editor/es_AR.po @@ -1940,17 +1940,6 @@ msgstr "" "Desactivando el addon en '%s' para prevenir nuevos errores." msgid "" -"Unable to load addon script from path: '%s' Base type is not EditorPlugin." -msgstr "" -"No se pudo cargar el script de addon desde la ruta: El tipo base de '%s' no " -"es EditorPlugin." - -msgid "Unable to load addon script from path: '%s' Script is not in tool mode." -msgstr "" -"No se pudo cargar el script de addon desde la ruta: El script '%s' no está en " -"modo tool." - -msgid "" "Scene '%s' was automatically imported, so it can't be modified.\n" "To make changes to it, a new inherited scene can be created." msgstr "" diff --git a/editor/translations/editor/et.po b/editor/translations/editor/et.po index 0b223b906d..b9d9d01aca 100644 --- a/editor/translations/editor/et.po +++ b/editor/translations/editor/et.po @@ -2288,17 +2288,6 @@ msgstr "" "Lisandmooduli keelamine aadressil '%s', et vältida edasisi vigu." msgid "" -"Unable to load addon script from path: '%s' Base type is not EditorPlugin." -msgstr "" -"Lisa-skripti ei olnud võimalik laadida teelt: '%s'. Baastüüp ei ole " -"EditorPlugin." - -msgid "Unable to load addon script from path: '%s' Script is not in tool mode." -msgstr "" -"Lisa-skripti ei olnud võimalik laadida teelt: '%s'. Skript ei ole tööriista " -"režiimis." - -msgid "" "Scene '%s' was automatically imported, so it can't be modified.\n" "To make changes to it, a new inherited scene can be created." msgstr "" diff --git a/editor/translations/editor/fa.po b/editor/translations/editor/fa.po index dd71c4b913..4ee54eb7e4 100644 --- a/editor/translations/editor/fa.po +++ b/editor/translations/editor/fa.po @@ -2964,17 +2964,6 @@ msgid "Unable to load addon script from path: '%s'." msgstr "امکان بارگیری اسکریپت افزونه از مسیر وجود ندارد: '%s'." msgid "" -"Unable to load addon script from path: '%s' Base type is not EditorPlugin." -msgstr "" -"امکان بارگیری اسکریپت افزونه از مسیر: '%s' وجود ندارد، نوع پایه EditorPlugin " -"نیست." - -msgid "Unable to load addon script from path: '%s' Script is not in tool mode." -msgstr "" -"امکان بارگیری اسکریپت افزونه از مسیر: '%s' وجود ندارد، اسکریپت در حالت ابزار " -"نیست." - -msgid "" "Error loading scene, it must be inside the project path. Use 'Import' to open " "the scene, then save it inside the project path." msgstr "" diff --git a/editor/translations/editor/fi.po b/editor/translations/editor/fi.po index 82091eb62f..84cbf80ae3 100644 --- a/editor/translations/editor/fi.po +++ b/editor/translations/editor/fi.po @@ -24,7 +24,7 @@ msgstr "" "Project-Id-Version: Godot Engine editor interface\n" "Report-Msgid-Bugs-To: https://github.com/godotengine/godot\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2023-11-29 09:42+0000\n" +"PO-Revision-Date: 2023-12-08 07:17+0000\n" "Last-Translator: Mitja <mitja.leino@hotmail.com>\n" "Language-Team: Finnish <https://hosted.weblate.org/projects/godot-engine/" "godot/fi/>\n" @@ -3231,14 +3231,6 @@ msgstr "" "Lisäosa '%s' poistetaan käytöstä tulevien virheiden estämiseksi." msgid "" -"Unable to load addon script from path: '%s' Base type is not EditorPlugin." -msgstr "Virhe ladattaessa lisäosaa polusta: '%s'. Tyyppi ei ole EditorPlugin." - -msgid "Unable to load addon script from path: '%s' Script is not in tool mode." -msgstr "" -"Virhe ladattaessa lisäosaa polusta: '%s'. Skripti ei ole työkalu-tilassa." - -msgid "" "Scene '%s' was automatically imported, so it can't be modified.\n" "To make changes to it, a new inherited scene can be created." msgstr "" @@ -11350,6 +11342,9 @@ msgstr "Mitään ei ole yhdistetty syötteeseen '%s' solmussa '%s'." msgid "No root AnimationNode for the graph is set." msgstr "Graafille ei ole asetettu AnimationNode juurisolmua." +msgid "Enter a hex code (\"#ff0000\") or named color (\"red\")." +msgstr "Syötä hex koodi (\"#ff0000\") tai nimetty väri (\"red\")." + msgid "" "Color: #%s\n" "LMB: Apply color\n" @@ -11369,6 +11364,9 @@ msgstr "" msgid "Switch between hexadecimal and code values." msgstr "Vaihda heksadesimaali- ja koodiarvojen välillä." +msgid "Hex code or named color" +msgstr "Hex koodi tai nimetty väri" + msgid "Add current color as a preset." msgstr "Lisää nykyinen väri esiasetukseksi." @@ -11396,6 +11394,12 @@ msgstr "Huomio!" msgid "Please Confirm..." msgstr "Ole hyvä ja vahvista..." +msgid "You don't have permission to access contents of this folder." +msgstr "Sinulla ei ole oikeuksia tämän kansio sisältöön." + +msgid "Right-to-Left" +msgstr "Oikealta-Vasemmalle" + msgid "If \"Exp Edit\" is enabled, \"Min Value\" must be greater than 0." msgstr "Jos \"Exp Edit\" on päällä, \"Min Value\" täytyy olla suurempi kuin 0." @@ -11468,6 +11472,9 @@ msgstr "2D Tila" msgid "Invalid arguments for the built-in function: \"%s(%s)\"." msgstr "Virheelliset argumentit sisäänrakennetulle funktiolle \"%s(%s)\"." +msgid "Recursion is not allowed." +msgstr "Rekursio ei ole sallittu." + msgid "Varying may not be assigned in the '%s' function." msgstr "Varying tyyppiä ei voi sijoittaa '%s' funktiossa." @@ -11492,6 +11499,9 @@ msgstr "Vastaavaa rakentaa ei löytynyt: '%s':lle." msgid "No matching function found for: '%s'." msgstr "Vastaavaa funktiota ei löytynyt: '%s':lle." +msgid "Unknown identifier in expression: '%s'." +msgstr "Tuntematon tunniste lausekkeessa: '%s'." + msgid "Unexpected end of expression." msgstr "Lausekkeen odottamaton loppu." diff --git a/editor/translations/editor/fr.po b/editor/translations/editor/fr.po index 4f6133de6d..b39f637c8c 100644 --- a/editor/translations/editor/fr.po +++ b/editor/translations/editor/fr.po @@ -160,8 +160,8 @@ msgstr "" "Project-Id-Version: Godot Engine editor interface\n" "Report-Msgid-Bugs-To: https://github.com/godotengine/godot\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2023-11-18 06:21+0000\n" -"Last-Translator: Roskai <angel.du.2558@gmail.com>\n" +"PO-Revision-Date: 2023-12-06 04:32+0000\n" +"Last-Translator: Rertsyd <rertsyd@outlook.com>\n" "Language-Team: French <https://hosted.weblate.org/projects/godot-engine/godot/" "fr/>\n" "Language: fr\n" @@ -169,7 +169,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" -"X-Generator: Weblate 5.2\n" +"X-Generator: Weblate 5.3-dev\n" msgid "Main Thread" msgstr "Thread principal" @@ -3597,12 +3597,12 @@ msgstr "" "L'extension '%s' a été désactivée pour prévenir de nouvelles erreurs." msgid "" -"Unable to load addon script from path: '%s' Base type is not EditorPlugin." +"Unable to load addon script from path: '%s'. Base type is not 'EditorPlugin'." msgstr "" "Impossible de charger le script de l'addon depuis le chemin : '%s'. Le type " -"de base n'est pas EditorPlugin." +"de base n'est pas 'EditorPlugin'." -msgid "Unable to load addon script from path: '%s' Script is not in tool mode." +msgid "Unable to load addon script from path: '%s'. Script is not in tool mode." msgstr "" "Impossible de charger le script de l’extension depuis le chemin : '%s'. Le " "script n'est pas en mode outil." @@ -5014,6 +5014,9 @@ msgstr "Duplication du dossier :" msgid "New Inherited Scene" msgstr "Nouvelle scène héritée" +msgid "Set as Main Scene" +msgstr "Définir comme scène principale" + msgid "Open Scenes" msgstr "Ouvrir des scènes" @@ -5125,6 +5128,9 @@ msgstr "Renommer..." msgid "Open in External Program" msgstr "Ouvrir dans l'éditeur externe" +msgid "Red" +msgstr "Rouge" + msgid "Orange" msgstr "Orange" @@ -8328,6 +8334,9 @@ msgstr "Créer un maillage de navigation" msgid "Create Debug Tangents" msgstr "Générer les tangentes debug" +msgid "No mesh to unwrap." +msgstr "Aucun mesh à déplier." + msgid "" "Mesh cannot unwrap UVs because it does not belong to the edited scene. Make " "it unique first." @@ -13816,6 +13825,12 @@ msgstr "Le paramètre global de shaders '%s' existe déjà" msgid "Name '%s' is a reserved shader language keyword." msgstr "Le nom '%s' est un mot-clé réservé du langage de shader." +msgid "" +"This project uses meshes with an outdated mesh format. Check the output log." +msgstr "" +"Ce projet utilise des meshes avec un format obsolète. Vérifiez le journal de " +"sortie." + msgid "Attempting to re-save " msgstr "Tentative de ré-enregistrer " diff --git a/editor/translations/editor/gl.po b/editor/translations/editor/gl.po index 2b8c68acf3..3dbb61edec 100644 --- a/editor/translations/editor/gl.po +++ b/editor/translations/editor/gl.po @@ -9,20 +9,20 @@ # davidrogel <david.rogel.pernas@icloud.com>, 2021. # David Salcas <davisalcas@gmail.com>, 2023. # David Fernández Pérez <davidfernandezperez.thezombrex@gmail.com>, 2023. +# Carlos Cortes Garcia <carlos.cortes.games@gmail.com>, 2023. msgid "" msgstr "" "Project-Id-Version: Godot Engine editor interface\n" "Report-Msgid-Bugs-To: https://github.com/godotengine/godot\n" -"PO-Revision-Date: 2023-10-04 02:10+0000\n" -"Last-Translator: David Fernández Pérez <davidfernandezperez.thezombrex@gmail." -"com>\n" +"PO-Revision-Date: 2023-12-11 21:00+0000\n" +"Last-Translator: Carlos Cortes Garcia <carlos.cortes.games@gmail.com>\n" "Language-Team: Galician <https://hosted.weblate.org/projects/godot-engine/" "godot/gl/>\n" "Language: gl\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8-bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 5.1-dev\n" +"X-Generator: Weblate 5.3-dev\n" msgid "Main Thread" msgstr "Fío principal" @@ -198,6 +198,12 @@ msgstr "Xesto de ampliación en (%s) co factor %s" msgid "Pan Gesture at (%s) with delta (%s)" msgstr "Xesto panorámico en (%s) con delta (%s)" +msgid "MIDI Input on Channel=%s Message=%s" +msgstr "Entrada MIDI no Canal=%s Mensaxe=%s" + +msgid "Input Event with Shortcut=%s" +msgstr "Evento de Entrada con atallo=%s" + msgid "Accept" msgstr "Aceptar" @@ -273,6 +279,9 @@ msgstr "Reducción de sangría" msgid "Backspace" msgstr "Retroceso" +msgid "Backspace Word" +msgstr "Retroceder Palabra" + msgid "Backspace all to Left" msgstr "Retroceder todo á esquerda" @@ -297,12 +306,36 @@ msgstr "Cursor Dereita" msgid "Caret Word Right" msgstr "Cursor Palabra Dereita" +msgid "Caret Up" +msgstr "Cursor Enriba" + +msgid "Caret Down" +msgstr "Cursor Abaixo" + +msgid "Caret Line Start" +msgstr "Comezo da Línea do Cursor" + +msgid "Caret Line End" +msgstr "Cursor Final da Línea" + msgid "Caret Page Up" msgstr "Cursor Páxina Superior" +msgid "Caret Page Down" +msgstr "Cursor Páxina Inferior" + msgid "Caret Document Start" msgstr "Cursor Inicio do Documento" +msgid "Caret Document End" +msgstr "Cursor Final do Documento" + +msgid "Caret Add Below" +msgstr "Cursor Engadir Abaixo" + +msgid "Caret Add Above" +msgstr "Cursor Engadir Enriba" + msgid "Scroll Up" msgstr "Desprazamento cara arriba" @@ -312,6 +345,9 @@ msgstr "Desprazamento cara abaixo" msgid "Select All" msgstr "Seleccionar Todo" +msgid "Select Word Under Caret" +msgstr "Seleccionar Palabra Debaixo do Cursor" + msgid "Add Selection for Next Occurrence" msgstr "Engadir selección para a próxima aparición" @@ -468,6 +504,15 @@ msgstr "Duplicar Clave(s) Seleccionadas(s)" msgid "Delete Selected Key(s)" msgstr "Eliminar Clave(s) Seleccionada(s)" +msgid "Make Handles Free" +msgstr "Liberar os Manipuladores" + +msgid "Make Handles Linear" +msgstr "Facer os Manipuladores Lineais" + +msgid "Make Handles Balanced" +msgstr "Facer os Manipuladores equilibrados" + msgid "Add Bezier Point" msgstr "Engadir Punto Bezier" @@ -2404,17 +2449,6 @@ msgstr "" "Desactive o complemento en '%s' para evitar máis erros." msgid "" -"Unable to load addon script from path: '%s' Base type is not EditorPlugin." -msgstr "" -"Non se puido cargar o Script da característica adicional (Plugin): O tipo " -"base de %s non é EditorPlugin." - -msgid "Unable to load addon script from path: '%s' Script is not in tool mode." -msgstr "" -"Non se puido cargar Script de característica adicional (Addon) na ruta: '%s'. " -"O script non está en modo ferramenta (tool)." - -msgid "" "Scene '%s' was automatically imported, so it can't be modified.\n" "To make changes to it, a new inherited scene can be created." msgstr "" diff --git a/editor/translations/editor/he.po b/editor/translations/editor/he.po index b6ce679883..764b4a1102 100644 --- a/editor/translations/editor/he.po +++ b/editor/translations/editor/he.po @@ -1867,13 +1867,6 @@ msgstr "" "יש לבטל את ההרחבה ,%s, כדי למנוע שגיאות נוספות." msgid "" -"Unable to load addon script from path: '%s' Base type is not EditorPlugin." -msgstr "לא ניתן לטעון סקריפט הרחבה מנתיב: '%s' סוג הבסיס אינו EditorPlugin." - -msgid "Unable to load addon script from path: '%s' Script is not in tool mode." -msgstr "לא ניתן לטעון סקריפט הרחבה מנתיב: '%s' סקריפט אינו מוגדר ככלי (tool)." - -msgid "" "Scene '%s' was automatically imported, so it can't be modified.\n" "To make changes to it, a new inherited scene can be created." msgstr "" diff --git a/editor/translations/editor/hu.po b/editor/translations/editor/hu.po index 87c89bb544..3bc2c4e019 100644 --- a/editor/translations/editor/hu.po +++ b/editor/translations/editor/hu.po @@ -1850,17 +1850,6 @@ msgid "Unable to load addon script from path: '%s'." msgstr "Nem sikerült az addon szkript betöltése a következő útvonalról: '%s'." msgid "" -"Unable to load addon script from path: '%s' Base type is not EditorPlugin." -msgstr "" -"Nem sikerült az addon szkript betöltése a következő útvonalról: '%s' Az " -"alaptípus nem EditorPlugin." - -msgid "Unable to load addon script from path: '%s' Script is not in tool mode." -msgstr "" -"Nem sikerült az addon szkript betöltése a következő útvonalról: '%s' A " -"szkript nem eszközmódban van." - -msgid "" "Scene '%s' was automatically imported, so it can't be modified.\n" "To make changes to it, a new inherited scene can be created." msgstr "" diff --git a/editor/translations/editor/id.po b/editor/translations/editor/id.po index a772be1121..4c6d1ef46f 100644 --- a/editor/translations/editor/id.po +++ b/editor/translations/editor/id.po @@ -3422,16 +3422,6 @@ msgstr "" "Lepaskan addon di '%s' to mencegah kesalahan kedepan." msgid "" -"Unable to load addon script from path: '%s' Base type is not EditorPlugin." -msgstr "" -"Tidak dapat memuat skrip addon dari jalur: '%s' karena jenis Basisnya bukan " -"EditorPlugin." - -msgid "Unable to load addon script from path: '%s' Script is not in tool mode." -msgstr "" -"Tidak dapat memuat addon script dari jalur: '%s' Script tidak pada mode tool." - -msgid "" "Scene '%s' was automatically imported, so it can't be modified.\n" "To make changes to it, a new inherited scene can be created." msgstr "" diff --git a/editor/translations/editor/it.po b/editor/translations/editor/it.po index 1ee9bd8616..782e9f457f 100644 --- a/editor/translations/editor/it.po +++ b/editor/translations/editor/it.po @@ -93,13 +93,14 @@ # Luca Martinelli <lucxmangajet@gmail.com>, 2023. # Frankie McEyes <mceyes@protonmail.com>, 2023. # Andrea <andrea.rubino1990@gmail.com>, 2023. +# Samuele Righi <blackdestinyx145@gmail.com>, 2023. msgid "" msgstr "" "Project-Id-Version: Godot Engine editor interface\n" "Report-Msgid-Bugs-To: https://github.com/godotengine/godot\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2023-11-15 10:05+0000\n" -"Last-Translator: Micila Micillotto <micillotto@gmail.com>\n" +"PO-Revision-Date: 2023-12-11 21:00+0000\n" +"Last-Translator: Samuele Righi <blackdestinyx145@gmail.com>\n" "Language-Team: Italian <https://hosted.weblate.org/projects/godot-engine/" "godot/it/>\n" "Language: it\n" @@ -107,10 +108,10 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 5.2-dev\n" +"X-Generator: Weblate 5.3-dev\n" msgid "Main Thread" -msgstr "Thread principale" +msgstr "Thread Principale" msgid "Unset" msgstr "Non impostato" @@ -997,6 +998,9 @@ msgstr "" msgid "Animation Add RESET Keys" msgstr "Aggiungi le chiavi RESET di un'animazione" +msgid "Bake Animation as Linear Keys" +msgstr "Salva l'animazione come chiavi lineari" + msgid "" "This animation belongs to an imported scene, so changes to imported tracks " "will not be saved.\n" @@ -1016,6 +1020,9 @@ msgstr "" "delle tracce personalizzate, abilitare \"Save To File\" e\n" "\"Keep Custom Tracks\"." +msgid "AnimationPlayer is inactive. The playback will not be processed." +msgstr "AnimationPlayer non è attivo. La riproduzione non verrà eseguita." + msgid "Select an AnimationPlayer node to create and edit animations." msgstr "Selezionare un nodo AnimationPlayer per creare e modificare animazioni." @@ -1025,6 +1032,18 @@ msgstr "Scena importata" msgid "Warning: Editing imported animation" msgstr "Attenzione: sta venendo modificata un'animazione importata" +msgid "Dummy Player" +msgstr "Player Fantoccio" + +msgid "Warning: Editing dummy AnimationPlayer" +msgstr "Attenzione: AnimationPlayer fantoccio in modifica" + +msgid "Inactive Player" +msgstr "Player inattivo" + +msgid "Warning: AnimationPlayer is inactive" +msgstr "Avviso: AnimationPlayer non attivo" + msgid "Toggle between the bezier curve editor and track editor." msgstr "Commuta tra l'editor di curve di Bézier e l'editor di tracce." @@ -1868,6 +1887,12 @@ msgstr "Il nome della cartella non può essere vuoto." msgid "Folder name contains invalid characters." msgstr "Il nome della cartella contiene caratteri non validi." +msgid "Folder name cannot begin or end with a space." +msgstr "Il nome della cartella non può cominciare o finire con uno spazio." + +msgid "Folder name cannot begin with a dot." +msgstr "Il nome di una cartella non può cominciare con un punto." + msgid "File with that name already exists." msgstr "Esiste già un File con questo nome." @@ -1915,6 +1940,9 @@ msgstr "Sviluppatori" msgid "Authors" msgstr "Autori" +msgid "Patrons" +msgstr "Patrons" + msgid "Platinum Sponsors" msgstr "Sponsor platino" @@ -1924,6 +1952,15 @@ msgstr "Sponsor oro" msgid "Silver Sponsors" msgstr "Sponsor argento" +msgid "Diamond Members" +msgstr "Membri Diamond" + +msgid "Titanium Members" +msgstr "Membri Titanium" + +msgid "Platinum Members" +msgstr "Membri Platinum" + msgid "Donors" msgstr "Donatori" @@ -1960,6 +1997,16 @@ msgstr "" msgid "%s (already exists)" msgstr "%s (già esistente)" +msgid "%d file conflicts with your project and won't be installed" +msgid_plural "%d files conflict with your project and won't be installed" +msgstr[0] "" +"Il file %d va in conflitto con il tuo progetto e non verrà installato" +msgstr[1] "" +"I file %d vanno in conflitto con il tuo progetto e non verranno installati" + +msgid "Select Install Folder" +msgstr "Seleziona Cartella di Installazione" + msgid "Uncompressing Assets" msgstr "Estraendo i contenuti" @@ -1975,6 +2022,28 @@ msgstr "Contenuto \"%s\" installato con successo!" msgid "Success!" msgstr "Successo!" +msgid "Asset:" +msgstr "Contenuto:" + +msgid "Open the list of the asset contents and select which files to install." +msgstr "Apri la lista degli asset delle risorse e seleziona quasi installare." + +msgid "Change Install Folder" +msgstr "Cambia la cartella di installazione" + +msgid "" +"Change the folder where the contents of the asset are going to be installed." +msgstr "Cambia la cartella dove verranno installati i contenuti degli asset." + +msgid "Ignore asset root" +msgstr "Ignora contenuto root" + +msgid "No files conflict with your project" +msgstr "Nessun file in conflitto con il tuo progetto" + +msgid "Installation preview:" +msgstr "Anteprima installazione:" + msgid "Install" msgstr "Installa" @@ -2344,6 +2413,13 @@ msgstr "Classi forzate al rilevamento:" msgid "Edit Build Configuration Profile" msgstr "Modifica il profilo di configurazione di costruzione" +msgid "" +"Failed to execute command \"%s\":\n" +"%s." +msgstr "" +"Impossibile aprire l'eseguibile \"%s\".\n" +"%s." + msgid "Filter Commands" msgstr "Filtra i comandi" @@ -2580,6 +2656,15 @@ msgstr "" msgid "Error codes returned:" msgstr "Codici di errore restituiti:" +msgid "There is currently no description for this method." +msgstr "Attualmente non esiste nessuna descrizione per questo metodo." + +msgid "There is currently no description for this constructor." +msgstr "Attualmente non esiste nessuna descrizione per questo costruttore." + +msgid "There is currently no description for this operator." +msgstr "Attualmente non esiste nessuna descrizione per questo operatore." + msgid "Top" msgstr "In cima" @@ -2700,12 +2785,24 @@ msgstr "Descrizioni del metodo" msgid "Operator Descriptions" msgstr "Descrizione degli operatori" +msgid "Metadata:" +msgstr "Metadati:" + msgid "Property:" msgstr "Proprietà:" +msgid "Method:" +msgstr "Metodo:" + msgid "Signal:" msgstr "Segnale:" +msgid "Theme Item:" +msgstr "Oggetto Tema:" + +msgid "No description available." +msgstr "Nessuna descrizione disponibile." + msgid "%d match." msgstr "%d corrispondenze." @@ -3304,17 +3401,6 @@ msgstr "" "Disabilitata l'aggiunta di '%s' per prevenire ulteriori errori." msgid "" -"Unable to load addon script from path: '%s' Base type is not EditorPlugin." -msgstr "" -"Impossibile caricare uno script di estensione dal percorso: il tipo base di " -"\"%s\" non è EditorPlugin." - -msgid "Unable to load addon script from path: '%s' Script is not in tool mode." -msgstr "" -"Impossibile caricare lo script di un componente aggiuntivo dal percorso: " -"\"%s\" Lo script non è in modalità strumento." - -msgid "" "Scene '%s' was automatically imported, so it can't be modified.\n" "To make changes to it, a new inherited scene can be created." msgstr "" @@ -9219,6 +9305,12 @@ msgstr "Apri il file nell'ispettore" msgid "Close File" msgstr "Chiudi il file" +msgid "Make the shader editor floating." +msgstr "Rendi l'editor delle shader fluttuante." + +msgid "Shader stage compiled without errors." +msgstr "Fase delle shader compilata senza errori." + msgid "" "File structure for '%s' contains unrecoverable errors:\n" "\n" @@ -10224,6 +10316,15 @@ msgstr "Seleziona il livello della mappa dei tasselli selezionata" msgid "Toggle grid visibility." msgstr "Commuta la visibilità della griglia." +msgid "Source-level proxies" +msgstr "Proxies a livello sorgente" + +msgid "Coords-level proxies" +msgstr "Proxies a livello coordinate" + +msgid "Alternative-level proxies" +msgstr "Proxies di livello alternativo" + msgid "From Coords" msgstr "Dalle coordinate" @@ -10247,9 +10348,23 @@ msgstr "Layer di fisica %d" msgid "Navigation Layer %d" msgstr "Layer di navigazione %d" +msgid "" +"Create and customize navigation layers in the inspector of the TileSet " +"resource." +msgstr "" +"Crea e personalizza i layers di navigazione nell'ispettore della risorsa " +"TileSet." + msgid "Custom Data %d" msgstr "Dati personalizzati %d" +msgid "" +"Create and customize custom data layers in the inspector of the TileSet " +"resource." +msgstr "" +"Crea e personalizza i layers di dati personalizzati nell'ispettore della " +"risorsa TileSet." + msgid "Select a property editor" msgstr "Seleziona un editor delle proprietà" @@ -10301,6 +10416,13 @@ msgstr "Seleziona dei tasselli." msgid "Paint properties." msgstr "Proprietà di disegno." +msgid "" +"No tiles selected.\n" +"Select one or more tiles from the palette to edit its properties." +msgstr "" +"Nessuna tile selezionata.\n" +"Seleziona una o più tile dalla palette per modificare le sue proprietà." + msgid "Paint Properties:" msgstr "Proprietà di disegno:" @@ -10311,6 +10433,13 @@ msgid "Remove Tiles in Fully Transparent Texture Regions" msgstr "" "Rimuovi i tasselli nelle regioni completamente trasparenti della texture" +msgid "" +"The current atlas source has tiles outside the texture.\n" +"You can clear it using \"%s\" option in the 3 dots menu." +msgstr "" +"La corrente risorsa atlas ha delle tile fuori dalla texture.\n" +"Puoi cancellarle usando l'opzione \"%s\" nel menu a 3 punti." + msgid "Create an Alternative Tile" msgstr "Crea un tassello alternativo" @@ -10348,12 +10477,27 @@ msgstr "Ordina i sorgenti" msgid "Open Atlas Merging Tool" msgstr "Apri lo strumento di fusione degli atlas" +msgid "" +"No TileSet source selected. Select or create a TileSet source.\n" +"You can create a new source by using the Add button on the left or by " +"dropping a tileset texture onto the source list." +msgstr "" +"Nessuna TileSet sorgente selezionata. Seleziona o crea una TileSet sorgente.\n" +"Puoi creare una nuova sorgente usando il pulsante Aggiungi sulla sinistra o " +"rilasciando una texture tileset dentro alla lista sorgenti." + +msgid "Add new patterns in the TileMap editing mode." +msgstr "Aggiunti nuovi modelli nella modalità di editing della TileMap." + msgid "Add a Scene Tile" msgstr "Aggiungi un tassello scena" msgid "Remove a Scene Tile" msgstr "Rimuovi un tassello scena" +msgid "Drag and drop scenes here or use the Add button." +msgstr "Trascina e rilascia scene qui o usando il pulsante Aggiungi." + msgid "Tile properties:" msgstr "Proprietà del tassello:" @@ -10758,6 +10902,9 @@ msgstr "Aggiungi Nodo" msgid "Create Shader Node" msgstr "Crea Nodo Shader" +msgid "Delete Shader Varying" +msgstr "Elimina la Varying Shader" + msgid "Color function." msgstr "Funzione colore." @@ -10862,6 +11009,9 @@ msgstr "" msgid "Boolean constant." msgstr "Costante booleana." +msgid "Translated to '%s' in Godot Shading Language." +msgstr "Tradotto in \"%s\" nel Godot Shading Language." + msgid "'%s' input parameter for all shader modes." msgstr "Parametro di input \"%s\" per tutte le modalità shader." @@ -10906,6 +11056,14 @@ msgstr "" "Parametro d'ingresso \"%s\" per la modalità di elaborazione e collisione " "dello shader." +msgid "" +"A node for help to multiply a position input vector by rotation using " +"specific axis. Intended to work with emitters." +msgstr "" +"Un nodo per aiutare a moltiplicare un vettore di input di posizione mediante " +"rotazione utilizzando un asse specifico. Pensato per lavorare con gli " +"emettitori." + msgid "Float function." msgstr "Funzione float." @@ -11017,6 +11175,9 @@ msgstr "Restituisce il minore di due valori." msgid "Linear interpolation between two scalars." msgstr "Interpolazione lineare tra due scalari." +msgid "Performs a fused multiply-add operation (a * b + c) on scalars." +msgstr "Esegue un'operazione di moltiplicazione fusa (a * b *c) su scalari." + msgid "Returns the opposite value of the parameter." msgstr "Restituisce il valore opposto del parametro." @@ -11211,9 +11372,29 @@ msgstr "Parametro scalare intero." msgid "Scalar unsigned integer parameter." msgstr "Parametro scalare intero senza segno." +msgid "Casts a ray against the screen SDF and returns the distance travelled." +msgstr "" +"Lancia un raggio contro lo schermo SDF e restituisce la distanza percorsa." + +msgid "Converts a SDF to screen UV." +msgstr "Converte un SDF ad uno schermo UV." + +msgid "Function to be applied on texture coordinates." +msgstr "Funzione da applicare sulle coordinate della texture." + +msgid "Polar coordinates conversion applied on texture coordinates." +msgstr "" +"Conversione delle coordinate polari applicata sulle coordinate della texture." + msgid "Perform the cubic texture lookup." msgstr "Esegue la ricerca di texture cubiche." +msgid "" +"Returns the depth value obtained from the depth prepass in a linear space." +msgstr "" +"Restituisce il valore della profondità ottenuto dal prepass della profondità " +"in uno spazio lineare." + msgid "Perform the 3D texture lookup." msgstr "Esegue la ricerca di texture 3D." @@ -11860,6 +12041,15 @@ msgstr "" "\n" msgid "" +"Warning: This project was last edited in Godot %s. Opening will change it to " +"Godot %s.\n" +"\n" +msgstr "" +"Avviso: Questo progetto è stato modificato l'ultima volta in Godot %s. " +"Aprirlo lo convertirà a Godot %s.\n" +"\n" + +msgid "" "Warning: This project uses the following features not supported by this build " "of Godot:\n" "\n" @@ -13789,6 +13979,15 @@ msgstr "" "\"Frames\" per permettere a AnimatedSprite2D di visualizzare i fotogrammi." msgid "" +"Only one visible CanvasModulate is allowed per canvas.\n" +"When there are more than one, only one of them will be active. Which one is " +"undefined." +msgstr "" +"Solo un CanvasModulate è permesso per ogni canvas.\n" +"Quando ne è presente più di uno, solo uno sarà attivo. Quale di essi non è " +"definito." + +msgid "" "This node has no shape, so it can't collide or interact with other objects.\n" "Consider adding a CollisionShape2D or CollisionPolygon2D as a child to define " "its shape." @@ -14529,18 +14728,42 @@ msgstr "Previsto: '%s'." msgid "Expected a '%s' after '%s'." msgstr "Previsto '%s' dopo '%s'." +msgid "Invalid argument name." +msgstr "Nome argomento invalido." + +msgid "'##' must not appear at end of macro expansion." +msgstr "'##' non deve apparire alla fine dell'espansione macro." + msgid "Unmatched elif." msgstr "elif spaiato." +msgid "Missing condition." +msgstr "Condizione mancante." + msgid "Unmatched else." msgstr "else non corrisposto." +msgid "Invalid endif." +msgstr "endif non valido." + +msgid "Invalid ifdef." +msgstr "ifdef non valido." + +msgid "Invalid ifndef." +msgstr "ifndef non valido." + msgid "Shader include file does not exist:" msgstr "Il file Shader include non esiste:" msgid "Macro expansion limit exceeded." msgstr "Limite espansione macro superato." +msgid "Invalid macro argument count." +msgstr "Numero di argomenti macro invalido." + +msgid "Invalid symbols placed before directive." +msgstr "Simboli invalidi inseriti prima della direttiva." + msgid "The const '%s' is declared but never used." msgstr "La costante '%s' è dichiarata ma mai utilizzata." diff --git a/editor/translations/editor/ja.po b/editor/translations/editor/ja.po index 082a1630ee..a8593c2e08 100644 --- a/editor/translations/editor/ja.po +++ b/editor/translations/editor/ja.po @@ -68,7 +68,7 @@ msgstr "" "Project-Id-Version: Godot Engine editor interface\n" "Report-Msgid-Bugs-To: https://github.com/godotengine/godot\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2023-11-26 13:51+0000\n" +"PO-Revision-Date: 2023-12-01 04:24+0000\n" "Last-Translator: Koji Horaguchi <koji.horaguchi@gmail.com>\n" "Language-Team: Japanese <https://hosted.weblate.org/projects/godot-engine/" "godot/ja/>\n" @@ -77,7 +77,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Weblate 5.2.1-rc\n" +"X-Generator: Weblate 5.3-dev\n" msgid "Main Thread" msgstr "メインスレッド" @@ -3443,17 +3443,6 @@ msgstr "" "さらなるエラーを防ぐため、'%s' のアドオンを無効化します。" msgid "" -"Unable to load addon script from path: '%s' Base type is not EditorPlugin." -msgstr "" -"パス '%s' からアドオンスクリプトを読み込めません。基底型が EditorPlugin ではあ" -"りません。" - -msgid "Unable to load addon script from path: '%s' Script is not in tool mode." -msgstr "" -"パス '%s' からアドオンスクリプトを読み込めません。スクリプトがツールモードでは" -"ありません。" - -msgid "" "Scene '%s' was automatically imported, so it can't be modified.\n" "To make changes to it, a new inherited scene can be created." msgstr "" @@ -13943,11 +13932,61 @@ msgstr "名前 '%s' は予約されたシェーダー言語キーワードです msgid "Add Shader Global Parameter" msgstr "シェーダーグローバルパラメーターを追加" +msgid "" +"This project uses meshes with an outdated mesh format from previous Godot " +"versions. The engine needs to update the format in order to use those meshes. " +"Please use the 'Upgrade Mesh Surfaces' tool from the 'Project > Tools' menu. " +"You can ignore this message and keep using outdated meshes, but keep in mind " +"that this leads to increased load times every time you load the project." +msgstr "" +"このプロジェクトでは、以前の Godot バージョンの古いメッシュ形式のメッシュが使" +"用されています。 これらのメッシュを使用するには、エンジンでフォーマットを更新" +"する必要があります。 「プロジェクト > ツール」メニューから「メッシュサーフェス" +"のアップグレード」ツールを使用してください。 このメッセージを無視して古いメッ" +"シュを使用し続けることもできますが、プロジェクトをロードするたびにロード時間が" +"長くなることに注意してください。" + +msgid "" +"This project uses meshes with an outdated mesh format. Check the output log." +msgstr "" +"このプロジェクトでは、古いメッシュ形式のメッシュが使用されています。 出力され" +"たログを確認してください。" + msgid "Upgrading All Meshes in Project" msgstr "プロジェクト内のすべてのメッシュをアップグレード中" msgid "Attempting to re-save " -msgstr "再保存を思考中 " +msgstr "再保存を試行中 " + +msgid "Attempting to remove " +msgstr "削除を試行中 " + +msgid "" +"The mesh format has changed in Godot 4.2, which affects both imported meshes " +"and meshes authored inside of Godot. The engine needs to update the format in " +"order to use those meshes.\n" +"\n" +"If your project predates Godot 4.2 and contains meshes, we recommend you run " +"this one time conversion tool. This update will restart the editor and may " +"take several minutes. Upgrading will make the meshes incompatible with " +"previous versions of Godot.\n" +"\n" +"You can still use your existing meshes as is. The engine will update each " +"mesh in memory, but the update will not be saved. Choosing this option will " +"lead to slower load times every time this project is loaded." +msgstr "" +"Godot 4.2 ではメッシュ形式が変更され、インポートされたメッシュと Godot 内で作" +"成されたメッシュの両方に影響します。 これらのメッシュを使用するには、エンジン" +"でフォーマットを更新する必要があります。\n" +"\n" +"プロジェクトが Godot 4.2 よりも古く、メッシュが含まれている場合は、この 1 回限" +"りの変換ツールを実行することをお勧めします。 この更新によりエディタが再起動さ" +"れ、数分かかる場合があります。 アップグレードすると、メッシュは Godot の以前の" +"バージョンと互換性がなくなります。\n" +"\n" +"既存のメッシュをそのまま使用できます。 エンジンはメモリ内の各メッシュを更新し" +"ますが、更新は保存されません。 このオプションを選択すると、このプロジェクトが" +"ロードされるたびにロード時間が遅くなります。" msgid "Restart & Upgrade" msgstr "再起動とアップグレード" diff --git a/editor/translations/editor/ko.po b/editor/translations/editor/ko.po index 8d69f31ad5..0f80bd97ac 100644 --- a/editor/translations/editor/ko.po +++ b/editor/translations/editor/ko.po @@ -61,8 +61,8 @@ msgstr "" "Project-Id-Version: Godot Engine editor interface\n" "Report-Msgid-Bugs-To: https://github.com/godotengine/godot\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2023-11-10 08:03+0000\n" -"Last-Translator: Myeongjin Lee <aranet100@gmail.com>\n" +"PO-Revision-Date: 2023-12-04 01:29+0000\n" +"Last-Translator: nulta <un5450@naver.com>\n" "Language-Team: Korean <https://hosted.weblate.org/projects/godot-engine/godot/" "ko/>\n" "Language: ko\n" @@ -70,7 +70,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Weblate 5.2-dev\n" +"X-Generator: Weblate 5.3-dev\n" msgid "Main Thread" msgstr "메인 스레드" @@ -607,10 +607,19 @@ msgid "Animation Change Call" msgstr "애니메이션 변경 호출" msgid "Animation Multi Change Transition" -msgstr "애니메이션 다중 변경 전환" +msgstr "애니메이션 전환 다수 변경" + +msgid "Animation Multi Change Position3D" +msgstr "애니메이션 3D 위치 다수 변경" + +msgid "Animation Multi Change Rotation3D" +msgstr "애니메이션 3D 회전 다수 변경" + +msgid "Animation Multi Change Scale3D" +msgstr "애니메이션 3D 크기 다수 변경" msgid "Animation Multi Change Keyframe Value" -msgstr "애니메이션 다중 키프레임 값 변경" +msgstr "애니메이션 키프레임 값 다수 변경" msgid "Animation Multi Change Call" msgstr "애니메이션 다중 호출 바꾸기" @@ -718,7 +727,7 @@ msgid "(Invalid, expected type: %s)" msgstr "(인식불가. 예상한 타입: %s)" msgid "Easing:" -msgstr "Easing:" +msgstr "이징:" msgid "In-Handle:" msgstr "입력 핸들:" @@ -820,6 +829,11 @@ msgstr "" msgid "Remove Anim Track" msgstr "애니메이션 트랙 제거" +msgid "Hold Shift when clicking the key icon to skip this dialog." +msgstr "" +"키 아이콘을 누를 때 Shift 키를 누르고 있으면 이 대화 상자를 건너뛸 수 있습니" +"다." + msgid "Create new track for %s and insert key?" msgstr "\"%s\"을(를) 위한 새로운 트랙을 생성하고 키를 삽입하시겠습니까?" @@ -942,7 +956,7 @@ msgid "Animation Scale Keys" msgstr "애니메이션 스케일 키" msgid "Make Easing Keys" -msgstr "완화 키 만들기" +msgstr "이징 키 만들기" msgid "" "This option does not work for Bezier editing, as it's only a single track." @@ -952,6 +966,9 @@ msgstr "" msgid "Animation Add RESET Keys" msgstr "애니메이션 리셋 키 추가" +msgid "Bake Animation as Linear Keys" +msgstr "애니메이션을 직선형 키로 굽기" + msgid "" "This animation belongs to an imported scene, so changes to imported tracks " "will not be saved.\n" @@ -971,6 +988,22 @@ msgstr "" "면,\n" "\"파일으로 저장\"과 \"커스텀 트랙 유지\"를 켜세요." +msgid "" +"Some AnimationPlayerEditor's options are disabled since this is the dummy " +"AnimationPlayer for preview.\n" +"\n" +"The dummy player is forced active, non-deterministic and doesn't have the " +"root motion track. Furthermore, the original node is inactive temporary." +msgstr "" +"미리보기용 더미 AnimationPlayer를 사용 중이기 때문에 AnimationPlayerEditor의 " +"일부 설정을 조작할 수 없습니다.\n" +"\n" +"더미 플레이어는 항상 활성화 상태이고, 비결정적이며, 루트 모션 트랙을 가지지 않" +"습니다. 또한 본래 노드는 임시적으로 비활성화됩니다." + +msgid "AnimationPlayer is inactive. The playback will not be processed." +msgstr "AnimationPlayer가 비활성 상태입니다. 애니메이션을 재생하지 않습니다." + msgid "Select an AnimationPlayer node to create and edit animations." msgstr "애니메이션을 만들고 편집하려면 AnimationPlayer노드를 선택하세요." @@ -980,6 +1013,18 @@ msgstr "가져온 씬" msgid "Warning: Editing imported animation" msgstr "경고: 가져온 애니메이션을 편집하고 있음" +msgid "Dummy Player" +msgstr "더미 플레이어" + +msgid "Warning: Editing dummy AnimationPlayer" +msgstr "경고: 더미 AnimationPlayer를 편집하고 있음" + +msgid "Inactive Player" +msgstr "비활성 플레이어" + +msgid "Warning: AnimationPlayer is inactive" +msgstr "경고: AnimationPlayer가 비활성 상태입니다" + msgid "Toggle between the bezier curve editor and track editor." msgstr "베지어 커브 편집기와 트랙 편집기 사이를 전환합니다." @@ -1017,7 +1062,7 @@ msgid "Scale From Cursor" msgstr "커서 위치에서 스케일 조절" msgid "Make Easing Selection" -msgstr "완화 선택 만들기" +msgstr "선택된 구간에 이징 만들기" msgid "Duplicate Selection" msgstr "선택 항목 복제" @@ -1089,7 +1134,7 @@ msgid "Scale Ratio:" msgstr "스케일 비율:" msgid "Select Transition and Easing" -msgstr "전환 및 완화 선택" +msgstr "전환 및 이징 선택" msgctxt "Transition Type" msgid "Linear" @@ -1229,6 +1274,14 @@ msgstr "모두 바꾸기" msgid "Selection Only" msgstr "선택 영역만" +msgctxt "Indentation" +msgid "Spaces" +msgstr "스페이스" + +msgctxt "Indentation" +msgid "Tabs" +msgstr "탭" + msgid "Toggle Scripts Panel" msgstr "스크립트 패널 토글" @@ -1641,6 +1694,9 @@ msgstr "정지" msgid "Continue" msgstr "계속" +msgid "Thread:" +msgstr "스레드:" + msgid "Stack Frames" msgstr "스택 프레임" @@ -1819,6 +1875,12 @@ msgstr "폴더 이름은 비워둘 수 없습니다." msgid "Folder name contains invalid characters." msgstr "폴더 이름에 잘못된 문자가 있습니다." +msgid "Folder name cannot begin or end with a space." +msgstr "폴더 이름의 시작이나 끝에 공백 문자를 넣을 수 없습니다." + +msgid "Folder name cannot begin with a dot." +msgstr "폴더 이름은 점으로 시작할 수 없습니다." + msgid "File with that name already exists." msgstr "이 이름으로 된 파일이 이미 있습니다." @@ -1866,6 +1928,9 @@ msgstr "개발자" msgid "Authors" msgstr "저자" +msgid "Patrons" +msgstr "후원자" + msgid "Platinum Sponsors" msgstr "플래티넘 스폰서" @@ -1875,6 +1940,18 @@ msgstr "골드 스폰서" msgid "Silver Sponsors" msgstr "실버 스폰서" +msgid "Diamond Members" +msgstr "다이아몬드 멤버" + +msgid "Titanium Members" +msgstr "티타늄 멤버" + +msgid "Platinum Members" +msgstr "플래티넘 멤버" + +msgid "Gold Members" +msgstr "골드 멤버" + msgid "Donors" msgstr "기부자" @@ -1909,6 +1986,19 @@ msgstr "\"%s\"에 대한 애셋 파일을 여는 중 오류 (ZIP 형식이 아 msgid "%s (already exists)" msgstr "%s (이미 있습니다)" +msgid "%d file conflicts with your project and won't be installed" +msgid_plural "%d files conflict with your project and won't be installed" +msgstr[0] "%d개 파일이 프로젝트와 충돌하기 때문에 설치되지 않습니다" + +msgid "This asset doesn't have a root directory, so it can't be ignored." +msgstr "이 에셋에는 루트 디렉터리가 없기 때문에 무시할 수 없습니다." + +msgid "Ignore the root directory when extracting files." +msgstr "파일을 압축 해제할 때 루트 디렉터리를 무시합니다." + +msgid "Select Install Folder" +msgstr "설치 폴더 선택" + msgid "Uncompressing Assets" msgstr "애셋 압축 풀기" @@ -1924,6 +2014,37 @@ msgstr "애셋 \"%s\"를 성공적으로 설치했습니다!" msgid "Success!" msgstr "성공!" +msgid "Asset:" +msgstr "에셋:" + +msgid "Open the list of the asset contents and select which files to install." +msgstr "에셋의 파일 목록을 열고 그 중 설치할 파일들을 선택합니다." + +msgid "Change Install Folder" +msgstr "설치 폴더 변경" + +msgid "" +"Change the folder where the contents of the asset are going to be installed." +msgstr "에셋의 파일들을 설치할 폴더를 변경합니다." + +msgid "Ignore asset root" +msgstr "에셋 루트 무시" + +msgid "No files conflict with your project" +msgstr "프로젝트와 충돌하는 파일이 없습니다" + +msgid "Show contents of the asset and conflicting files." +msgstr "에셋과 충돌하는 파일의 내용을 봅니다." + +msgid "Contents of the asset:" +msgstr "에셋의 내용:" + +msgid "Installation preview:" +msgstr "설치 미리보기:" + +msgid "Configure Asset Before Installing" +msgstr "설치 전 에셋 설정" + msgid "Install" msgstr "설치" @@ -2291,6 +2412,13 @@ msgstr "감지 시 강제 클래스:" msgid "Edit Build Configuration Profile" msgstr "빌드 설정 프로필 편집" +msgid "" +"Failed to execute command \"%s\":\n" +"%s." +msgstr "" +"명령 \"%s\"를 실행할 수 없습니다:\n" +"%s." + msgid "Filter Commands" msgstr "명령어 필터" @@ -2516,6 +2644,36 @@ msgstr "" msgid "Error codes returned:" msgstr "반환된 오류 코드:" +msgid "There is currently no description for this method." +msgstr "현재 이 메서드에 대한 설명이 없습니다." + +msgid "There is currently no description for this constructor." +msgstr "현재 이 생성자에 대한 설명이 없습니다." + +msgid "There is currently no description for this operator." +msgstr "현재 이 연산자에 대한 설명이 없습니다." + +msgid "" +"There is currently no description for this method. Please help us by " +"[color=$color][url=$url]contributing one[/url][/color]!" +msgstr "" +"현재 이 메서드의 설명이 없습니다. [color=$color][url=$url]관련 정보를 기여하여" +"[/url][/color] 개선할 수 있도록 도와주세요!" + +msgid "" +"There is currently no description for this constructor. Please help us by " +"[color=$color][url=$url]contributing one[/url][/color]!" +msgstr "" +"현재 이 생성자의 설명이 없습니다. [color=$color][url=$url]관련 정보를 기여하여" +"[/url][/color] 개선할 수 있도록 도와주세요!" + +msgid "" +"There is currently no description for this operator. Please help us by " +"[color=$color][url=$url]contributing one[/url][/color]!" +msgstr "" +"현재 이 연산자의 설명이 없습니다. [color=$color][url=$url]관련 정보를 기여하여" +"[/url][/color] 개선할 수 있도록 도와주세요!" + msgid "Top" msgstr "위쪽" @@ -2554,6 +2712,16 @@ msgstr "" "현재 이 클래스의 설명이 없습니다. [color=$color][url=$url]관련 정보를 기여하여" "[/url][/color] 개선할 수 있도록 도와주세요!" +msgid "Note:" +msgstr "노트:" + +msgid "" +"There are notable differences when using this API with C#. See [url=%s]C# API " +"differences to GDScript[/url] for more information." +msgstr "" +"이 API는 C#에서 사용될 때 큰 차이가 있습니다. 자세한 내용은 [url=%s]C# API와 " +"GDScript의 차이점[/url]을 참조하십시오." + msgid "Online Tutorials" msgstr "온라인 튜토리얼" @@ -2637,12 +2805,24 @@ msgstr "메서드 설명" msgid "Operator Descriptions" msgstr "연산자 설명" +msgid "Metadata:" +msgstr "메타데이터:" + msgid "Property:" msgstr "속성:" +msgid "Method:" +msgstr "메서드:" + msgid "Signal:" msgstr "시그널:" +msgid "Theme Item:" +msgstr "테마 항목:" + +msgid "No description available." +msgstr "사용 가능한 설명이 없습니다." + msgid "%d match." msgstr "%d개 일치." @@ -2744,6 +2924,12 @@ msgid "Move element %d to position %d in property array with prefix %s." msgstr "" "%d번 요소의 위치를 %d번으로, 접두어 %s를 가진 속성 배열 내에서 이동합니다." +msgid "Clear Property Array with Prefix %s" +msgstr "접두어 %s를 가진 속성 배열 비우기" + +msgid "Resize Property Array with Prefix %s" +msgstr "접두어 %s를 가진 속성 배열 크기 변경" + msgid "Element %d: %s%d*" msgstr "요소 %d: %s%d*" @@ -2783,6 +2969,9 @@ msgstr "메타데이터 추가" msgid "Set %s" msgstr "Set %s" +msgid "Set Multiple: %s" +msgstr "다수 변경: %s" + msgid "Remove metadata %s" msgstr "메타데이터 %s 제거" @@ -3231,17 +3420,6 @@ msgstr "" "추가 오류를 방지하려면 '%s'에서 애드온을 비활성화하세요." msgid "" -"Unable to load addon script from path: '%s' Base type is not EditorPlugin." -msgstr "" -"다음 경로에서 애드온 스크립트를 불러올 수 없음: '%s' 기본 타입이 EditorPlugin" -"이 아닙니다." - -msgid "Unable to load addon script from path: '%s' Script is not in tool mode." -msgstr "" -"다음 경로에서 애드온 스크립트를 불러올 수 없음: '%s' 스크립트가 tool 모드가 아" -"닙니다." - -msgid "" "Scene '%s' was automatically imported, so it can't be modified.\n" "To make changes to it, a new inherited scene can be created." msgstr "" @@ -3300,9 +3478,34 @@ msgstr "레이아웃 삭제" msgid "Default" msgstr "기본값" +msgid "This scene was never saved." +msgstr "이 씬은 저장되지 않았습니다." + +msgid "%d second ago" +msgid_plural "%d seconds ago" +msgstr[0] "%d초 전" + +msgid "%d minute ago" +msgid_plural "%d minutes ago" +msgstr[0] "%d분 전" + +msgid "%d hour ago" +msgid_plural "%d hours ago" +msgstr[0] "%d시간 전" + +msgid "" +"Scene \"%s\" has unsaved changes.\n" +"Last saved: %s." +msgstr "" +"씬 \"%s\"에 저장되지 않은 변경 사항이 있습니다.\n" +"마지막 저장: %s." + msgid "Save & Close" msgstr "저장 & 닫기" +msgid "Save before closing?" +msgstr "닫기 전에 저장합니까?" + msgid "%d more files or folders" msgstr "외 %d개의 파일 또는 폴더" @@ -3413,6 +3616,9 @@ msgstr "툴" msgid "Orphan Resource Explorer..." msgstr "미사용 리소스 탐색기..." +msgid "Upgrade Mesh Surfaces..." +msgstr "메시 표면 업그레이드..." + msgid "Reload Current Project" msgstr "현재 프로젝트 새로고침" @@ -3607,6 +3813,12 @@ msgstr "새로고침" msgid "Resave" msgstr "다시 저장" +msgid "Create Version Control Metadata..." +msgstr "버전 관리 메타데이터 생성..." + +msgid "Version Control Settings..." +msgstr "버전 관리 설정..." + msgid "New Inherited" msgstr "새 상속 씬" @@ -3715,6 +3927,12 @@ msgstr "" msgid "Assign..." msgstr "지정..." +msgid "Copy as Text" +msgstr "텍스트로 복사" + +msgid "Show Node in Tree" +msgstr "트리에 노드 표시" + msgid "Invalid RID" msgstr "잘못된 RID" @@ -3810,6 +4028,9 @@ msgstr "파일시스템에서 보기" msgid "Convert to %s" msgstr "%s(으)로 변환" +msgid "Select resources to make unique:" +msgstr "유일하게 만들 리소스를 선택하세요:" + msgid "New %s" msgstr "새 %s" @@ -3858,6 +4079,12 @@ msgstr "실행 취소: %s" msgid "Redo: %s" msgstr "다시 실행: %s" +msgid "Edit Built-in Action: %s" +msgstr "내장 액션 편집: %s" + +msgid "Edit Shortcut: %s" +msgstr "단축키 편집: %s" + msgid "Common" msgstr "일반" @@ -4041,6 +4268,9 @@ msgstr "PCK 임베딩" msgid "On 32-bit exports the embedded PCK cannot be bigger than 4 GiB." msgstr "32비트 환경에서는 4 GiB보다 큰 내장 PCK를 내보낼 수 없습니다." +msgid "Plugin \"%s\" is not supported on \"%s\"" +msgstr "플러그인 \"%s\"는 \"%s\"에서는 지원되지 않습니다" + msgid "Open the folder containing these templates." msgstr "이 템플릿이 포함된 폴더를 엽니다." @@ -4237,6 +4467,15 @@ msgstr "" "템플릿이 다운로드를 계속할 것입니다.\n" "완료되면 에디터가 짧게 멈추는 현상을 겪을 수 있습니다." +msgid "" +"Target platform requires '%s' texture compression. Enable 'Import %s' to fix." +msgstr "" +"대상 플랫폼에서 '%s' 텍스처 압축이 필요합니다. 프로젝트 설정에서 '%s 가져오" +"기' 설정을 활성화하세요." + +msgid "Fix Import" +msgstr "가져오기 고치기" + msgid "Runnable" msgstr "실행가능" @@ -4254,6 +4493,9 @@ msgstr "'%s' 프리셋을 삭제하시겠습니까?" msgid "Resources to exclude:" msgstr "제외하는 리소스:" +msgid "Resources to override export behavior:" +msgstr "내보내기 동작을 덮어쓸 리소스:" + msgid "Resources to export:" msgstr "내보내는 리소스:" @@ -4501,6 +4743,9 @@ msgstr "%s 에 리소스를 저장하지 못했습니다: %s" msgid "Failed to load resource at %s: %s" msgstr "%s 에서 리소스를 불러오지 못했습니다: %s" +msgid "Unable to update dependencies for:" +msgstr "종속성을 업데이트할 수 없음:" + msgid "" "This filename begins with a dot rendering the file invisible to the editor.\n" "If you want to rename it anyway, use your operating system's file manager." @@ -4521,6 +4766,9 @@ msgstr "" msgid "A file or folder with this name already exists." msgstr "이 이름은 이미 어떤 파일이나 폴더가 쓰고 있습니다." +msgid "Name begins with a dot." +msgstr "이름이 점으로 시작합니다." + msgid "" "The following files or folders conflict with items in the target location " "'%s':" @@ -4541,6 +4789,9 @@ msgstr "폴더 복제 중:" msgid "New Inherited Scene" msgstr "새 상속된 씬" +msgid "Set as Main Scene" +msgstr "메인 씬으로 설정" + msgid "Open Scenes" msgstr "씬 열기" @@ -4580,6 +4831,12 @@ msgstr "계층 구조 펼치기" msgid "Collapse Hierarchy" msgstr "계층 구조 접기" +msgid "Set Folder Color..." +msgstr "폴더 색깔 설정..." + +msgid "Default (Reset)" +msgstr "기본값 (리셋)" + msgid "Move/Duplicate To..." msgstr "다른 곳으로 이동/복제..." @@ -4646,6 +4903,33 @@ msgstr "이름 바꾸기..." msgid "Open in External Program" msgstr "다른 프로그램에서 열기" +msgid "Red" +msgstr "붉은색" + +msgid "Orange" +msgstr "주황색" + +msgid "Yellow" +msgstr "노란색" + +msgid "Green" +msgstr "초록색" + +msgid "Teal" +msgstr "하늘색" + +msgid "Blue" +msgstr "파란색" + +msgid "Purple" +msgstr "보라색" + +msgid "Pink" +msgstr "분홍색" + +msgid "Gray" +msgstr "회색" + msgid "Go to previous selected folder/file." msgstr "이전 파일/폴더로 이동합니다." @@ -4655,6 +4939,9 @@ msgstr "다음 파일/폴더로 이동합니다." msgid "Re-Scan Filesystem" msgstr "파일시스템 다시 스캔" +msgid "Change Split Mode" +msgstr "분할 모드 변경" + msgid "Filter Files" msgstr "파일 필터" @@ -5086,9 +5373,15 @@ msgstr "씬 트리 (노드):" msgid "Node Configuration Warning!" msgstr "노드 설정 경고!" +msgid "Allowed:" +msgstr "허용값:" + msgid "Select a Node" msgstr "노드를 선택하세요" +msgid "Show All" +msgstr "모두 보기" + msgid "The Beginning" msgstr "시작점" @@ -5435,6 +5728,9 @@ msgstr "메쉬" msgid "Materials" msgstr "머티리얼" +msgid "Selected Animation Play/Pause" +msgstr "선택된 애니메이션 재생/정지" + msgid "Save Extension:" msgstr "저장할 확장자:" @@ -5486,6 +5782,20 @@ msgid "Advanced..." msgstr "고급..." msgid "" +"The imported resource is currently loaded. All instances will be replaced and " +"undo history will be cleared." +msgstr "" +"불러온 리소스가 이미 로드되어 있습니다. 모든 인스턴스를 교환한 뒤 되돌리기 내" +"역을 비웁니다." + +msgid "" +"WARNING: Assets exist that use this resource. They may stop loading properly " +"after changing type." +msgstr "" +"경고: 이 리소스를 사용하는 에셋이 있습니다. 타입을 변경한 후에는 정상적으로 불" +"러오지 못할 수도 있습니다." + +msgid "" "Select a resource file in the filesystem or in the inspector to adjust import " "settings." msgstr "" @@ -5716,21 +6026,74 @@ msgstr "업데이트" msgid "Plugin Name:" msgstr "플러그인 이름:" +msgid "Required. This name will be displayed in the list of plugins." +msgstr "필수. 플러그인 목록에 표시할 이름입니다." + msgid "Subfolder:" msgstr "하위 폴더:" +msgid "" +"Optional. The folder name should generally use `snake_case` naming (avoid " +"spaces and special characters).\n" +"If left empty, the folder will be named after the plugin name converted to " +"`snake_case`." +msgstr "" +"선택. 폴더 이름에는 보통 스네이크 표기법( `snake_case` ) 을 사용하며 띄어쓰기" +"나 특수문자 사용은 피합니다.\n" +"공란일 경우, 폴더 이름으로는 스네이크 표기법으로 치환된 플러그인 이름을 사용합" +"니다." + +msgid "" +"Optional. This description should be kept relatively short (up to 5 lines).\n" +"It will display when hovering the plugin in the list of plugins." +msgstr "" +"선택. 설명은 최대 5줄 내로 간결하게 작성하여야 합니다.\n" +"이 설명은 플러그인 목록에서 마우스를 가져다댈 때 보여집니다." + msgid "Author:" msgstr "저자:" +msgid "Optional. The author's username, full name, or organization name." +msgstr "선택. 제작자의 닉네임이나 풀 네임 또는 만든 조직의 이름입니다." + msgid "Version:" msgstr "버전:" +msgid "" +"Optional. A human-readable version identifier used for informational purposes " +"only." +msgstr "선택. 사람이 읽을 수 있는 버전 식별자입니다. 설명 용도로만 사용됩니다." + +msgid "" +"Required. The scripting language to use for the script.\n" +"Note that a plugin may use several languages at once by adding more scripts " +"to the plugin." +msgstr "" +"필수. 스크립트에 사용할 스크립팅 언어입니다.\n" +"플러그인에 여러 스크립트를 추가하여 여러 언어를 사용할 수도 있습니다." + msgid "Script Name:" msgstr "스크립트 이름:" +msgid "" +"Optional. The path to the script (relative to the add-on folder). If left " +"empty, will default to \"plugin.gd\"." +msgstr "" +"선택. 스크립트의 (애드온 폴더로부터 상대적인) 경로입니다. 공란일 경우 기본값" +"인 \"plugin.gd\"를 사용합니다." + msgid "Activate now?" msgstr "지금 실행할까요?" +msgid "Plugin name is valid." +msgstr "플러그인 이름이 올바릅니다." + +msgid "Script extension is valid." +msgstr "스크립트 확장이 올바릅니다." + +msgid "Subfolder name is valid." +msgstr "서브폴더 이름이 올바릅니다." + msgid "Create Polygon" msgstr "폴리곤 만들기" @@ -5862,6 +6225,9 @@ msgstr "점과 삼각형 지우기." msgid "Generate blend triangles automatically (instead of manually)" msgstr "(수동 대신) 자동으로 혼합 삼각형 만들기" +msgid "Parameter Changed: %s" +msgstr "매개변수 변경됨: %s" + msgid "Inspect Filters" msgstr "필터 인스펙트" @@ -6011,6 +6377,9 @@ msgstr "애니메이션 유일하게 만들기: %s" msgid "Invalid AnimationLibrary file." msgstr "잘못된 AnimationLibrary 파일입니다." +msgid "This library is already added to the mixer." +msgstr "이 라이브러리는 이미 믹서에 추가되었습니다." + msgid "Invalid Animation file." msgstr "잘못된 애니메이션 파일입니다." @@ -6140,6 +6509,9 @@ msgstr "[전역] (생성)" msgid "Duplicated Animation Name:" msgstr "복제된 애니메이션 이름:" +msgid "Onion skinning requires a RESET animation." +msgstr "어니언 스키닝을 사용하려면 RESET 애니메이션이 필요합니다." + msgid "Play selected animation backwards from current pos. (A)" msgstr "선택한 애니메이션을 현재 위치에서 거꾸로 재생합니다. (A)" @@ -6236,6 +6608,12 @@ msgstr "노드 이동" msgid "Transition exists!" msgstr "전환이 있습니다!" +msgid "Play/Travel to %s" +msgstr "재생/이동 %s" + +msgid "Edit %s" +msgstr "편집 %s" + msgid "Add Node and Transition" msgstr "노드와 전환 추가" @@ -6520,6 +6898,11 @@ msgstr "회전 단계:" msgid "Scale Step:" msgstr "스케일 단계:" +msgid "" +"Children of a container get their position and size determined only by their " +"parent." +msgstr "컨테이너 노드의 자식은 부모에 의해서만 위치와 크기가 결정됩니다." + msgid "Move Node(s) to Position" msgstr "노드를 위치로 이동" @@ -6580,6 +6963,12 @@ msgstr "잠김" msgid "Grouped" msgstr "그룹됨" +msgid "Add Node Here..." +msgstr "여기에 노드 추가..." + +msgid "Instantiate Scene Here..." +msgstr "여기에 씬 인스턴스화..." + msgid "Paste Node(s) Here" msgstr "여기에 노드 붙여넣기" @@ -6855,6 +7244,18 @@ msgstr "프레임 선택" msgid "Preview Canvas Scale" msgstr "캔버스 스케일 미리보기" +msgid "Project theme" +msgstr "프로젝트 테마" + +msgid "Editor theme" +msgstr "에디터 테마" + +msgid "Default theme" +msgstr "기본 테마" + +msgid "Preview Theme" +msgstr "미리보기 테마" + msgid "Translation mask for inserting keys." msgstr "키를 삽입하기 위한 전환 마스크." @@ -6905,6 +7306,9 @@ msgstr "%s 추가하는 중..." msgid "Drag and drop to add as child of current scene's root node." msgstr "드래그 & 드롭하면 현재 씬의 루트 노드의 자식으로서 노드를 추가합니다." +msgid "Hold %s when dropping to add as child of selected node." +msgstr "%s을 누른 채 드롭하면 현재 선택된 노드의 자식으로서 노드를 추가합니다." + msgid "Hold Shift when dropping to add as sibling of selected node." msgstr "Shift를 누른 채 드롭하면 선택된 노드의 형제로서 노드를 추가합니다." @@ -7075,9 +7479,15 @@ msgstr "가로 방향 정렬" msgid "Vertical alignment" msgstr "세로 방향 정렬" +msgid "Convert to GPUParticles3D" +msgstr "GPUParticles3D로 변환" + msgid "Load Emission Mask" msgstr "방출 마스크 불러오기" +msgid "Convert to GPUParticles2D" +msgstr "GPUParticles2D로 변환" + msgid "CPUParticles2D" msgstr "CPUParticles2D" @@ -7096,6 +7506,12 @@ msgstr "테두리 픽셀" msgid "Directed Border Pixels" msgstr "방향성이 있는 테두리 픽셀" +msgid "Centered" +msgstr "중앙" + +msgid "Capture Colors from Pixel" +msgstr "픽셀에서 색상 캡처" + msgid "CPUParticles3D" msgstr "CPUParticles3D" @@ -7218,6 +7634,18 @@ msgstr "" "이 설정이 활성화되면, 프로젝트를 실행하는 동안 어보이던스 오브젝트의 모양, 직" "경과 속도가 보이게 됩니다." +msgid "Debug CanvasItem Redraws" +msgstr "CanvasItem 리드로우를 디버그" + +msgid "" +"When this option is enabled, redraw requests of 2D objects will become " +"visible (as a short flash) in the running project.\n" +"This is useful to troubleshoot low processor mode." +msgstr "" +"이 설정이 활성화되면, 프로젝트 내에서 2D 오브젝트의 다시 그리기 요청이 (짧은 " +"섬광으로) 시각화됩니다.\n" +"이 설정은 로우 프로세서 모드에서의 문제 해결에 도움을 줄 수도 있습니다." + msgid "Synchronize Scene Changes" msgstr "씬 변경사항 동기화" @@ -7360,6 +7788,9 @@ msgstr "끝 위치 바꾸기" msgid "Change Probe Size" msgstr "프로브 크기 바꾸기" +msgid "Change Probe Origin Offset" +msgstr "프로브 원점 오프셋 바꾸기" + msgid "Change Notifier AABB" msgstr "알림이 AABB 바꾸기" @@ -7469,6 +7900,24 @@ msgstr "" msgid "Select path for SDF Texture" msgstr "SDF 텍스처의 경로를 선택하세요" +msgid "Add Gradient Point" +msgstr "그라디언트 포인트 추가" + +msgid "Remove Gradient Point" +msgstr "그라디언트 포인트 제거" + +msgid "Move Gradient Point" +msgstr "그라디언트 포인트 이동" + +msgid "Recolor Gradient Point" +msgstr "그라디언트 포인트 색 바꾸기" + +msgid "Reverse Gradient" +msgstr "그라디언트 뒤집기" + +msgid "Reverse/Mirror Gradient" +msgstr "그라디언트 돌리기/뒤집기" + msgid "Move GradientTexture2D Fill Point" msgstr "GradientTexture2D 채우기 포인트 교체" @@ -7507,6 +7956,9 @@ msgstr "에디터 씬의 루트를 찾을 수 없습니다." msgid "Lightmap data is not local to the scene." msgstr "라이트맵 데이터가 씬에 로컬이 아닙니다." +msgid "Maximum texture size is too small for the lightmap images." +msgstr "최대 텍스처 사이즈가 라이트맵 이미지에 사용하기에는 너무 작습니다." + msgid "Bake Lightmaps" msgstr "라이트맵 굽기" @@ -7558,6 +8010,9 @@ msgstr "내비게이션 메시 만들기" msgid "Create Debug Tangents" msgstr "디버그 접선 생성" +msgid "No mesh to unwrap." +msgstr "언랩할 메시가 없습니다." + msgid "" "Mesh cannot unwrap UVs because it does not belong to the edited scene. Make " "it unique first." @@ -7585,6 +8040,15 @@ msgstr "UV2 펼치기" msgid "Contained Mesh is not of type ArrayMesh." msgstr "포함된 메시가 ArrayMesh 타입이 아닙니다." +msgid "Can't unwrap mesh with blend shapes." +msgstr "메시를 블렌드 셰이프로 언랩할 수 없습니다." + +msgid "Only triangles are supported for lightmap unwrap." +msgstr "라이트맵 언랩에는 삼각형만 사용할 수 있습니다." + +msgid "Normals are required for lightmap unwrap." +msgstr "라이트맵을 언랩하기 위해서는 노멀이 필요합니다." + msgid "UV Unwrap failed, mesh may not be manifold?" msgstr "UV 펼치기를 실패했습니다. 메시가 다양한 것 같은데요?" @@ -7827,6 +8291,27 @@ msgstr "폴리곤 편집 (점 제거)" msgid "Create Navigation Polygon" msgstr "내비게이션 폴리곤 만들기" +msgid "Bake NavigationPolygon" +msgstr "NavigationPolygon 굽기" + +msgid "" +"Bakes the NavigationPolygon by first parsing the scene for source geometry " +"and then creating the navigation polygon vertices and polygons." +msgstr "" +"씬의 지형을 분석한 뒤, 내비게이션 폴리곤 버텍스 및 폴리곤을 만들어서 " +"NavigationPolygon을 굽습니다." + +msgid "Clear NavigationPolygon" +msgstr "NavigationPolygon 모두 제거" + +msgid "Clears the internal NavigationPolygon outlines, vertices and polygons." +msgstr "내부 NavigationPolygon의 아웃라인, 버텍스, 폴리곤을 모두 비웁니다." + +msgid "" +"A NavigationPolygon resource must be set or created for this node to work." +msgstr "" +"이 노드가 작동하려면 NavigationPolygon 리소스를 설정하거나 만들어야 합니다." + msgid "Unnamed Gizmo" msgstr "이름 없는 기즈모" @@ -7969,11 +8454,20 @@ msgid "Translate" msgstr "옮기기" msgid "Translating:" -msgstr "번역 중:" +msgstr "이동 중:" msgid "Rotating %s degrees." msgstr "%s도로 회전." +msgid "Translating %s." +msgstr "%s 이동 중." + +msgid "Rotating %f degrees." +msgstr "%f 도 회전 중." + +msgid "Scaling %s." +msgstr "%s 스케일 중." + msgid "Auto Orthogonal Enabled" msgstr "자동 직교 활성화" @@ -8058,6 +8552,9 @@ msgstr "오클루전 컬링 버퍼" msgid "Motion Vectors" msgstr "모션 벡터" +msgid "Internal Buffer" +msgstr "내부 버퍼" + msgid "Display Advanced..." msgstr "자세히 표시..." @@ -8155,6 +8652,13 @@ msgstr "더욱 확대하려면, 카메라의 클리핑 평면을 바꾸세요 ( msgid "Overriding material..." msgstr "머티리얼 오버라이드..." +msgid "" +"Drag and drop to override the material of any geometry node.\n" +"Hold %s when dropping to override a specific surface." +msgstr "" +"드래그 & 드롭으로 아무 지오메트리 노드의 머티리얼을 오버라이드합니다.\n" +"%s을 누르며 드롭하여 특정한 면을 오버라이드합니다." + msgid "XForm Dialog" msgstr "XForm 대화 상자" @@ -8526,6 +9030,15 @@ msgstr "핸들 길이 거울" msgid "Curve Point #" msgstr "곡선 점 #" +msgid "Handle In #" +msgstr "핸들 인 #" + +msgid "Handle Out #" +msgstr "핸들 아웃 #" + +msgid "Handle Tilt #" +msgstr "핸들 틸트 #" + msgid "Set Curve Point Position" msgstr "곡선 점 위치 설정" @@ -8535,12 +9048,24 @@ msgstr "곡선의 아웃 위치 설정" msgid "Set Curve In Position" msgstr "곡선의 인 위치 설정" +msgid "Set Curve Point Tilt" +msgstr "곡선 점 틸트 설정" + msgid "Split Path" msgstr "경로 가르기" msgid "Remove Path Point" msgstr "경로 점 제거" +msgid "Reset Out-Control Point" +msgstr "아웃-컨트롤 점 초기화" + +msgid "Reset In-Control Point" +msgstr "인-컨트롤 점 초기화" + +msgid "Reset Point Tilt" +msgstr "점 틸트 초기화" + msgid "Split Segment (in curve)" msgstr "(곡선에서) 세그먼트 가르기" @@ -8618,9 +9143,15 @@ msgstr "본" msgid "Move Points" msgstr "점 이동" +msgid ": Rotate" +msgstr ": 회전" + msgid "Shift: Move All" msgstr "Shift: 모두 이동" +msgid "Shift: Scale" +msgstr "Shift: 스케일 조절" + msgid "Move Polygon" msgstr "폴리곤 이동" @@ -8711,6 +9242,15 @@ msgstr "리소스 붙여넣기" msgid "Load Resource" msgstr "리소스 불러오기" +msgid "Path to AnimationMixer is invalid" +msgstr "AnimationMixer를 향하는 경로가 잘못되었습니다" + +msgid "" +"AnimationMixer has no valid root node path, so unable to retrieve track names." +msgstr "" +"AnimationMixer의 루트 노드 경로가 유효하지 않으므로 트랙 이름을 받아올 수 없습" +"니다." + msgid "Clear Recent Files" msgstr "최근 파일 지우기" @@ -8874,6 +9414,9 @@ msgstr "" msgid "Search Results" msgstr "검색 결과" +msgid "Save changes to the following script(s) before quitting?" +msgstr "종료하기 전에 해당 스크립트의 변경사항을 저장하시겠습니까?" + msgid "Clear Recent Scripts" msgstr "최근 스크립트 지우기" @@ -8964,6 +9507,9 @@ msgstr "행 접기/펼치기" msgid "Fold All Lines" msgstr "모든 행 접기" +msgid "Create Code Region" +msgstr "코드 영역 생성" + msgid "Unfold All Lines" msgstr "모든 행 펼치기" @@ -9024,6 +9570,9 @@ msgstr "다음 중단점으로 이동" msgid "Go to Previous Breakpoint" msgstr "이전 중단점으로 이동" +msgid "Save changes to the following shaders(s) before quitting?" +msgstr "종료하기 전에 해당 셰이더의 변경사항을 저장하시겠습니까?" + msgid "Shader Editor" msgstr "셰이더 에디터" @@ -12778,9 +13327,24 @@ msgstr "%s의 <이름 없음>" msgid "(used %d times)" msgstr "(%d회 사용됨)" +msgid "Add Child Node..." +msgstr "자식 노드 추가..." + +msgid "Instantiate Child Scene..." +msgstr "자식 씬 인스턴스화..." + msgid "Expand/Collapse Branch" msgstr "하위 항목 펼치기/접기" +msgid "Change Type..." +msgstr "타입 바꾸기..." + +msgid "Attach Script..." +msgstr "스크립트 붙이기..." + +msgid "Extend Script..." +msgstr "스크립트 상속..." + msgid "Reparent to New Node" msgstr "새 노드에 부모 노드 다시 지정" @@ -12802,6 +13366,9 @@ msgid "" msgstr "" "씬 파일을 노드로 인스턴스화합니다. 루트 노드가 없으면 상속된 씬을 만듭니다." +msgid "Filter: name, t:type, g:group" +msgstr "필터: 이름, t:타입, g:그룹" + msgid "Attach a new or existing script to the selected node." msgstr "선택한 노드에 새 스크립트나 기존 스크립트를 붙입니다." @@ -13022,6 +13589,9 @@ msgid "Invalid type argument to convert(), use TYPE_* constants." msgstr "" "convert() 메서드의 인수 타입이 올바르지 않습니다. TYPE_* 상수를 사용하세요." +msgid "Cannot resize array." +msgstr "배열 크기를 변경할 수 없습니다." + msgid "Step argument is zero!" msgstr "스텝 인수가 0입니다!" diff --git a/editor/translations/editor/lv.po b/editor/translations/editor/lv.po index 3d1e21086f..70b5bc9c40 100644 --- a/editor/translations/editor/lv.po +++ b/editor/translations/editor/lv.po @@ -2038,15 +2038,6 @@ msgstr "" "Spraudnis '%s' atspējots, lai novērstu tupmākās kļūdas." msgid "" -"Unable to load addon script from path: '%s' Base type is not EditorPlugin." -msgstr "" -"Nevar ielādēt papildinājuma skriptu no ceļa: '%s' Bāzes tips nav EditorPlugin." - -msgid "Unable to load addon script from path: '%s' Script is not in tool mode." -msgstr "" -"Nevarēja ielādēt spraudņa skriptu no mapes: '%s' Skripts nav rīka režīmā." - -msgid "" "Scene '%s' was automatically imported, so it can't be modified.\n" "To make changes to it, a new inherited scene can be created." msgstr "" diff --git a/editor/translations/editor/ms.po b/editor/translations/editor/ms.po index 4a4edb5e3e..0019a5e3f6 100644 --- a/editor/translations/editor/ms.po +++ b/editor/translations/editor/ms.po @@ -1970,17 +1970,6 @@ msgstr "" "Menyahdayakan addon pada '%s' untuk mengelakkan ralat selanjutnya." msgid "" -"Unable to load addon script from path: '%s' Base type is not EditorPlugin." -msgstr "" -"Tidak dapat memuatkan skrip addon dari laluan: '%s' Jenis Asas bukan " -"EditorPlugin." - -msgid "Unable to load addon script from path: '%s' Script is not in tool mode." -msgstr "" -"Tidak dapat memuat skrip addon dari laluan: Skrip '%s' tidak berada dalam mod " -"alat." - -msgid "" "Scene '%s' was automatically imported, so it can't be modified.\n" "To make changes to it, a new inherited scene can be created." msgstr "" diff --git a/editor/translations/editor/nb.po b/editor/translations/editor/nb.po index 9b8f4e8b94..1f2c42820d 100644 --- a/editor/translations/editor/nb.po +++ b/editor/translations/editor/nb.po @@ -1628,16 +1628,6 @@ msgid "Unable to load addon script from path: '%s'." msgstr "Kan ikke laste addon-skript fra bane: '%s'." msgid "" -"Unable to load addon script from path: '%s' Base type is not EditorPlugin." -msgstr "" -"Kan ikke laste addon-skript fra stien: Basistypen '%s' er ikke en " -"EditorPlugin." - -msgid "Unable to load addon script from path: '%s' Script is not in tool mode." -msgstr "" -"Kunne ikke laste tillegsskript fra sti: '%s' Script er ikke i verktøymodus." - -msgid "" "Scene '%s' was automatically imported, so it can't be modified.\n" "To make changes to it, a new inherited scene can be created." msgstr "" diff --git a/editor/translations/editor/nl.po b/editor/translations/editor/nl.po index 2d8e6138fd..e99e6524cc 100644 --- a/editor/translations/editor/nl.po +++ b/editor/translations/editor/nl.po @@ -2709,15 +2709,6 @@ msgstr "" "Schakel de extra uit op '%s' om toekomstige fouten te vermijden." msgid "" -"Unable to load addon script from path: '%s' Base type is not EditorPlugin." -msgstr "" -"Volgend script kon niet geladen worden: '%' Basistype is niet EditorPlugin." - -msgid "Unable to load addon script from path: '%s' Script is not in tool mode." -msgstr "" -"Uitbreidingsscript kon niet geladen worden: '%s' Script is niet in toolmodus." - -msgid "" "Scene '%s' was automatically imported, so it can't be modified.\n" "To make changes to it, a new inherited scene can be created." msgstr "" diff --git a/editor/translations/editor/pl.po b/editor/translations/editor/pl.po index dc381992ba..80f9bb2cf4 100644 --- a/editor/translations/editor/pl.po +++ b/editor/translations/editor/pl.po @@ -83,12 +83,13 @@ # BorysBe <b.bobulski@wp.pl>, 2023. # johnny1029 <jkste07@gmail.com>, 2023. # Marcin Zieliński <czolgista83@gmail.com>, 2023. +# Aleksander Łagowiec <mineolek10@users.noreply.hosted.weblate.org>, 2023. msgid "" msgstr "" "Project-Id-Version: Godot Engine editor interface\n" "Report-Msgid-Bugs-To: https://github.com/godotengine/godot\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2023-11-28 16:10+0000\n" +"PO-Revision-Date: 2023-12-11 21:00+0000\n" "Last-Translator: Tomek <kobewi4e@gmail.com>\n" "Language-Team: Polish <https://hosted.weblate.org/projects/godot-engine/godot/" "pl/>\n" @@ -804,7 +805,7 @@ msgid "Continuous" msgstr "Ciągłe" msgid "Discrete" -msgstr "Dyskretnie" +msgstr "Nieciągły" msgid "Capture" msgstr "Przechwyć" @@ -3474,15 +3475,15 @@ msgstr "" "Wyłączam dodatek \"%s\" by uniknąć dalszych błędów." msgid "" -"Unable to load addon script from path: '%s' Base type is not EditorPlugin." +"Unable to load addon script from path: '%s'. Base type is not 'EditorPlugin'." msgstr "" "Nie można wczytać skryptu dodatku ze ścieżki: \"%s\" Skrypt nie dziedziczy po " "klasie EditorPlugin." -msgid "Unable to load addon script from path: '%s' Script is not in tool mode." +msgid "Unable to load addon script from path: '%s'. Script is not in tool mode." msgstr "" -"Nie można załadować skryptu dodatku z ścieżki: \"%s\" Skrypt nie jest w " -"trybie narzędzia (tool)." +"Nie można wczytać skryptu dodatku z ścieżki: \"%s\" Skrypt nie jest w trybie " +"narzędzia (tool)." msgid "" "Scene '%s' was automatically imported, so it can't be modified.\n" @@ -14047,12 +14048,61 @@ msgstr "Nazwa \"%s\" jest zarezerwowanym słowem kluczowym języka shaderów." msgid "Add Shader Global Parameter" msgstr "Dodaj parametr globalny shadera" +msgid "" +"This project uses meshes with an outdated mesh format from previous Godot " +"versions. The engine needs to update the format in order to use those meshes. " +"Please use the 'Upgrade Mesh Surfaces' tool from the 'Project > Tools' menu. " +"You can ignore this message and keep using outdated meshes, but keep in mind " +"that this leads to increased load times every time you load the project." +msgstr "" +"Ten projekt używa siatki (meshes) z nieaktualnym formacie siatki (mesh) z " +"poprzedniej wersji Godot. Silnik potrzebuje zaktualizować format aby móc " +"używać tych siatek (meshes). Proszę użyj 'Zaktualizuj powierzchnie siatki' w " +"menu 'Projekt > Narzędzia'. Możesz zignorować tą wiadomość i wciąż używać " +"przestarzałych siatek ale pamiętaj że to wydłuża czas ładowania za każdym " +"razem kiedy otwierasz (ładujesz) projekt." + +msgid "" +"This project uses meshes with an outdated mesh format. Check the output log." +msgstr "Ten projekt używa przestarzałych formatów siatek. Sprawdź dziennik." + msgid "Upgrading All Meshes in Project" msgstr "Aktualizacja wszystkich siatek w projekcie" msgid "Attempting to re-save " msgstr "Próba ponownego zapisania " +msgid "Attempting to remove " +msgstr "Próba usunięcia " + +msgid "" +"The mesh format has changed in Godot 4.2, which affects both imported meshes " +"and meshes authored inside of Godot. The engine needs to update the format in " +"order to use those meshes.\n" +"\n" +"If your project predates Godot 4.2 and contains meshes, we recommend you run " +"this one time conversion tool. This update will restart the editor and may " +"take several minutes. Upgrading will make the meshes incompatible with " +"previous versions of Godot.\n" +"\n" +"You can still use your existing meshes as is. The engine will update each " +"mesh in memory, but the update will not be saved. Choosing this option will " +"lead to slower load times every time this project is loaded." +msgstr "" +"Format siatki uległ zmianie w Godot 4.2, co dotyczy zarówno siatek " +"importowanych, jak i siatek stworzonych w Godocie. Aby móc korzystać z tych " +"siatek, silnik musi zaktualizować format.\n" +"\n" +"Jeśli Twój projekt jest starszy niż Godot 4.2 i zawiera siatki, zalecamy " +"uruchomienie tego jednorazowego narzędzia do konwersji. Ta aktualizacja " +"spowoduje ponowne uruchomienie edytora i może zająć kilka minut. Aktualizacja " +"spowoduje, że siatki będą niekompatybilne z poprzednimi wersjami Godota.\n" +"\n" +"Nadal możesz używać istniejących siatek w niezmienionej postaci. Silnik " +"zaktualizuje każdą siatkę w pamięci, ale aktualizacja nie zostanie zapisana. " +"Wybranie tej opcji spowoduje wolniejsze wczytywanie przy każdym ładowaniu " +"tego projektu." + msgid "Restart & Upgrade" msgstr "Zrestartuj i zaktualizuj" @@ -14100,7 +14150,7 @@ msgid "Not based on a resource file" msgstr "Nie bazuje na pliku zasobów" msgid "Invalid instance dictionary format (missing @path)" -msgstr "Niepoprawna instancja formatu słownika (brakuje @path)" +msgstr "Niepoprawna/Nieważna instancja formatu słownika (brakuje @path)" msgid "Invalid instance dictionary format (can't load script at @path)" msgstr "" @@ -15742,8 +15792,8 @@ msgstr "" msgid "" "An occluder polygon must be set (or drawn) for this occluder to take effect." msgstr "" -"Wielokąt przesłaniający musi być ustawiony (lub narysowany), aby ten occluder " -"zadziałał." +"Wielokąt przesłaniający musi być ustawiony (lub narysowany), aby ten " +"\"occluder\" zadziałał." msgid "The occluder polygon for this occluder is empty. Please draw a polygon." msgstr "" diff --git a/editor/translations/editor/pt.po b/editor/translations/editor/pt.po index c3f8f89656..8888daf251 100644 --- a/editor/translations/editor/pt.po +++ b/editor/translations/editor/pt.po @@ -43,13 +43,14 @@ # Lucas Souza <lucasteisouza@gmail.com>, 2023. # André Luiz Santana Siqueira <hivosoft@outlook.com>, 2023. # gomakappa <gomaproi@outlook.com>, 2023. +# 100Nome <100nome.portugal@gmail.com>, 2023. msgid "" msgstr "" "Project-Id-Version: Godot Engine editor interface\n" "Report-Msgid-Bugs-To: https://github.com/godotengine/godot\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2023-10-20 09:04+0000\n" -"Last-Translator: gomakappa <gomaproi@outlook.com>\n" +"PO-Revision-Date: 2023-12-04 15:08+0000\n" +"Last-Translator: 100Nome <100nome.portugal@gmail.com>\n" "Language-Team: Portuguese <https://hosted.weblate.org/projects/godot-engine/" "godot/pt/>\n" "Language: pt\n" @@ -57,7 +58,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 5.1\n" +"X-Generator: Weblate 5.3-dev\n" msgid "Main Thread" msgstr "Linha Principal" @@ -3268,17 +3269,6 @@ msgstr "" "A desativar o addon em '%s' para prevenir mais erros." msgid "" -"Unable to load addon script from path: '%s' Base type is not EditorPlugin." -msgstr "" -"Incapaz de carregar script addon do Caminho: '%s' Tipo base não é " -"EditorPlugin." - -msgid "Unable to load addon script from path: '%s' Script is not in tool mode." -msgstr "" -"Incapaz de carregar script addon do Caminho: '%s' Script não está no modo " -"ferramenta." - -msgid "" "Scene '%s' was automatically imported, so it can't be modified.\n" "To make changes to it, a new inherited scene can be created." msgstr "" @@ -4640,7 +4630,7 @@ msgid "Resource..." msgstr "Recurso..." msgid "TextFile..." -msgstr "Fucheiro de Texto..." +msgstr "Ficheiro de Texto..." msgid "Expand Folder" msgstr "Expandir Pasta" diff --git a/editor/translations/editor/pt_BR.po b/editor/translations/editor/pt_BR.po index b7e474aee8..20693fc5d3 100644 --- a/editor/translations/editor/pt_BR.po +++ b/editor/translations/editor/pt_BR.po @@ -3468,17 +3468,6 @@ msgstr "" "Desativando o complemento em '%s' para evitar mais erros." msgid "" -"Unable to load addon script from path: '%s' Base type is not EditorPlugin." -msgstr "" -"Não foi possível carregar o script complementar do caminho: '%s' Tipo base " -"não é EditorPlugin." - -msgid "Unable to load addon script from path: '%s' Script is not in tool mode." -msgstr "" -"Não foi possível carregar o script complementar do caminho: '%s' Script não " -"está em modo ferramenta." - -msgid "" "Scene '%s' was automatically imported, so it can't be modified.\n" "To make changes to it, a new inherited scene can be created." msgstr "" diff --git a/editor/translations/editor/ro.po b/editor/translations/editor/ro.po index cbde238be5..f6fbdaa30d 100644 --- a/editor/translations/editor/ro.po +++ b/editor/translations/editor/ro.po @@ -1688,17 +1688,6 @@ msgstr "" "Dezactivez addon-ul de la '%s' pentru a preveni erori ulterioare." msgid "" -"Unable to load addon script from path: '%s' Base type is not EditorPlugin." -msgstr "" -"Nu a putut fi încărcat scriptul add-on din calea: '%s' tipul de Bază nu este " -"EditorPlugin." - -msgid "Unable to load addon script from path: '%s' Script is not in tool mode." -msgstr "" -"Nu a putut fi încărcat scriptul add-on din calea: '%s' Scriptul nu este în " -"modul unealtă." - -msgid "" "Scene '%s' was automatically imported, so it can't be modified.\n" "To make changes to it, a new inherited scene can be created." msgstr "" diff --git a/editor/translations/editor/ru.po b/editor/translations/editor/ru.po index 77e842b394..463758931b 100644 --- a/editor/translations/editor/ru.po +++ b/editor/translations/editor/ru.po @@ -3566,16 +3566,6 @@ msgstr "" "Отключение аддона '%s' для предотвращения дальнейших ошибок." msgid "" -"Unable to load addon script from path: '%s' Base type is not EditorPlugin." -msgstr "" -"Не удалось загрузить скрипт из источника: '%s' Базовый тип не EditorPlugin." - -msgid "Unable to load addon script from path: '%s' Script is not in tool mode." -msgstr "" -"Не удалось загрузить скрипт аддона из источника: '%s'. Скрипт не в режиме " -"инструмента." - -msgid "" "Scene '%s' was automatically imported, so it can't be modified.\n" "To make changes to it, a new inherited scene can be created." msgstr "" diff --git a/editor/translations/editor/sk.po b/editor/translations/editor/sk.po index cc86cb39bb..b9a8a693c4 100644 --- a/editor/translations/editor/sk.po +++ b/editor/translations/editor/sk.po @@ -18,13 +18,14 @@ # Lukáš Ševc <sevclukas0@gmail.com>, 2023. # Milan Šalka <salka.milan@googlemail.com>, 2023. # Mikuláš Suchanovský <mikulassuchanovsky@protonmail.com>, 2023. +# Ellie Star <gender.thief.star@gmail.com>, 2023. msgid "" msgstr "" "Project-Id-Version: Godot Engine editor interface\n" "Report-Msgid-Bugs-To: https://github.com/godotengine/godot\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2023-11-26 04:03+0000\n" -"Last-Translator: Mikuláš Suchanovský <mikulassuchanovsky@protonmail.com>\n" +"PO-Revision-Date: 2023-12-02 19:36+0000\n" +"Last-Translator: Ellie Star <gender.thief.star@gmail.com>\n" "Language-Team: Slovak <https://hosted.weblate.org/projects/godot-engine/godot/" "sk/>\n" "Language: sk\n" @@ -32,7 +33,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" -"X-Generator: Weblate 5.2.1-rc\n" +"X-Generator: Weblate 5.3-dev\n" msgid "Main Thread" msgstr "Hlavné vlákno" @@ -133,6 +134,9 @@ msgstr "Späť, Sony Select, Xbox Back, Nintendo -" msgid "Guide, Sony PS, Xbox Home" msgstr "Sprievodca, Sony PS, Xbox Home" +msgid "Start, Xbox Menu, Nintendo +" +msgstr "Štart, Xbox Menu, Nintendo +" + msgid "Left Stick, Sony L3, Xbox L/LS" msgstr "Ľavá páčka, Sony L3, Xbox L/LS" @@ -157,12 +161,30 @@ msgstr "D-pad vľavo" msgid "D-pad Right" msgstr "D-pad vpravo" +msgid "Xbox Share, PS5 Microphone, Nintendo Capture" +msgstr "Xbox zdieľanie, PS5 mikrofón, Nintendo záznam" + +msgid "Xbox Paddle 1" +msgstr "Xbox pádlo 1" + +msgid "Xbox Paddle 2" +msgstr "Xbox pádlo 2" + +msgid "Xbox Paddle 3" +msgstr "Xbox pádlo 3" + +msgid "Xbox Paddle 4" +msgstr "Xbox pádlo 4" + msgid "PS4/5 Touchpad" msgstr "PS4/5 Touchpad" msgid "Joypad Button %d" msgstr "Joypad Tlačidlo %d" +msgid "Pressure:" +msgstr "Tlak:" + msgid "canceled" msgstr "zrušený" @@ -189,6 +211,9 @@ msgstr "Gesto presúvania na (%s) s deltou (%s)" msgid "MIDI Input on Channel=%s Message=%s" msgstr "Vstup MIDI na Kanále=%s Správa=%s" +msgid "Input Event with Shortcut=%s" +msgstr "Vstupná udalosť so skratkou=%s" + msgid "Accept" msgstr "Potvrdiť" @@ -198,6 +223,12 @@ msgstr "Zvoliť" msgid "Cancel" msgstr "Zrušiť" +msgid "Focus Next" +msgstr "Zamerať ďalšie" + +msgid "Focus Prev" +msgstr "Zamerať predchádzajúce" + msgid "Left" msgstr "Vľavo" @@ -237,6 +268,9 @@ msgstr "Späť" msgid "Redo" msgstr "Prerobiť" +msgid "Completion Query" +msgstr "Dotaz na dokončenie" + msgid "New Line" msgstr "Nový riadok" @@ -252,6 +286,9 @@ msgstr "Odsadenie" msgid "Dedent" msgstr "Zmazať odsadenie" +msgid "Backspace" +msgstr "Backspace" + msgid "Backspace Word" msgstr "Zmazať slovo naľavo" @@ -324,6 +361,15 @@ msgstr "Označiť slovo pod kurzorom" msgid "Add Selection for Next Occurrence" msgstr "Pridať ďalší výskyt do označenia" +msgid "Clear Carets and Selection" +msgstr "Vyčistiť vsuvky a výber" + +msgid "Toggle Insert Mode" +msgstr "Prepnúť mód vloženia" + +msgid "Submit Text" +msgstr "Odoslať text" + msgid "Duplicate Nodes" msgstr "Duplikovať Nody" @@ -339,6 +385,12 @@ msgstr "Obnoviť" msgid "Show Hidden" msgstr "Zobraziť Skryté" +msgid "Swap Input Direction" +msgstr "Prevrátiť smer vstupu" + +msgid "Invalid input %d (not passed) in expression" +msgstr "Nesprávny vstup %d (neprešiel) vo výraze" + msgid "self can't be used because instance is null (not passed)" msgstr "self nemôže byť použité, pretože inštancia je null (nie je platná)" @@ -406,6 +458,9 @@ msgstr "Akcia s názvom '%s' už existuje." msgid "Cannot Revert - Action is same as initial" msgstr "Nie je možný návrat - akcia je rovnaká ako počiatočná" +msgid "Revert Action" +msgstr "Vrátiť akciu" + msgid "Add Event" msgstr "Pridať udalosť" @@ -424,6 +479,9 @@ msgstr "Odstrániť Udalosť" msgid "Filter by name..." msgstr "Filtrovať podľa názvu..." +msgid "Clear All" +msgstr "Vyčistiť všetko" + msgid "Add New Action" msgstr "Pridať Novú Akciu" @@ -445,6 +503,9 @@ msgstr "Čas:" msgid "Value:" msgstr "Hodnota:" +msgid "Update Selected Key Handles" +msgstr "Upraviť vybrané rukoväte kľúčov" + msgid "Insert Key Here" msgstr "Tu vložte kľúč" @@ -454,6 +515,15 @@ msgstr "Duplikovať kľúč(e)" msgid "Delete Selected Key(s)" msgstr "Zmazať označené kľúč(e)" +msgid "Make Handles Free" +msgstr "Uvoľniť rukoväte" + +msgid "Make Handles Linear" +msgstr "Spraviť rukoväte lineárne" + +msgid "Make Handles Balanced" +msgstr "Spraviť rukoväte balancované" + msgid "Add Bezier Point" msgstr "Pridať Bezier bod" @@ -2785,15 +2855,6 @@ msgstr "" "Deaktivujem addon z '%s', aby sa predišlo ďalším chybám." msgid "" -"Unable to load addon script from path: '%s' Base type is not EditorPlugin." -msgstr "" -"Nepodarilo sa načítať addon script z cesty: '%s' Base type není EditorPlugin." - -msgid "Unable to load addon script from path: '%s' Script is not in tool mode." -msgstr "" -"Nepodarilo sa načítať addon script z cesty: '%s' Script není v tool móde." - -msgid "" "Scene '%s' was automatically imported, so it can't be modified.\n" "To make changes to it, a new inherited scene can be created." msgstr "" diff --git a/editor/translations/editor/sv.po b/editor/translations/editor/sv.po index 83a7925f3e..31dcbf5ca4 100644 --- a/editor/translations/editor/sv.po +++ b/editor/translations/editor/sv.po @@ -38,8 +38,8 @@ msgstr "" "Project-Id-Version: Godot Engine editor interface\n" "Report-Msgid-Bugs-To: https://github.com/godotengine/godot\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2023-11-03 04:56+0000\n" -"Last-Translator: Henrik Nilsson <nsmoooose@gmail.com>\n" +"PO-Revision-Date: 2023-12-06 04:32+0000\n" +"Last-Translator: Kristoffer Grundström <swedishsailfishosuser@tutanota.com>\n" "Language-Team: Swedish <https://hosted.weblate.org/projects/godot-engine/" "godot/sv/>\n" "Language: sv\n" @@ -47,7 +47,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 5.2-dev\n" +"X-Generator: Weblate 5.3-dev\n" msgid "Main Thread" msgstr "Huvudtråd" @@ -967,6 +967,12 @@ msgstr "Importerad Scen" msgid "Warning: Editing imported animation" msgstr "Varning: Redigerar importerad animation" +msgid "Inactive Player" +msgstr "Inaktiv spelare" + +msgid "Warning: AnimationPlayer is inactive" +msgstr "Varning: Animationsspelaren är inaktiv" + msgid "Toggle between the bezier curve editor and track editor." msgstr "Växla mellan redigeringen av bezierkurvor och spårredigeringen." @@ -1107,6 +1113,10 @@ msgid "Elastic" msgstr "Elastisk" msgctxt "Transition Type" +msgid "Cubic" +msgstr "Kubisk" + +msgctxt "Transition Type" msgid "Circ" msgstr "Cirkulär" @@ -1280,6 +1290,12 @@ msgstr "Filtrera metoder" msgid "No method found matching given filters." msgstr "Ingen metod funnen som matchar det givna filtret." +msgid "Script Methods Only" +msgstr "Endast skriptmetoder" + +msgid "Compatible Methods Only" +msgstr "Endast kompatibla metoder" + msgid "Remove" msgstr "Ta bort" @@ -1607,6 +1623,9 @@ msgstr "Stack Frames" msgid "Filter Stack Variables" msgstr "Filtrera stackvariabler" +msgid "Expand All" +msgstr "Expandera alla" + msgid "Profiler" msgstr "Profiler" @@ -1619,6 +1638,9 @@ msgstr "Lista över Videominnes-användning av Resource:" msgid "Total:" msgstr "Totalt:" +msgid "Export list to a CSV file" +msgstr "Exportera lista till en CSV-fil" + msgid "Resource Path" msgstr "Resource Path" @@ -1767,6 +1789,21 @@ msgstr "Resurser Utan Explicit Ägande:" msgid "Folder name cannot be empty." msgstr "Katalognamnet kan inte vara tomt." +msgid "Folder name contains invalid characters." +msgstr "Mappnamnet innehåller ogiltiga tecken." + +msgid "Folder name cannot begin or end with a space." +msgstr "Mappens namn kan inte börja eller sluta med ett mellanslag." + +msgid "Folder name cannot begin with a dot." +msgstr "Namnet på mappen kan inte börja med en punkt." + +msgid "File with that name already exists." +msgstr "Fil med det där namnet existerar redan." + +msgid "Folder with that name already exists." +msgstr "Mapp med det där namnet existerar redan." + msgid "Using slashes in folder names will create subfolders recursively." msgstr "Användandet av snedstreck i mappnamn skapar undermappar rekursivt." @@ -1779,6 +1816,9 @@ msgstr "Skapa ny katalog i %s:" msgid "Create Folder" msgstr "Skapa Mapp" +msgid "Folder name is valid." +msgstr "Mapp-namnet är ogiltigt." + msgid "Thanks from the Godot community!" msgstr "Tack från Godot-gemenskapen!" @@ -1794,6 +1834,10 @@ msgstr "Projektgrundare" msgid "Lead Developer" msgstr "Ledande utvecklare" +msgctxt "Job Title" +msgid "Project Manager" +msgstr "Projektledare" + msgid "Developers" msgstr "Utvecklare" @@ -1809,6 +1853,18 @@ msgstr "Guldsponsorer" msgid "Silver Sponsors" msgstr "Silverdonatorer" +msgid "Diamond Members" +msgstr "Diamantmedlemmar" + +msgid "Titanium Members" +msgstr "Titanium-medlemmar" + +msgid "Platinum Members" +msgstr "Platinum-medlemmar" + +msgid "Gold Members" +msgstr "Guld-medlemmar" + msgid "Donors" msgstr "Donatorer" @@ -2034,6 +2090,9 @@ msgstr "Skapa en ny Buss-Layout." msgid "Invalid name." msgstr "Ogiltigt namn." +msgid "Cannot begin with a digit." +msgstr "Kan inte börja med en siffra." + msgid "Valid characters:" msgstr "Giltiga tecken:" @@ -2112,6 +2171,9 @@ msgstr "XR" msgid "RenderingDevice" msgstr "Renderare" +msgid "OpenGL" +msgstr "OpenGL" + msgid "Vulkan" msgstr "Vulkan" @@ -2222,6 +2284,9 @@ msgstr "Profil:" msgid "Reset to Defaults" msgstr "Återställ till standardvärden" +msgid "Detect from Project" +msgstr "Upptäck från projekt" + msgid "Actions:" msgstr "Åtgärder:" @@ -2590,6 +2655,9 @@ msgstr "Typsnittsstorlekar" msgid "Icons" msgstr "Ikoner" +msgid "Styles" +msgstr "Stilar" + msgid "Enumerations" msgstr "Enumerationer" @@ -2631,12 +2699,24 @@ msgstr "Metodbeskrivning" msgid "Operator Descriptions" msgstr "Operator beskrivning" +msgid "Metadata:" +msgstr "Metadata:" + msgid "Property:" msgstr "Egenskap:" +msgid "Method:" +msgstr "Metod:" + msgid "Signal:" msgstr "Signal:" +msgid "Theme Item:" +msgstr "Tema-objekt:" + +msgid "No description available." +msgstr "Ingen beskrivning tillgänglig." + msgid "%d match." msgstr "%d matcha." @@ -2661,6 +2741,9 @@ msgstr "Endast Metoder" msgid "Signals Only" msgstr "Endast Signaler" +msgid "Annotations Only" +msgstr "Endast annoteringar" + msgid "Constants Only" msgstr "Enbart konstanter" @@ -3139,15 +3222,6 @@ msgstr "" "Stänger av addon vid '%s' för att förhindra fler fel." msgid "" -"Unable to load addon script from path: '%s' Base type is not EditorPlugin." -msgstr "" -"Kunde inte ladda addon script från sökväg: '%s' Bastyp är inte EditorPlugin." - -msgid "Unable to load addon script from path: '%s' Script is not in tool mode." -msgstr "" -"Kunde inte ladda addon script från sökväg: '%s' Skript är inte i verktygsläge." - -msgid "" "Scene '%s' was automatically imported, so it can't be modified.\n" "To make changes to it, a new inherited scene can be created." msgstr "" diff --git a/editor/translations/editor/th.po b/editor/translations/editor/th.po index 00e0d50576..cce2c863be 100644 --- a/editor/translations/editor/th.po +++ b/editor/translations/editor/th.po @@ -2066,13 +2066,6 @@ msgid "Unable to load addon script from path: '%s'." msgstr "ไม่สามารถโหลดสคริปต์จาก: '%s'" msgid "" -"Unable to load addon script from path: '%s' Base type is not EditorPlugin." -msgstr "ไม่สามารถโหลดสคริปต์จาก: '%s' ไม่ได้สืบทอดจาก EditorPlugin" - -msgid "Unable to load addon script from path: '%s' Script is not in tool mode." -msgstr "ไม่สามารถโหลดสคริปต์จาก: '%s' ไม่ใช่สคริปต์ tool" - -msgid "" "Scene '%s' was automatically imported, so it can't be modified.\n" "To make changes to it, a new inherited scene can be created." msgstr "" diff --git a/editor/translations/editor/tr.po b/editor/translations/editor/tr.po index 13f74a42fc..995b00ab25 100644 --- a/editor/translations/editor/tr.po +++ b/editor/translations/editor/tr.po @@ -111,7 +111,7 @@ msgstr "" "Project-Id-Version: Godot Engine editor interface\n" "Report-Msgid-Bugs-To: https://github.com/godotengine/godot\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2023-11-26 04:03+0000\n" +"PO-Revision-Date: 2023-12-11 09:59+0000\n" "Last-Translator: Yılmaz Durmaz <yilmaz_durmaz@hotmail.com>\n" "Language-Team: Turkish <https://hosted.weblate.org/projects/godot-engine/" "godot/tr/>\n" @@ -120,7 +120,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 5.2.1-rc\n" +"X-Generator: Weblate 5.3-dev\n" msgid "Main Thread" msgstr "Ana İş Parçası" @@ -3508,12 +3508,12 @@ msgstr "" "Daha fazla hatayı önlemek için '%s' eklentisi devre dışı bırakılıyor." msgid "" -"Unable to load addon script from path: '%s' Base type is not EditorPlugin." +"Unable to load addon script from path: '%s'. Base type is not 'EditorPlugin'." msgstr "" -"Eklenti betiği '%s' yolundan yüklenemedi. Bunun temel tipi EditorPlugin " -"(düzenleyici eklentisi) değil." +"Eklenti betiği '%s' yolundan yüklenemedi. Bunun temel tipi " +"'EditorPlugin' (düzenleyici eklentisi) değil." -msgid "Unable to load addon script from path: '%s' Script is not in tool mode." +msgid "Unable to load addon script from path: '%s'. Script is not in tool mode." msgstr "Eklenti betiği '%s' yolundan yüklenemedi. Betik, araç kipinde değil." msgid "" @@ -14105,12 +14105,63 @@ msgstr "'%s' ismi gölgelendirici dili için ayrılmış bir anahtar kelimedir." msgid "Add Shader Global Parameter" msgstr "Gölgelendirici Genel Parametresi Ekle" +msgid "" +"This project uses meshes with an outdated mesh format from previous Godot " +"versions. The engine needs to update the format in order to use those meshes. " +"Please use the 'Upgrade Mesh Surfaces' tool from the 'Project > Tools' menu. " +"You can ignore this message and keep using outdated meshes, but keep in mind " +"that this leads to increased load times every time you load the project." +msgstr "" +"Bu proje, önceki Godot sürümlerinden gelen eski bir örgü formatına sahip " +"örgüleri kullanıyor. Motorun bu örgüleri kullanabilmesi için bu formatı " +"güncellemesi gerekmektedir. Lütfen 'Proje > Araçlar' menüsünden 'Örgü " +"Yüzeylerini Yükselt' aracını kullanın. Bu mesajı görmezden gelebilir ve eski " +"örgüleri kullanmaya devam edebilirsiniz, ancak bunun projeyi her " +"yüklediğinizde yükleme sürelerinin artmasına neden olduğunu unutmayın." + +msgid "" +"This project uses meshes with an outdated mesh format. Check the output log." +msgstr "" +"Bu proje, eski bir örgü formatına sahip örgüler kullanıyor. Çıktı günlüğünü " +"kontrol edin." + msgid "Upgrading All Meshes in Project" msgstr "Projedeki Tüm Örgüler Güncelleniyor" msgid "Attempting to re-save " msgstr "Yeniden kaydetme deneniyor " +msgid "Attempting to remove " +msgstr "Kaldırılmaya çalışılıyor " + +msgid "" +"The mesh format has changed in Godot 4.2, which affects both imported meshes " +"and meshes authored inside of Godot. The engine needs to update the format in " +"order to use those meshes.\n" +"\n" +"If your project predates Godot 4.2 and contains meshes, we recommend you run " +"this one time conversion tool. This update will restart the editor and may " +"take several minutes. Upgrading will make the meshes incompatible with " +"previous versions of Godot.\n" +"\n" +"You can still use your existing meshes as is. The engine will update each " +"mesh in memory, but the update will not be saved. Choosing this option will " +"lead to slower load times every time this project is loaded." +msgstr "" +"Örgü formatı Godot 4.2'de değişmiştir, bu da hem içe aktarılan örgüleri hem " +"de Godot içinde yazılan örgüleri etkilemektedir. Motorun bu örgüleri " +"kullanabilmesi için formatı güncellemesi gerekiyor.\n" +"\n" +"Eğer projeniz Godot 4.2'den önceye aitse ve örgüler içeriyorsa, bu tek " +"seferlik dönüştürme aracını çalıştırmanızı öneririz. Bu güncelleme " +"düzenleyiciyi yeniden başlatacaktır ve birkaç dakika sürebilir. Yükseltme " +"işlemi, örgüleri Godot'un önceki sürümleriyle uyumsuz hale getirecektir.\n" +"\n" +"Mevcut örgülerinizi olduğu gibi kullanmaya devam da edebilirsiniz. Motor, her " +"bir örgüyü bellekte güncelleyecek, ancak bu güncelleme kaydedilmeyecektir. Bu " +"seçeneğin seçilmesi, projenin her açılmasında daha yavaş yükleme sürelerine " +"yol açacaktır." + msgid "Restart & Upgrade" msgstr "Yeniden Başlat ve Güncelle" diff --git a/editor/translations/editor/uk.po b/editor/translations/editor/uk.po index 39e34daae4..b4a90a5d8d 100644 --- a/editor/translations/editor/uk.po +++ b/editor/translations/editor/uk.po @@ -37,13 +37,14 @@ # Siked Siked <siked3@gmail.com>, 2023. # Kipя <yankirill@icloud.com>, 2023. # Kristian Sik <jemynanyt@gmail.com>, 2023. +# Maxi fox <oleksijyerukh@gmail.com>, 2023. msgid "" msgstr "" "Project-Id-Version: Ukrainian (Godot Engine)\n" "Report-Msgid-Bugs-To: https://github.com/godotengine/godot\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2023-10-13 02:30+0000\n" -"Last-Translator: Siked Siked <siked3@gmail.com>\n" +"PO-Revision-Date: 2023-12-02 19:36+0000\n" +"Last-Translator: Maxi fox <oleksijyerukh@gmail.com>\n" "Language-Team: Ukrainian <https://hosted.weblate.org/projects/godot-engine/" "godot/uk/>\n" "Language: uk\n" @@ -52,7 +53,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" -"X-Generator: Weblate 5.1-dev\n" +"X-Generator: Weblate 5.3-dev\n" msgid "Main Thread" msgstr "Головний потік" @@ -908,6 +909,22 @@ msgstr "" "увімкніть опції \"Зберегти у файл\" і\n" "\"Зберегти власні доріжки\"." +msgid "" +"Some AnimationPlayerEditor's options are disabled since this is the dummy " +"AnimationPlayer for preview.\n" +"\n" +"The dummy player is forced active, non-deterministic and doesn't have the " +"root motion track. Furthermore, the original node is inactive temporary." +msgstr "" +"Деякі опції редактора AnimationPlayerEditor вимкнено, оскільки це фіктивний " +"плеєр для попереднього перегляду.\n" +"\n" +"Фіктивний програвач є примусово активним, недетермінованим і не має кореневої " +"доріжки руху. Крім того, оригінальний вузол тимчасово неактивний." + +msgid "AnimationPlayer is inactive. The playback will not be processed." +msgstr "Програвач AnimationPlayer неактивний. Відтворення не буде оброблено." + msgid "Select an AnimationPlayer node to create and edit animations." msgstr "Виберіть вузол AnimationPlayer для створення і редагування анімацій." @@ -1036,15 +1053,60 @@ msgctxt "Transition Type" msgid "Sine" msgstr "Синусоїдний" +msgctxt "Transition Type" +msgid "Quint" +msgstr "Квінт" + +msgctxt "Transition Type" +msgid "Quart" +msgstr "Кварт" + +msgctxt "Transition Type" +msgid "Quad" +msgstr "Чотири." + +msgctxt "Transition Type" +msgid "Elastic" +msgstr "Еластичний" + +msgctxt "Transition Type" +msgid "Circ" +msgstr "Кірк" + +msgctxt "Transition Type" +msgid "Bounce" +msgstr "Відскок" + +msgctxt "Transition Type" +msgid "Spring" +msgstr "Весна" + +msgctxt "Ease Type" +msgid "InOut" +msgstr "Увійшов - вийшов" + +msgctxt "Ease Type" +msgid "OutIn" +msgstr "Виходьте У" + +msgid "Ease Type:" +msgstr "Легкий тип:" + msgid "FPS:" msgstr "FPS:" msgid "Animation Baker" msgstr "Запікання анімації" +msgid "3D Pos/Rot/Scl Track:" +msgstr "Трек 3D пози./пово./розм.:" + msgid "Blendshape Track:" msgstr "Доріжка змішаних форм:" +msgid "Value Track:" +msgstr "Ціннісний трек:" + msgid "Select Tracks to Copy" msgstr "Виберіть доріжки для копіювання" @@ -1072,6 +1134,9 @@ msgstr "Номер рядка:" msgid "%d replaced." msgstr "%d замінено." +msgid "No match" +msgstr "Не збігається" + msgid "%d match" msgid_plural "%d matches" msgstr[0] "%d збіг" @@ -1099,6 +1164,14 @@ msgstr "Замінити всі" msgid "Selection Only" msgstr "Тільки виділити" +msgctxt "Indentation" +msgid "Spaces" +msgstr "Простори" + +msgctxt "Indentation" +msgid "Tabs" +msgstr "Tabs" + msgid "Toggle Scripts Panel" msgstr "Перемкнути панель скриптів" @@ -1331,6 +1404,18 @@ msgstr "" msgid "Toggle Visibility" msgstr "Перемкнути видимість" +msgid "Updating assets on target device:" +msgstr "Оновлення ресурсів на цільовому пристрої:" + +msgid "Decompressing remote file system" +msgstr "Розпакування віддаленої файлової системи" + +msgid "Scanning for local changes" +msgstr "Сканування локальних змін" + +msgid "Sending list of changed files:" +msgstr "Надсилання списку змінених файлів:" + msgid "ms" msgstr "мс" @@ -1484,6 +1569,9 @@ msgstr "Пауза" msgid "Continue" msgstr "Продовжити" +msgid "Thread:" +msgstr "Нитки:" + msgid "Stack Frames" msgstr "Стосувати кадри" @@ -1585,6 +1673,12 @@ msgstr "Редактор залежностей" msgid "Search Replacement Resource:" msgstr "Знайти замінний ресурс:" +msgid "Open Scene" +msgid_plural "Open Scenes" +msgstr[0] "Відкрити сцену" +msgstr[1] "Відкрити сцени" +msgstr[2] "Відкрити сцен" + msgid "Open" msgstr "Відкрити" @@ -1655,9 +1749,17 @@ msgstr "Кількість" msgid "Resources Without Explicit Ownership:" msgstr "Ресурси без явної власності:" +msgid "Using slashes in folder names will create subfolders recursively." +msgstr "" +"Використання косих рисок у назвах теки призведе до рекурсивного створення " +"підтеки." + msgid "Could not create folder." msgstr "Неможливо створити теку." +msgid "Create new folder in %s:" +msgstr "Створіть нову папку в %s:" + msgid "Create Folder" msgstr "Створити Теку" @@ -1686,6 +1788,9 @@ msgstr "Розробники" msgid "Authors" msgstr "Автори" +msgid "Patrons" +msgstr "Меценати" + msgid "Platinum Sponsors" msgstr "Платинові спонсори" @@ -1695,6 +1800,12 @@ msgstr "Золоті спонсори" msgid "Silver Sponsors" msgstr "Срібні донори" +msgid "Diamond Members" +msgstr "Діамантові учасники" + +msgid "Titanium Members" +msgstr "Титанові члени" + msgid "Donors" msgstr "Донори" @@ -1731,6 +1842,18 @@ msgstr "" msgid "%s (already exists)" msgstr "%s (вже існує)" +msgid "%d file conflicts with your project and won't be installed" +msgid_plural "%d files conflict with your project and won't be installed" +msgstr[0] "Файл %d конфліктує з вашим проектом і не буде інстальований" +msgstr[1] "Файли %d конфліктують з вашим проектом і не будуть встановлені" +msgstr[2] "Файли %d конфліктують з вашим проектом і не будуть встановлені" + +msgid "This asset doesn't have a root directory, so it can't be ignored." +msgstr "Цей ресурс не має кореневого каталогу, тому його не можна ігнорувати." + +msgid "Ignore the root directory when extracting files." +msgstr "Ігноруйте кореневий каталог під час видобування файлів." + msgid "Uncompressing Assets" msgstr "Розпаковування ресурсів" @@ -1746,6 +1869,32 @@ msgstr "Пакунок «%s» успішно встановлено!" msgid "Success!" msgstr "Успіх!" +msgid "Open the list of the asset contents and select which files to install." +msgstr "" +"Відкрийте список вмісту ресурсу і виберіть файли, які потрібно встановити." + +msgid "" +"Change the folder where the contents of the asset are going to be installed." +msgstr "Змініть папку, до якої буде інстальовано вміст ресурсу." + +msgid "Ignore asset root" +msgstr "Ігнорувати корінь активу" + +msgid "No files conflict with your project" +msgstr "Файли не конфліктують з вашим проектом" + +msgid "Show contents of the asset and conflicting files." +msgstr "Показати вміст ресурсу та конфліктуючі файли." + +msgid "Contents of the asset:" +msgstr "Зміст активу:" + +msgid "Installation preview:" +msgstr "Попередній перегляд установки:" + +msgid "Configure Asset Before Installing" +msgstr "Налаштуйте актив перед встановленням" + msgid "Install" msgstr "Встановити" @@ -1963,10 +2112,10 @@ msgid "XR" msgstr "XR" msgid "OpenGL" -msgstr "OpenGL" +msgstr "ОпенГЛ" msgid "Vulkan" -msgstr "Vulkan" +msgstr "Вулкан" msgid "Text Server: Fallback" msgstr "Текстовий сервер: Запасний" @@ -1983,6 +2132,9 @@ msgstr "Шрифти WOFF2" msgid "SIL Graphite Fonts" msgstr "Шрифти SIL Graphite" +msgid "Multi-channel Signed Distance Field Font Rendering" +msgstr "Багатоканальна візуалізація шрифтів полів зі знаками відстані" + msgid "2D Physics nodes and PhysicsServer2D." msgstr "Вузли 2D фізики та PhysicsServer2D." @@ -2041,6 +2193,14 @@ msgstr "" "Підтримка технології інтелектуальних шрифтів SIL Graphite (підтримується лише " "в Розширеному текстовому сервері)." +msgid "" +"Multi-channel signed distance field font rendering support using msdfgen " +"library (pre-rendered MSDF fonts can be used even if this option disabled)." +msgstr "" +"Підтримка багатоканального відображення шрифтів підписаних полів відстані за " +"допомогою бібліотеки msdfgen (можна використовувати попередньо відмальовані " +"MSDF-шрифти, навіть якщо цю опцію вимкнено)." + msgid "General Features:" msgstr "Основні можливості:" @@ -2092,9 +2252,19 @@ msgstr "Завантажити профіль" msgid "Export Profile" msgstr "Експорт профілю" +msgid "Forced classes on detect:" +msgstr "Примусові заняття з детективу:" + msgid "Edit Build Configuration Profile" msgstr "Редагувати профіль конфігурації збірки" +msgid "" +"Failed to execute command \"%s\":\n" +"%s." +msgstr "" +"Не вдалося виконати команду \"%s\":\n" +"%s." + msgid "Filter Commands" msgstr "Фільтрувати команди" @@ -2291,6 +2461,11 @@ msgstr "Імпорт ресурсів типу: %s" msgid "No return value." msgstr "Значення не повертається." +msgid "This value is an integer composed as a bitmask of the following flags." +msgstr "" +"Це значення є цілим числом, складеним у вигляді бітової маски з наступних " +"прапорів." + msgid "Deprecated" msgstr "Застаріле" @@ -2324,6 +2499,26 @@ msgstr "" msgid "Error codes returned:" msgstr "Повернуто коди помилок:" +msgid "There is currently no description for this method." +msgstr "Наразі для цього методу немає опису." + +msgid "There is currently no description for this constructor." +msgstr "Наразі для цього конструктора немає опису." + +msgid "" +"There is currently no description for this method. Please help us by " +"[color=$color][url=$url]contributing one[/url][/color]!" +msgstr "" +"Наразі для цього методу немає опису. Будь ласка, допоможіть нам, " +"[color=$color][url=$url]зробивши один внесок[/url][/color]!" + +msgid "" +"There is currently no description for this constructor. Please help us by " +"[color=$color][url=$url]contributing one[/url][/color]!" +msgstr "" +"Наразі для цього конструктора немає опису. Будь ласка, допоможіть нам, " +"[color=$color][url=$url]зробивши один внесок[/url][/color]!" + msgid "Top" msgstr "Верхівка" @@ -2361,6 +2556,17 @@ msgstr "" "Наразі для цього класу немає опису. Будь ласка, допоможіть нам, [color=$color]" "[url=$url]зробивши внесок[/url][/color]!" +msgid "Note:" +msgstr "Зауважте:" + +msgid "" +"There are notable differences when using this API with C#. See [url=%s]C# API " +"differences to GDScript[/url] for more information." +msgstr "" +"Існують помітні відмінності при використанні цього API з C#. Дивіться " +"[url=%s]Відмінності API C# від GDScript[/url] для отримання додаткової " +"інформації." + msgid "Online Tutorials" msgstr "Підручники в інтернеті" @@ -2444,12 +2650,24 @@ msgstr "Описи методів" msgid "Operator Descriptions" msgstr "Описи операторів" +msgid "Metadata:" +msgstr "Метадані:" + msgid "Property:" msgstr "Властивість:" +msgid "Method:" +msgstr "Метод:" + msgid "Signal:" msgstr "Сигнал:" +msgid "Theme Item:" +msgstr "Тема \"Предмет\":" + +msgid "No description available." +msgstr "Опис відсутній." + msgid "%d match." msgstr "%d відповідник." @@ -2533,6 +2751,12 @@ msgstr "" "Пришпилення значення призведе до примусового збереження значення, навіть якщо " "воно дорівнює типовому." +msgid "(%d change)" +msgid_plural "(%d changes)" +msgstr[0] "(%d зміна)" +msgstr[1] "(%d зміни)" +msgstr[2] "(%d змін)" + msgid "Add element to property array with prefix %s." msgstr "Додати елемент до масиву властивостей із перфіксом %s." @@ -2543,6 +2767,12 @@ msgid "Move element %d to position %d in property array with prefix %s." msgstr "" "Перемістити елемент %d на позицію %d в масиві властивостей із перфіксом %s." +msgid "Clear Property Array with Prefix %s" +msgstr "Очистити масив властивостей з префіксом %s" + +msgid "Resize Property Array with Prefix %s" +msgstr "Зміна розміру масиву властивостей з префіксом %s" + msgid "Element %d: %s%d*" msgstr "Елемент %d: %s%d*" @@ -2576,6 +2806,9 @@ msgstr "Додати метадані" msgid "Set %s" msgstr "Встановити %s" +msgid "Set Multiple: %s" +msgstr "Встановити множину: %s" + msgid "Remove metadata %s" msgstr "Видалити метадані %s" @@ -2627,6 +2860,9 @@ msgstr "Мініатюра..." msgid "Select existing layout:" msgstr "Виберіть існуючий макет:" +msgid "Or enter new layout name" +msgstr "Або введіть нову назву макета" + msgid "Changed Locale Language Filter" msgstr "Змінено фільтр локальної мови" @@ -2707,6 +2943,9 @@ msgstr "Переключити видимість попереджень." msgid "Toggle visibility of editor messages." msgstr "Переключити видимість повідомлень редактора." +msgid "Native Shader Source Inspector" +msgstr "Інспектор джерел нативних шейдерів" + msgid "Unnamed Project" msgstr "Проєкт без назви" @@ -3019,17 +3258,6 @@ msgstr "" "Вимикаємо додаток у «%s», щоб запобігти подальшим помилкам." msgid "" -"Unable to load addon script from path: '%s' Base type is not EditorPlugin." -msgstr "" -"Не вдається завантажити скрипт доповнення з шляху: '%s' Базовий тип не є " -"редактором плагінів." - -msgid "Unable to load addon script from path: '%s' Script is not in tool mode." -msgstr "" -"Неможливо завантажити скрипт доповнення з шляху: '%s' Скрипт не в режимі " -"інструменту." - -msgid "" "Scene '%s' was automatically imported, so it can't be modified.\n" "To make changes to it, a new inherited scene can be created." msgstr "" @@ -3053,6 +3281,9 @@ msgstr "Очистити недавні сцени" msgid "There is no defined scene to run." msgstr "Немає визначеної сцени для виконання." +msgid "%s - Godot Engine" +msgstr "%s - Godot Engine" + msgid "" "No main scene has ever been defined, select one?\n" "You can change it later in \"Project Settings\" under the 'application' " @@ -3089,9 +3320,37 @@ msgstr "Видалити компонування" msgid "Default" msgstr "Типовий" +msgid "%d second ago" +msgid_plural "%d seconds ago" +msgstr[0] "%d секунду тому" +msgstr[1] "%d секунд тому" +msgstr[2] "%d секунд тому" + +msgid "%d minute ago" +msgid_plural "%d minutes ago" +msgstr[0] "%d хвилину тому" +msgstr[1] "%d хвилин тому" +msgstr[2] "%d хвилин тому" + +msgid "%d hour ago" +msgid_plural "%d hours ago" +msgstr[0] "%d годину тому" +msgstr[1] "%d годин тому" +msgstr[2] "%d годин тому" + +msgid "" +"Scene \"%s\" has unsaved changes.\n" +"Last saved: %s." +msgstr "" +"Сцена \"%s\" має незбережені зміни.\n" +"Останнє збереження: %s." + msgid "Save & Close" msgstr "Зберегти та закрити" +msgid "Save before closing?" +msgstr "Зберегти перед закриттям?" + msgid "%d more files or folders" msgstr "%d більше файлів або тек" @@ -3260,6 +3519,13 @@ msgstr "Запитання і відповіді" msgid "Community" msgstr "Спільнота" +msgid "Copy System Info" +msgstr "Копіювати інформацію про систему" + +msgid "Copies the system info as a single-line text into the clipboard." +msgstr "" +"Копіює системну інформацію у вигляді однорядкового тексту до буфера обміну." + msgid "Report a Bug" msgstr "Повідомити про ваду" @@ -3391,6 +3657,12 @@ msgstr "Перезавантажити" msgid "Resave" msgstr "Перезаписати" +msgid "Create Version Control Metadata..." +msgstr "Створення метаданих керування версіями..." + +msgid "Version Control Settings..." +msgstr "Налаштування контролю версій..." + msgid "New Inherited" msgstr "Новий успадкований" @@ -3530,6 +3802,9 @@ msgstr "Виберіть панель перегляду" msgid "Selected node is not a Viewport!" msgstr "Позначений вузол не є панеллю перегляду!" +msgid "(Nil) %s" +msgstr "(Ніл) %s" + msgid "%s (size %s)" msgstr "%s (розмір %s)" @@ -3563,6 +3838,9 @@ msgstr "Локалізуючий рядок (розмір %d)" msgid "Add Translation" msgstr "Додати переклад" +msgid "Lock/Unlock Component Ratio" +msgstr "Співвідношення компонентів блокування/розблокування" + msgid "" "The selected resource (%s) does not match any type expected for this property " "(%s)." @@ -3633,6 +3911,12 @@ msgid "" msgstr "" "Не вдалося запустити скрипт редактора, ви забули перевизначити метод '_run'?" +msgid "Edit Built-in Action: %s" +msgstr "Редагувати вбудовану дію: %s" + +msgid "Edit Shortcut: %s" +msgstr "Ярлик редагування: %s" + msgid "Common" msgstr "Загальні" @@ -3654,12 +3938,42 @@ msgstr "Клавіатурні скорочення" msgid "Binding" msgstr "Палітурка" +msgid "Left Stick Left, Joystick 0 Left" +msgstr "Ліва кнопка ліворуч, джойстик 0 ліворуч" + +msgid "Left Stick Right, Joystick 0 Right" +msgstr "Ліва паличка Вправо, джойстик 0 Вправо" + +msgid "Left Stick Up, Joystick 0 Up" +msgstr "Лівий стік вгору, джойстик 0 вгору" + +msgid "Left Stick Down, Joystick 0 Down" +msgstr "Лівий стік вниз, джойстик 0 вниз" + +msgid "Right Stick Left, Joystick 1 Left" +msgstr "Права кнопка ліворуч, джойстик 1 ліворуч" + +msgid "Right Stick Right, Joystick 1 Right" +msgstr "Правий джойстик вправо, джойстик 1 вправо" + +msgid "Right Stick Up, Joystick 1 Up" +msgstr "Правий джойстик вгору, джойстик 1 вгору" + +msgid "Right Stick Down, Joystick 1 Down" +msgstr "Правий стік вниз, джойстик 1 вниз" + msgid "Joystick 2 Left" msgstr "Джойстик 2 Лівий" +msgid "Left Trigger, Sony L2, Xbox LT, Joystick 2 Right" +msgstr "Лівий тригер, Sony L2, Xbox LT, джойстик 2 Правий" + msgid "Joystick 2 Up" msgstr "Джойстик 2 Вгору" +msgid "Right Trigger, Sony R2, Xbox RT, Joystick 2 Down" +msgstr "Правий тригер, Sony R2, Xbox RT, джойстик 2 вниз" + msgid "Joystick 3 Left" msgstr "Джойстик 3 Лівий" @@ -3684,6 +3998,9 @@ msgstr "Джойстик 4 Вгору" msgid "Joystick 4 Down" msgstr "Джойстик 4 Вниз" +msgid "Unicode" +msgstr "Юнікод" + msgid "Joypad Axis %d %s (%s)" msgstr "Вісь джойстика %d %s (%s)" @@ -3782,6 +4099,9 @@ msgstr "" "При експортуванні у 32-бітовому режимі вбудовані PCK не можуть перевищувати " "за розміром 4 ГіБ." +msgid "Plugin \"%s\" is not supported on \"%s\"" +msgstr "Плагін \"%s\" не підтримується на \"%s\"" + msgid "Open the folder containing these templates." msgstr "Відкрити теку, яка містить ці шаблони." @@ -3981,6 +4301,12 @@ msgstr "" "Отримання шаблонів буде продовжено.\n" "Під час завершення можливе тимчасове «замерзання» редактора." +msgid "" +"Target platform requires '%s' texture compression. Enable 'Import %s' to fix." +msgstr "" +"Цільова платформа вимагає стиснення текстур '%s'. Увімкніть \"Імпортувати " +"%s\" для виправлення." + msgid "Runnable" msgstr "Активний" @@ -4233,6 +4559,14 @@ msgid "Failed to load resource at %s: %s" msgstr "Не вдалося завантажити ресурс з %s: %s" msgid "" +"This filename begins with a dot rendering the file invisible to the editor.\n" +"If you want to rename it anyway, use your operating system's file manager." +msgstr "" +"Ім'я файлу починається з крапки, що робить його невидимим для редактора.\n" +"Якщо ви все одно хочете перейменувати його, скористайтеся файловим менеджером " +"вашої операційної системи." + +msgid "" "This file extension is not recognized by the editor.\n" "If you want to rename it anyway, use your operating system's file manager.\n" "After renaming to an unknown extension, the file won't be shown in the editor " @@ -4254,6 +4588,12 @@ msgstr "" "Наступні файли або папки конфліктують з елементами в цільовому розташуванні " "'%s':" +msgid "Do you wish to overwrite them or rename the copied files?" +msgstr "Ви хочете перезаписати їх або перейменувати скопійовані файли?" + +msgid "Do you wish to overwrite them or rename the moved files?" +msgstr "Ви хочете перезаписати їх або перейменувати переміщені файли?" + msgid "Duplicating file:" msgstr "Дублювання файлу:" @@ -4353,6 +4693,27 @@ msgstr "Перейменувати..." msgid "Open in External Program" msgstr "Відкрити в зовнішній програмі" +msgid "Yellow" +msgstr "Жовтий" + +msgid "Green" +msgstr "Зелений" + +msgid "Teal" +msgstr "Чирок." + +msgid "Blue" +msgstr "Синій" + +msgid "Purple" +msgstr "Фіолетовий" + +msgid "Pink" +msgstr "Рожевий." + +msgid "Gray" +msgstr "Грей." + msgid "Go to previous selected folder/file." msgstr "Перейти до попередньої вибраної теки/файлу." @@ -4793,6 +5154,9 @@ msgstr "Дерево сцени (вузли):" msgid "Node Configuration Warning!" msgstr "Попередження щодо налаштування вузлів!" +msgid "Allowed:" +msgstr "Дозвольте:" + msgid "Select a Node" msgstr "Виберіть вузол" @@ -4821,6 +5185,9 @@ msgstr "" msgid "Loop:" msgstr "Цикл:" +msgid "BPM:" +msgstr "УД./ХВ:" + msgid "" "Configure the Beats Per Measure (tempo) used for the interactive streams.\n" "This is required in order to configure beat information." @@ -5070,6 +5437,9 @@ msgstr "Існуючий файл з такою самою назвою буде msgid "Will create new file" msgstr "Створить файл скрипту" +msgid "Already External" +msgstr "Вже зовнішній" + msgid "" "This material already references an external file, no action will be taken.\n" "Disable the external property for it to be extracted again." @@ -5209,6 +5579,13 @@ msgid "Advanced..." msgstr "Додатково..." msgid "" +"The imported resource is currently loaded. All instances will be replaced and " +"undo history will be cleared." +msgstr "" +"Наразі завантажується імпортований ресурс. Всі екземпляри будуть замінені, а " +"історія скасувань буде очищена." + +msgid "" "Select a resource file in the filesystem or in the inspector to adjust import " "settings." msgstr "" @@ -5437,18 +5814,66 @@ msgstr "Оновити" msgid "Plugin Name:" msgstr "Назва додатка:" +msgid "Required. This name will be displayed in the list of plugins." +msgstr "Обов'язкова умова. Ця назва буде відображатися у списку плагінів." + msgid "Subfolder:" msgstr "Підтека:" +msgid "" +"Optional. The folder name should generally use `snake_case` naming (avoid " +"spaces and special characters).\n" +"If left empty, the folder will be named after the plugin name converted to " +"`snake_case`." +msgstr "" +"Необов'язково. Ім'я теки зазвичай має використовувати регістр `snake_case` " +"(уникайте пробілів і спеціальних символів).\n" +"Якщо залишити теку порожньою, її буде названо ім'ям плагіна, перетвореним на " +"`snake_case`." + +msgid "" +"Optional. This description should be kept relatively short (up to 5 lines).\n" +"It will display when hovering the plugin in the list of plugins." +msgstr "" +"Необов'язково. Цей опис має бути відносно коротким (до 5 рядків).\n" +"Він відображатиметься при наведенні на плагін у списку плагінів." + msgid "Author:" msgstr "Автор:" +msgid "Optional. The author's username, full name, or organization name." +msgstr "" +"Необов'язково. Ім'я користувача, повне ім'я або назва організації автора." + msgid "Version:" msgstr "Версія:" +msgid "" +"Optional. A human-readable version identifier used for informational purposes " +"only." +msgstr "" +"Необов'язково. Ідентифікатор версії, що читається людиною і використовується " +"лише в інформаційних цілях." + +msgid "" +"Required. The scripting language to use for the script.\n" +"Note that a plugin may use several languages at once by adding more scripts " +"to the plugin." +msgstr "" +"Обов'язковий параметр. Мова скриптів, яку буде використано для скрипту.\n" +"Зверніть увагу, що плагін може використовувати декілька мов одночасно, " +"додавши більше скриптів до плагіна." + msgid "Script Name:" msgstr "Назва скрипту:" +msgid "" +"Optional. The path to the script (relative to the add-on folder). If left " +"empty, will default to \"plugin.gd\"." +msgstr "" +"Необов'язково. Шлях до скрипту (відносно теки доповнення). Якщо залишити " +"порожнім, за замовчуванням буде \"plugin.gd\"." + msgid "Activate now?" msgstr "Задіяти зараз?" @@ -5588,6 +6013,9 @@ msgstr "Вилучити точки і трикутники." msgid "Generate blend triangles automatically (instead of manually)" msgstr "Створити трикутники злиття автоматично (а не вручну)" +msgid "Parameter Changed: %s" +msgstr "Параметр змінено: %s" + msgid "Inspect Filters" msgstr "Оглянути фільтри" @@ -5784,6 +6212,9 @@ msgstr "Видалити анімацію з бібліотеки: %s" msgid "[built-in]" msgstr "[вбудована]" +msgid "[foreign]" +msgstr "[іноземна мова]" + msgid "[imported]" msgstr "[імпортована]" @@ -5962,6 +6393,12 @@ msgstr "Пересунути вузол" msgid "Transition exists!" msgstr "Існує перехід!" +msgid "Play/Travel to %s" +msgstr "Відтворення/подорож до %s" + +msgid "Edit %s" +msgstr "Редагування %s" + msgid "Add Node and Transition" msgstr "Додати вузол і перехід" @@ -6247,6 +6684,13 @@ msgstr "Крок повороту:" msgid "Scale Step:" msgstr "Крок масштабу:" +msgid "" +"Children of a container get their position and size determined only by their " +"parent." +msgstr "" +"Дочки контейнера отримують позицію і розмір, які визначаються тільки їхнім " +"батьком." + msgid "Move Node(s) to Position" msgstr "Пересунути вузол(и) на Позицію" @@ -6307,12 +6751,21 @@ msgstr "Заблоковано" msgid "Grouped" msgstr "Згруповано" +msgid "Add Node Here..." +msgstr "Додати вузол тут..." + +msgid "Instantiate Scene Here..." +msgstr "Миттєва сцена тут..." + msgid "Paste Node(s) Here" msgstr "Вставити вузол(и) сюди" msgid "Move Node(s) Here" msgstr "Пересунути вузол(и) сюди" +msgid "px" +msgstr "пкс." + msgid "units" msgstr "одиниць" @@ -6580,6 +7033,9 @@ msgstr "Кадрувати вибране" msgid "Preview Canvas Scale" msgstr "Попередній перегляд масштабованого полотна" +msgid "Project theme" +msgstr "Тема проекту" + msgid "Translation mask for inserting keys." msgstr "Маска перенесення для вставляння ключових кадрів." @@ -6632,6 +7088,10 @@ msgid "Drag and drop to add as child of current scene's root node." msgstr "" "Перетягніть, щоб додати дочірній елемент кореневому вузлу поточної сцени." +msgid "Hold %s when dropping to add as child of selected node." +msgstr "" +"Утримуйте %s при скиданні, щоб додати дочірню вершину до вибраного вузла." + msgid "Hold Shift when dropping to add as sibling of selected node." msgstr "" "Утримуйте Shift під час перетягування, щоб додати брата вибраному вузлу." @@ -6930,6 +7390,19 @@ msgstr "" "Якщо увімкнено цей параметр, у запущеному проєкті буде показано навігаційні " "сітки та полігони." +msgid "Debug CanvasItem Redraws" +msgstr "Налагодження перемальовувань CanvasItem" + +msgid "" +"When this option is enabled, redraw requests of 2D objects will become " +"visible (as a short flash) in the running project.\n" +"This is useful to troubleshoot low processor mode." +msgstr "" +"Якщо цю опцію увімкнено, запити на перемальовування 2D-об'єктів стануть " +"видимими (у вигляді короткого спалаху) у запущеному проекті.\n" +"Це корисно для усунення несправностей у режимі низької завантаженості " +"процесора." + msgid "Synchronize Scene Changes" msgstr "Синхронізувати зміни сцени" @@ -6978,6 +7451,15 @@ msgstr[0] "Запустити %d вставлений екземпляр" msgstr[1] "Запустити %d вставлені екземпляри" msgstr[2] "Запустити %d вставлених екземплярів" +msgid "Size: %s" +msgstr "Розмір: %s" + +msgid "Type: %s" +msgstr "Введіть: %s" + +msgid "Dimensions: %d × %d" +msgstr "Розміри: %d × %d" + msgid "Overrides (%d)" msgstr "Перевизначення (%d)" @@ -7171,6 +7653,12 @@ msgstr "" msgid "Select path for SDF Texture" msgstr "Виберіть шлях до текстури SDF" +msgid "Reverse Gradient" +msgstr "Зворотний градієнт" + +msgid "Reverse/Mirror Gradient" +msgstr "Зворотний/дзеркальний градієнт" + msgid "Swap GradientTexture2D Fill Points" msgstr "Поміняти точки заповнення GradientTexture2D" @@ -7208,6 +7696,9 @@ msgstr "Коренева сцена редактора не знайдена." msgid "Lightmap data is not local to the scene." msgstr "Дані карти світла не є локальними для сцени." +msgid "Maximum texture size is too small for the lightmap images." +msgstr "Максимальний розмір текстури замалий для зображень з лайтмапами." + msgid "Bake Lightmaps" msgstr "Запікати карти освітлення" @@ -7283,6 +7774,12 @@ msgstr "Розгорнути UV2" msgid "Contained Mesh is not of type ArrayMesh." msgstr "Вбудована сітка не має типу ArrayMesh." +msgid "Only triangles are supported for lightmap unwrap." +msgstr "Для розгортання лайтмапами підтримуються лише трикутники." + +msgid "Normals are required for lightmap unwrap." +msgstr "Для розгортання лайтмапами потрібні нормалі." + msgid "UV Unwrap failed, mesh may not be manifold?" msgstr "UV-розгортка не вдалася, можливо у сітки не однозв'язна форма?" @@ -7510,6 +8007,9 @@ msgstr "Задати start_position" msgid "Set end_position" msgstr "Задати end_position" +msgid "Set NavigationObstacle3D Vertices" +msgstr "Встановити вершини NavigationObstacle3D" + msgid "Edit Poly" msgstr "Редагувати полігон" @@ -7519,6 +8019,16 @@ msgstr "Редагувати полігон (вилучити точку)" msgid "Create Navigation Polygon" msgstr "Створення навігаційного полігону" +msgid "" +"Bakes the NavigationPolygon by first parsing the scene for source geometry " +"and then creating the navigation polygon vertices and polygons." +msgstr "" +"Створює NavigationPolygon, спочатку розбираючи сцену на вихідну геометрію, а " +"потім створюючи вершини та полігони навігаційного полігону." + +msgid "Clears the internal NavigationPolygon outlines, vertices and polygons." +msgstr "Очищає внутрішні контури, вершини та полігони NavigationPolygon." + msgid "Unnamed Gizmo" msgstr "Гаджет без назви" @@ -7588,6 +8098,15 @@ msgstr "Вставлення ключа вимкнено (ключ не вста msgid "Animation Key Inserted." msgstr "Вставлено ключ анімації." +msgid "X: %s\n" +msgstr "X: %s\n" + +msgid "Y: %s\n" +msgstr "Y: %s\n" + +msgid "Z: %s\n" +msgstr "Z: %s\n" + msgid "Size: %s (%.1fMP)\n" msgstr "Розмір: %s (%.1fMP)\n" @@ -7654,6 +8173,15 @@ msgstr "Перенесення:" msgid "Rotating %s degrees." msgstr "Обертання на %s градусів." +msgid "Translating %s." +msgstr "Переклад %s." + +msgid "Rotating %f degrees." +msgstr "Поворот на %f градусів." + +msgid "Scaling %s." +msgstr "Масштабування %s." + msgid "Auto Orthogonal Enabled" msgstr "Увімкнено автоматичну ортогоналізацію" @@ -7705,6 +8233,12 @@ msgstr "Зонди SDFGI" msgid "Scene Luminance" msgstr "Яскравість сцени" +msgid "SSAO" +msgstr "SSAO" + +msgid "SSIL" +msgstr "SSIL" + msgid "VoxelGI/SDFGI Buffer" msgstr "Буфер VoxelGI/SDFGI" @@ -7717,6 +8251,9 @@ msgstr "Кластер OmniLight3D" msgid "SpotLight3D Cluster" msgstr "Кластер SpotLight3D" +msgid "Decal Cluster" +msgstr "Кластер наклейок" + msgid "ReflectionProbe Cluster" msgstr "Кластер ReflectionProbe" @@ -7825,6 +8362,13 @@ msgstr "" msgid "Overriding material..." msgstr "Замінювання матеріалу..." +msgid "" +"Drag and drop to override the material of any geometry node.\n" +"Hold %s when dropping to override a specific surface." +msgstr "" +"Перетягніть, щоб замінити матеріал будь-якого вузла геометрії.\n" +"Утримуйте %s при перетягуванні, щоб замінити певну поверхню." + msgid "XForm Dialog" msgstr "Вікно XForm" @@ -8198,6 +8742,12 @@ msgstr "Віддзеркалити довжини елемента керува msgid "Curve Point #" msgstr "Точку кривої #" +msgid "Handle In #" +msgstr "Handle In #" + +msgid "Handle Tilt #" +msgstr "Нахил ручки #" + msgid "Set Curve Point Position" msgstr "Задати положення точки кривої" @@ -8430,6 +8980,27 @@ msgstr "Не вдається отримати скрипт для переза msgid "Reload only takes effect on tool scripts." msgstr "Перезавантаження діє лише для скриптів інструментів." +msgid "Cannot run the edited file because it's not a script." +msgstr "Неможливо запустити відредагований файл, оскільки він не є скриптом." + +msgid "Cannot run the script because it contains errors, check the output log." +msgstr "" +"Не вдається запустити скрипт, оскільки він містить помилки, перевірте журнал " +"виводу." + +msgid "Cannot run the script because it doesn't extend EditorScript." +msgstr "Неможливо запустити скрипт, оскільки він не розширює EditorScript." + +msgid "" +"Cannot run the script because it's not a tool script (add the @tool " +"annotation at the top)." +msgstr "" +"Неможливо запустити скрипт, оскільки він не є інструментальним (додайте " +"зверху анотацію @tool)." + +msgid "Cannot run the script because it's not a tool script." +msgstr "Неможливо запустити скрипт, оскільки він не є інструментальним." + msgid "Import Theme" msgstr "Імпортувати тему" @@ -8545,6 +9116,9 @@ msgstr "" msgid "Search Results" msgstr "Результати пошуку" +msgid "There are unsaved changes in the following built-in script(s):" +msgstr "У наступних вбудованих скриптах є незбережені зміни:" + msgid "Clear Recent Scripts" msgstr "Спорожнити список нещодавніх скриптів" @@ -8554,6 +9128,9 @@ msgstr "Стандартний" msgid "Plain Text" msgstr "Звичайний текст" +msgid "JSON" +msgstr "ДЖЕСОН" + msgid "Connections to method:" msgstr "З'єднання з методом:" @@ -8581,6 +9158,14 @@ msgstr "Рядок" msgid "Go to Function" msgstr "Перейти до функції" +msgid "" +"The resource does not have a valid path because it has not been saved.\n" +"Please save the scene or resource that contains this resource and try again." +msgstr "" +"Ресурс не має правильного шляху, оскільки його не було збережено.\n" +"Будь ласка, збережіть сцену або ресурс, який містить цей ресурс, і спробуйте " +"ще раз." + msgid "Can't drop nodes without an open scene." msgstr "Неможливо скинути вузли без відкритої сцени." @@ -8713,6 +9298,9 @@ msgstr "Відкрити файл в Інспекторі" msgid "Close File" msgstr "Закрити файл" +msgid "Make the shader editor floating." +msgstr "Зробіть редактор шейдерів плаваючим." + msgid "No valid shader stages found." msgstr "Допустимих етапів шейдера не знайдено." @@ -8739,7 +9327,7 @@ msgid "Create Rest Pose from Bones" msgstr "Створити вільну позу з кісток" msgid "Skeleton2D" -msgstr "Skeleton2D" +msgstr "Skeleton2D (Скелет 2Д)" msgid "Reset to Rest Pose" msgstr "Відновити вільну позу" @@ -8756,6 +9344,9 @@ msgstr "Не можна створити фізичний скелет для в msgid "Create physical bones" msgstr "Створити фізичний кістяк" +msgid "Cannot export a SkeletonProfile for a Skeleton3D node with no bones." +msgstr "Неможливо експортувати SkeletonProfile для вузла Skeleton3D без кісток." + msgid "Export Skeleton Profile As..." msgstr "Експортувати профіль скелета як..." @@ -8763,7 +9354,7 @@ msgid "Set Bone Parentage" msgstr "Задати кістці предка" msgid "Skeleton3D" -msgstr "Skeleton3D" +msgstr "Skeleton3D (Скелет 3Д)" msgid "Reset All Bone Poses" msgstr "Скинути всі пози кісток" @@ -8850,7 +9441,7 @@ msgid "Create LightOccluder2D Sibling" msgstr "Створити близнюка LightOccluder2D" msgid "Sprite2D" -msgstr "Sprite2D" +msgstr "Спрайт2Д" msgid "Simplification:" msgstr "Спрощення:" @@ -8954,9 +9545,42 @@ msgstr "Перемістити кадр вправо" msgid "Select Frames" msgstr "Вибрати кадри" +msgid "Frame Order" +msgstr "Порядок кадрів" + +msgid "By Row" +msgstr "По рядах" + +msgid "Left to Right, Top to Bottom" +msgstr "Зліва направо, зверху вниз" + +msgid "Left to Right, Bottom to Top" +msgstr "Зліва направо, знизу вгору" + +msgid "Right to Left, Top to Bottom" +msgstr "Справа наліво, зверху вниз" + +msgid "Right to Left, Bottom to Top" +msgstr "Справа наліво, знизу вгору" + +msgid "By Column" +msgstr "По колонці" + +msgid "Horizontal" +msgstr "Горизонтальний" + +msgid "Vertical" +msgstr "Вертикальний" + msgid "Size" msgstr "Розмір" +msgid "Separation" +msgstr "Розлука" + +msgid "Offset" +msgstr "Зміщення" + msgid "Create Frames from Sprite Sheet" msgstr "Створити кадри з аркуша спрайтів" @@ -8973,6 +9597,9 @@ msgstr "" "До цього шейдера внесено зміни на диску.\n" "Що слід зробити?" +msgid "%s Mipmaps" +msgstr "%s Mip-карти" + msgid "Memory: %s" msgstr "Пам'ять: %s" @@ -9566,6 +10193,9 @@ msgstr "Перезавантажити сцену для відтворення msgid "Merge TileSetAtlasSource" msgstr "Об'єднати TileSetAtlasSource" +msgid "%s (ID: %d)" +msgstr "%s (АЙДИ: %d)" + msgid "Atlas Merging" msgstr "Об'єднати атласи" @@ -9599,6 +10229,13 @@ msgstr "" "Координати атласу: %s\n" "Альтернатива: %d" +msgid "" +"The selected atlas source has no valid texture. Assign a texture in the " +"TileSet bottom tab." +msgstr "" +"Вибране джерело атласу не має правильної текстури. Призначте текстуру на " +"нижній вкладці TileSet." + msgid "Base Tiles" msgstr "Базова плитка" @@ -9671,6 +10308,9 @@ msgstr "Набір для малювання місцевості" msgid "Painting Terrain" msgstr "Малювання місцевості" +msgid "Can't rotate patterns when using non-square tile grid." +msgstr "Неможливо обертати шаблони при використанні неквадратної сітки плиток." + msgid "No Texture Atlas Source (ID: %d)" msgstr "Нема джерела текстурного атласу (ID: %d)" @@ -9686,12 +10326,25 @@ msgstr "Додати шаблон Набору плиток" msgid "Remove TileSet patterns" msgstr "Вилучити шаблон Набору плиток" +msgid "Index: %d" +msgstr "Індекс: %d" + msgid "Tile with Invalid Scene" msgstr "Плитка з неприпустимою сценою" +msgid "" +"The selected scene collection source has no scenes. Add scenes in the TileSet " +"bottom tab." +msgstr "" +"Вибране джерело колекції сцен не містить сцен. Додайте сцени на нижній " +"вкладці TileSet." + msgid "Delete tiles" msgstr "Видалити плитки" +msgid "Drawing Rect:" +msgstr "Прямокутник для малювання:" + msgid "Change selection" msgstr "Змінити вибір" @@ -9720,6 +10373,12 @@ msgstr "Лінія" msgid "Rect" msgstr "Прямокутник" +msgid "Bucket" +msgstr "Відро" + +msgid "Alternatively hold %s with other tools to pick tile." +msgstr "Або утримуйте %s іншими інструментами, щоб вибрати плитку." + msgid "Eraser" msgstr "Гумка" @@ -9817,6 +10476,13 @@ msgstr "Перемкнути видимість ґратки." msgid "Automatically Replace Tiles with Proxies" msgstr "Автоматична заміна плиток на проксі" +msgid "" +"The edited TileMap node has no TileSet resource.\n" +"Create or load a TileSet resource in the Tile Set property in the inspector." +msgstr "" +"Відредагований вузол TileMap не має ресурсу TileSet.\n" +"Створіть або завантажте ресурс TileSet у властивості Tile Set в інспекторі." + msgid "Remove Tile Proxies" msgstr "Видалити проксі плиток" @@ -9906,15 +10572,37 @@ msgstr "Фізика" msgid "Physics Layer %d" msgstr "Фізичний шар %d" +msgid "No physics layers" +msgstr "Ніяких фізичних шарів" + +msgid "" +"Create and customize physics layers in the inspector of the TileSet resource." +msgstr "Створюйте та налаштовуйте фізичні шари в інспекторі ресурсу TileSet." + msgid "Navigation Layer %d" msgstr "Шар навігації %d" +msgid "No navigation layers" +msgstr "Немає навігаційних шарів" + +msgid "" +"Create and customize navigation layers in the inspector of the TileSet " +"resource." +msgstr "" +"Створюйте та налаштовуйте навігаційні шари в інспекторі ресурсу TileSet." + msgid "Custom Data" msgstr "Спеціальні дані" msgid "Custom Data %d" msgstr "Спеціальні дані %d" +msgid "" +"Create and customize custom data layers in the inspector of the TileSet " +"resource." +msgstr "" +"Створюйте та налаштовуйте кастомні шари даних в інспекторі ресурсу TileSet." + msgid "Select a property editor" msgstr "Вибрати редактор властивостей" @@ -9965,6 +10653,13 @@ msgstr "Вибрати плитки." msgid "Paint properties." msgstr "Властивості малювання." +msgid "" +"No tiles selected.\n" +"Select one or more tiles from the palette to edit its properties." +msgstr "" +"Не вибрано жодної плитки.\n" +"Виберіть одну або декілька плиток з палітри, щоб змінити їхні властивості." + msgid "Paint Properties:" msgstr "Властивості малювання:" @@ -9974,6 +10669,13 @@ msgstr "Створення плиток у непрозорих областях msgid "Remove Tiles in Fully Transparent Texture Regions" msgstr "Видалити плитки у повністю прозорих областях текстури" +msgid "" +"The current atlas source has tiles outside the texture.\n" +"You can clear it using \"%s\" option in the 3 dots menu." +msgstr "" +"Поточне джерело атласу має плитки за межами текстури.\n" +"Ви можете очистити його за допомогою опції \"%s\" у меню 3 крапки." + msgid "Create an Alternative Tile" msgstr "Створити альтернативну плитку" @@ -10017,9 +10719,31 @@ msgstr "Відкрити інструмент злиття атласів" msgid "Manage Tile Proxies" msgstr "Керівник проксі плиток" +msgid "" +"No TileSet source selected. Select or create a TileSet source.\n" +"You can create a new source by using the Add button on the left or by " +"dropping a tileset texture onto the source list." +msgstr "" +"Не вибрано жодного джерела TileSet.\n" +"Виберіть або створіть джерело TileSet'а. Ви можете створити нове джерело за " +"допомогою кнопки Додати ліворуч або перетягнувши текстуру набору плиток до " +"списку джерел." + msgid "Add new patterns in the TileMap editing mode." msgstr "Додати нові візерунки в режимі редагування Карти плиток." +msgid "" +"Warning: Modifying a source ID will result in all TileMaps using that source " +"to reference an invalid source instead. This may result in unexpected data " +"loss. Change this ID carefully." +msgstr "" +"Попередження: Зміна ідентифікатора джерела призведе до того, що всі TileMaps, " +"які використовують це джерело, посилатимуться на недійсне джерело. Це може " +"призвести до неочікуваної втрати даних. Змінюйте цей ідентифікатор обережно." + +msgid "ID: %d" +msgstr "ID: %d" + msgid "Add a Scene Tile" msgstr "Додайте плитку сцени" @@ -10256,6 +10980,12 @@ msgstr "Додати вхід" msgid "Add Output" msgstr "Додати вихід" +msgid "Float" +msgstr "Поплавок" + +msgid "UInt" +msgstr "Ю-інт" + msgid "Boolean" msgstr "Булеве" @@ -10984,6 +11714,12 @@ msgstr "" "Повертає значення глибини, отримане з попереднього проходу глибини в " "лінійному просторі." +msgid "Reconstructs the World Position of the Node from the depth texture." +msgstr "Відновлює світову позицію вузла на основі текстури глибини." + +msgid "Unpacks the Screen Normal Texture in World Space" +msgstr "Розпаковує нормальну текстуру екрана у світовому просторі" + msgid "Perform the 2D texture lookup." msgstr "Виконує пошук 2D текстури." @@ -11091,6 +11827,9 @@ msgstr "" msgid "Remaps a given input from the input range to the output range." msgstr "Відображає вхідні дані з діапазону вводу у діапазон виводу." +msgid "Rotates an input vector by a given angle." +msgstr "Повертає вхідний вектор на заданий кут." + msgid "Vector function." msgstr "Векторна функція." @@ -11337,6 +12076,9 @@ msgstr "Отримати параметр варіації." msgid "Set varying parameter." msgstr "Встановити параметр варіації." +msgid "Edit Visual Property: %s" +msgstr "Редагувати візуальну властивість: %s" + msgid "Visual Shader Mode Changed" msgstr "Змінено режим візуального шейдерів" @@ -11372,6 +12114,13 @@ msgid "This directory already contains a Godot project." msgstr "У цьому каталозі вже міститься проєкт Godot." msgid "" +"You cannot save a project in the selected path. Please make a new folder or " +"choose a new path." +msgstr "" +"Ви не можете зберегти проект у вибраному шляху. Будь ласка, створіть нову " +"папку або виберіть новий шлях." + +msgid "" "The selected path is not empty. Choosing an empty folder is highly " "recommended." msgstr "" @@ -11525,6 +12274,9 @@ msgstr "Метадані керування версіями:" msgid "Git" msgstr "Git" +msgid "This project was last edited in a different Godot version: " +msgstr "Цей проект востаннє редагувався в іншій версії Godot: " + msgid "This project uses features unsupported by the current build:" msgstr "Цей проект використовує функції, які не підтримуються поточною збіркою:" @@ -11678,8 +12430,8 @@ msgid "" "Godot %s.\n" "\n" msgstr "" -"Попередження: Цей проект було створено на мові Godot %s.\n" -"Відкриття проекту покращить або погіршить його якість до версії Godot %s.\n" +"Попередження: Цей проект востаннє було відредаговано у Godot %s.При відкритті " +"буде змінено на Godot %s.\n" "\n" msgid "" @@ -11718,6 +12470,9 @@ msgstr "" msgid "Are you sure to run %d projects at once?" msgstr "Ви справді хочете запустити %d проєктів одночасно?" +msgid "Tag name must be lowercase." +msgstr "Ім'я тегу повинно бути маленькими літерами." + msgid "Remove %d projects from the list?" msgstr "Вилучити зі списку %d проєктів?" @@ -11780,6 +12535,9 @@ msgstr "" msgid "Last Edited" msgstr "Останнє редагування" +msgid "Tags" +msgstr "Теги" + msgid "Edit Project" msgstr "Редагувати проєкт" @@ -11814,6 +12572,18 @@ msgstr "" "Наразі у вас немає проєктів.\n" "Бажаєте переглянути офіційні приклади проєктів з бібліотеки ресурсів?" +msgid "Click tag to remove it from the project." +msgstr "Клацніть тег, щоб видалити його з проекту." + +msgid "Click tag to add it to the project." +msgstr "Натисніть на тег, щоб додати його до проекту." + +msgid "Create New Tag" +msgstr "Створити новий тег" + +msgid "Tags are capitalized automatically when displayed." +msgstr "Теги автоматично починаються з великої літери при відображенні." + msgid "Add Project Setting" msgstr "Додавання параметрів проекту" @@ -12001,6 +12771,9 @@ msgstr "Файл уже існує." msgid "Invalid root node name." msgstr "Некоректна назва кореневого вузла." +msgid "Invalid root node name characters have been replaced." +msgstr "Замінено невірні символи імені кореневого вузла." + msgid "Root Type:" msgstr "Тип кореня:" @@ -12019,6 +12792,13 @@ msgstr "Назва сцени:" msgid "Root Name:" msgstr "Назва кореня:" +msgid "" +"When empty, the root node name is derived from the scene name based on the " +"\"editor/naming/node_name_casing\" project setting." +msgstr "" +"Якщо порожньо, ім'я кореневого вузла походить від імені сцени на основі " +"параметра проекту \"editor/naming/node_name_casing\"." + msgid "Scene name is valid." msgstr "Назва сцени коректна." @@ -12094,6 +12874,9 @@ msgstr "Вилучити вузол «%s» і його дочірні запис msgid "Delete node \"%s\"?" msgstr "Вилучити вузол «%s»?" +msgid "Some nodes are referenced by animation tracks." +msgstr "На деякі вузли посилаються доріжки анімації." + msgid "Saving the branch as a scene requires having a scene open in the editor." msgstr "" "Щоб можна було зберегти гілку як сцену, сцена має бути відкрита у редакторі." @@ -12273,9 +13056,36 @@ msgstr "" msgid "Can't paste root node into the same scene." msgstr "Не можна вставляти кореневий вузол до сцени цього кореневого вузла." +msgid "Paste Node(s) as Sibling of %s" +msgstr "Вставити Вузол(и) як брата або сестру %s" + +msgid "Paste Node(s) as Child of %s" +msgstr "Вставити вузол(и) як дочірній до %s" + +msgid "<Unnamed> at %s" +msgstr "<Unnamed> at %s" + +msgid "Add Child Node..." +msgstr "Додати дочірній вузол..." + +msgid "Instantiate Child Scene..." +msgstr "Миттєва дитяча сцена..." + msgid "Expand/Collapse Branch" msgstr "Розгорнути/Згорнути гілку" +msgid "Paste as Sibling" +msgstr "Вставити як брата або сестру" + +msgid "Change Type..." +msgstr "Тип зміни..." + +msgid "Attach Script..." +msgstr "Прикріпити скрипт..." + +msgid "Extend Script..." +msgstr "Розширити сценарій..." + msgid "Reparent to New Node" msgstr "Змінити батьківський вузол на новий" @@ -12298,6 +13108,20 @@ msgstr "" "Створити екземпляр файла сцени як вузол. Створює успадковану сцену, якщо " "кореневого вузла не існує." +msgid "Filter: name, t:type, g:group" +msgstr "Фільтр: назва, t:тип, g:група" + +msgid "" +"Filter nodes by entering a part of their name, type (if prefixed with \"type:" +"\" or \"t:\")\n" +"or group (if prefixed with \"group:\" or \"g:\"). Filtering is case-" +"insensitive." +msgstr "" +"Відфільтруйте вузли, ввівши частину їхньої назви, тип (з префіксом \"type:\" " +"або \"t:\")\n" +"або групу (з префіксом \"group:\" або \"g:\"). Фільтрація не залежить від " +"регістру." + msgid "Attach a new or existing script to the selected node." msgstr "Долучити новий або наявний скрипт до позначеного вузла." @@ -12477,6 +13301,9 @@ msgstr "Створити шейдер" msgid "Set Shader Global Variable" msgstr "Встановити глобальну змінну шейдера" +msgid "Please specify a valid shader uniform identifier name." +msgstr "Будь ласка, вкажіть правильне ім'я ідентифікатора шейдерної уніформи." + msgid "Global shader parameter '%s' already exists'" msgstr "Глобальний параметр шейдера '%s' вже існує'" @@ -12486,6 +13313,29 @@ msgstr "Назва '%s' є зарезервованим ключовим сло msgid "Add Shader Global Parameter" msgstr "Додати глобальний параметр шейдера" +msgid "" +"This project uses meshes with an outdated mesh format from previous Godot " +"versions. The engine needs to update the format in order to use those meshes. " +"Please use the 'Upgrade Mesh Surfaces' tool from the 'Project > Tools' menu. " +"You can ignore this message and keep using outdated meshes, but keep in mind " +"that this leads to increased load times every time you load the project." +msgstr "" +"У цьому проекті використовуються сіті із застарілим форматом з попередніх " +"версій Godot. Рушій повинен оновити формат, щоб використовувати ці сіті. Будь " +"ласка, скористайтеся інструментом \"Upgrade Mesh Surfaces\" з меню \"Project " +"> Tools\". Ви можете проігнорувати це повідомлення і продовжувати " +"використовувати застарілі сіті, але майте на увазі, що це призведе до " +"збільшення часу завантаження при кожному завантаженні проекту." + +msgid "" +"This project uses meshes with an outdated mesh format. Check the output log." +msgstr "" +"У цьому проекті використовуються сіті із застарілим форматом. Перевірте лог " +"виводу." + +msgid "Select Screen" +msgstr "Виберіть екран" + msgid "Change Cylinder Radius" msgstr "Змінити радіус циліндра" @@ -12528,6 +13378,9 @@ msgstr "Некоректний формат словника екземпляр msgid "Invalid instance dictionary (invalid subclasses)" msgstr "Некоректний словник екземпляра (некоректні підкласи)" +msgid "Cannot instantiate GDScript class." +msgstr "Не вдається створити екземпляр класу GDScript." + msgid "Value of type '%s' can't provide a length." msgstr "Значення типу '%s' не може мати довжину." @@ -12563,6 +13416,9 @@ msgstr "Шлях не містить інсталятора Blender'а." msgid "Can't execute Blender binary." msgstr "Не вдається запустити файл Blender'а." +msgid "Unexpected --version output from Blender binary at: %s." +msgstr "Неочікуване виведення --версії з бінарного файлу Blender'а при: %s." + msgid "Path supplied lacks a Blender binary." msgstr "У наданому шляху відсутній бінарний файл Blender." @@ -12716,6 +13572,9 @@ msgstr "Пряме освітлення ділянки" msgid "Integrate indirect lighting" msgstr "Інтеграція непрямого освітлення" +msgid "Integrate indirect lighting %d%%" +msgstr "Інтегрувати непряме освітлення %d%%" + msgid "Baking lightprobes" msgstr "Запікання світлових зондів" @@ -12731,6 +13590,9 @@ msgstr "Отримання текстур" msgid "Class name can't be a reserved keyword" msgstr "Назвою класу не може бути зарезервоване ключове слово" +msgid "Class name must be a valid identifier" +msgstr "Ім'я класу має бути дійсним ідентифікатором" + msgid "Not enough bytes for decoding bytes, or invalid format." msgstr "Недостатньо байтів для їх декодування або вказано некоректний формат." @@ -12751,6 +13613,9 @@ msgstr "" msgid "Failed to load .NET runtime" msgstr "Не вдалося завантажити середовище виконання .NET" +msgid ".NET assemblies not found" +msgstr ".NET збірок не знайдено" + msgid "" "Unable to load .NET runtime, specifically hostfxr.\n" "Attempting to create/edit a project will lead to a crash.\n" @@ -12831,6 +13696,9 @@ msgstr "Будь ласка, спочатку виберіть MultiplayerSynchr msgid "The MultiplayerSynchronizer needs a root path." msgstr "MultiplayerSynchronizer потребує кореневого шляху." +msgid "Invalid property path: '%s'" +msgstr "Неправильний шлях до властивості: '%s'" + msgid "Delete Property?" msgstr "Видалити властивість?" @@ -13068,6 +13936,9 @@ msgstr "Запуск на пристрої…" msgid "Could not execute on device." msgstr "Не вдалося виконати на пристрої." +msgid "Android architecture %s not supported in C# projects." +msgstr "Архітектура Android %s не підтримується у проектах C#." + msgid "" "Android build template not installed in the project. Install it from the " "Project menu." @@ -13235,9 +14106,17 @@ msgstr "Не вдалося записати файл пакунка розши msgid "Building Android Project (gradle)" msgstr "Збирання проєкту Android (gradle)" +msgid "Building of Android project failed, check output for the error:" +msgstr "" +"Збірка проекту Android завершилася невдало, перевірте вивід на наявність " +"помилки:" + msgid "Moving output" msgstr "Пересування виведених даних" +msgid "Unable to copy and rename export file:" +msgstr "Неможливо скопіювати та перейменувати файл експорту:" + msgid "Package not found: \"%s\"." msgstr "Пакунок не знайдено: \"%s\"." @@ -13666,6 +14545,15 @@ msgstr "Відсутній розмір піктограми \"%d\"." msgid "Failed to rename temporary file \"%s\"." msgstr "Не вдалося перейменувати тимчасовий файл \"%s\"." +msgid "Invalid icon path." +msgstr "Неправильний шлях до іконки." + +msgid "Invalid file version." +msgstr "Неправильна версія файлу." + +msgid "Invalid product version." +msgstr "Неправильна версія продукту." + msgid "Could not find rcedit executable at \"%s\"." msgstr "Не вдалося знайти виконуваний файл rcedit за адресою \"%s\"." @@ -14452,6 +15340,9 @@ msgstr "" "ButtonGroup призначено для використання лише з кнопками, для яких toggle_mode " "встановлено у true." +msgid "Copy this constructor in a script." +msgstr "Скопіюйте цей конструктор у скрипт." + msgid "" "Color: #%s\n" "LMB: Apply color\n" @@ -15164,6 +16055,12 @@ msgstr "" "Допустимі рівномірні індекси екземплярів повинні знаходитися в діапазоні [0.." "%d]." +msgid "'hint_normal_roughness_texture' is not supported in '%s' shaders." +msgstr "'hint_normal_roughness_texture' не підтримується у шейдерах '%s'." + +msgid "'hint_depth_texture' is not supported in '%s' shaders." +msgstr "'hint_depth_texture' не підтримується у шейдерах '%s'." + msgid "This hint is only for sampler types." msgstr "Ця підказка стосується лише типів семплерів." @@ -15291,6 +16188,9 @@ msgstr "Відсутня умова." msgid "Condition evaluation error." msgstr "Помилка оцінки умови." +msgid "Unmatched else." +msgstr "Неперевершена." + msgid "Invalid else." msgstr "Некоректне else." @@ -15363,6 +16263,9 @@ msgstr "Функція '%s' оголошена, але ніде не викор msgid "The struct '%s' is declared but never used." msgstr "Структура '%s' оголошена, але ніде не використовується." +msgid "The uniform '%s' is declared but never used." +msgstr "Уніфікований символ '%s' декларується, але ніколи не використовується." + msgid "The varying '%s' is declared but never used." msgstr "Варіація '%s' оголошена, але ніде не використовується." diff --git a/editor/translations/editor/vi.po b/editor/translations/editor/vi.po index 35e12f2db2..119845453d 100644 --- a/editor/translations/editor/vi.po +++ b/editor/translations/editor/vi.po @@ -18,7 +18,7 @@ # LetterC67 <hoangdeptoong@gmail.com>, 2020, 2021. # Rev <revolnoom7801@gmail.com>, 2021. # SyliawDeV <thanhlongstranger@gmail.com>, 2021. -# IoeCmcomc <hopdaigia2004@gmail.com>, 2021, 2022. +# IoeCmcomc <hopdaigia2004@gmail.com>, 2021, 2022, 2023. # Hung <hungthitkhia@gmail.com>, 2021. # Paweł Fertyk <pfertyk@pfertyk.me>, 2022. # MInhTriet <luckyblockblack@gmail.com>, 2022. @@ -35,13 +35,14 @@ # Anh Hoang Nguyen <iam@hoanganh.dev>, 2023. # Huu Le <huuptag@gmail.com>, 2023. # Phaspez <tramtrimin@gmail.com>, 2023. +# phamminhkha <Phamminhkha.tc@gmail.com>, 2023. msgid "" msgstr "" "Project-Id-Version: Godot Engine editor interface\n" "Report-Msgid-Bugs-To: https://github.com/godotengine/godot\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2023-11-12 10:25+0000\n" -"Last-Translator: Phaspez <tramtrimin@gmail.com>\n" +"PO-Revision-Date: 2023-12-04 01:29+0000\n" +"Last-Translator: IoeCmcomc <hopdaigia2004@gmail.com>\n" "Language-Team: Vietnamese <https://hosted.weblate.org/projects/godot-engine/" "godot/vi/>\n" "Language: vi\n" @@ -49,7 +50,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Weblate 5.2-dev\n" +"X-Generator: Weblate 5.3-dev\n" msgid "Main Thread" msgstr "Chủ đề chính" @@ -132,6 +133,24 @@ msgstr "Trục chưa định danh của bàn di chuột" msgid "Joypad Motion on Axis %d (%s) with Value %.2f" msgstr "Chuyển động của Joypad trên Trục %d (%s) với Giá trị %.2f" +msgid "Bottom Action, Sony Cross, Xbox A, Nintendo B" +msgstr "Hành động phía dưới, Sony X, Xbox A, Nintendo B" + +msgid "Left Action, Sony Square, Xbox X, Nintendo Y" +msgstr "Thao tác bên trái, Sony Vuông, Xbox X, Nintendo Y" + +msgid "Top Action, Sony Triangle, Xbox Y, Nintendo X" +msgstr "Thao tác phía trên, Sony Tam Giác, Xbox Y, Nintendo X" + +msgid "Back, Sony Select, Xbox Back, Nintendo -" +msgstr "Trở về, Sony Select, Xbox Back, Nintendo -" + +msgid "Guide, Sony PS, Xbox Home" +msgstr "Hướng dẫn, Sony PS, Xbox Home" + +msgid "Start, Xbox Menu, Nintendo +" +msgstr "Bắt đầu, Xbox Menu, Nintendo +" + msgid "D-pad Up" msgstr "D-pad Trên" @@ -258,6 +277,9 @@ msgstr "Đối số không hợp lệ để dựng '%s'" msgid "On call to '%s':" msgstr "Khi gọi đến '%s':" +msgid "Built-in script" +msgstr "Tập lệnh có sẵn:" + msgid "B" msgstr "B" @@ -279,6 +301,9 @@ msgstr "PiB" msgid "EiB" msgstr "EiB" +msgid "Example: %s" +msgstr "Ví dụ: %s" + msgid "" "Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or " "'\"'" @@ -290,6 +315,9 @@ msgstr "Hành động với tên '%s' đã tồn tại." msgid "Add Event" msgstr "Thêm Sự kiện" +msgid "Filter by name..." +msgstr "Lọc theo tên..." + msgid "Add" msgstr "Thêm" @@ -602,6 +630,10 @@ msgstr "Dọn dẹp" msgid "Scale Ratio:" msgstr "Tỉ lệ phóng đại:" +msgctxt "Transition Type" +msgid "Sine" +msgstr "Sin" + msgid "Select Tracks to Copy" msgstr "Chọn các Track để sao chép" @@ -641,6 +673,10 @@ msgstr "Thay thế tất cả" msgid "Selection Only" msgstr "Chỉ chọn" +msgctxt "Indentation" +msgid "Tabs" +msgstr "Thẻ" + msgid "Toggle Scripts Panel" msgstr "Hiện/Ẩn bảng tập lệnh" @@ -672,24 +708,36 @@ msgid "" "Target method not found. Specify a valid method or attach a script to the " "target node." msgstr "" -"Phương thức không được tìm thấy. Chỉ định phương thức hợp lệ hoặc đính kèm " -"tập lệnh vào nút mục tiêu." +"Phương thức mục tiêu không được tìm thấy. Chỉ định phương thức hợp lệ hoặc " +"đính kèm tập lệnh vào nút mục tiêu." msgid "Connect to Node:" msgstr "Kết nối đến Nút:" msgid "Connect to Script:" -msgstr "Kết nối Tập lệnh:" +msgstr "Kết nối với tập lệnh:" msgid "From Signal:" msgstr "Từ tín hiệu:" +msgid "Filter Nodes" +msgstr "Lọc các nút" + msgid "Scene does not contain any script." msgstr "Cảnh không chứa tập lệnh nào cả." msgid "Select Method" msgstr "Chọn Phương thức" +msgid "Filter Methods" +msgstr "Lọc phương thức" + +msgid "No method found matching given filters." +msgstr "Không có phương thức nào khớp với bộ lọc này." + +msgid "Script Methods Only" +msgstr "Chỉ tìm phương thức tập lệnh" + msgid "Remove" msgstr "Xóa" @@ -749,6 +797,9 @@ msgstr "Bạn muốn xoá tất cả kết nối từ tín hiệu \"%s\"?" msgid "Signals" msgstr "Tín hiệu" +msgid "Filter Signals" +msgstr "Lọc tín hiệu" + msgid "Are you sure you want to remove all connections from this signal?" msgstr "Bạn có chắc muốn xóa bỏ tất cả kết nối từ tín hiệu này?" @@ -878,18 +929,33 @@ msgstr "Lỗi:" msgid "Copy Error" msgstr "Sao chép lỗi" +msgid "Open C++ Source on GitHub" +msgstr "Mở mã nguồn C++ trên GitHub" + +msgid "Video RAM" +msgstr "RAM video" + msgid "Skip Breakpoints" msgstr "Lờ đi điểm dừng" msgid "Step Into" msgstr "Bước vào" +msgid "Step Over" +msgstr "Bước qua" + msgid "Break" msgstr "Thoát" msgid "Continue" msgstr "Tiếp tục" +msgid "Thread:" +msgstr "Luồng:" + +msgid "Filter Stack Variables" +msgstr "Lọc biến ngăn xếp" + msgid "Breakpoints" msgstr "Điểm dừng" @@ -920,6 +986,9 @@ msgstr "Sử dụng" msgid "Misc" msgstr "Khác" +msgid "Set From Tree" +msgstr "Đặt từ cây" + msgid "Search Replacement For:" msgstr "Tìm kiếm thay thế cho:" @@ -964,6 +1033,18 @@ msgstr "Tìm kiếm tài nguyên thay thế:" msgid "Open" msgstr "Mở" +msgid "" +"The files being removed are required by other resources in order for them to " +"work.\n" +"Remove them anyway? (Cannot be undone.)\n" +"Depending on your filesystem configuration, the files will either be moved to " +"the system trash or deleted permanently." +msgstr "" +"Các tài nguyên khác cần những tệp bị xóa này mới hoạt động được.\n" +"Vẫn xóa hả? (không khôi phục được đâu.)\n" +"Tuỳ vào cấu hình hệ thống tệp của bạn, các tệp trên có thể được di chuyển vào " +"trong thùng rác hệ thống hoặc bị xoá vĩnh viễn." + msgid "Cannot remove:" msgstr "Không thể gỡ bỏ:" @@ -1259,9 +1340,15 @@ msgstr "Lưu" msgid "Reset to Defaults" msgstr "Đặt lại thành mặc định" +msgid "Detect from Project" +msgstr "Phát hiện từ dự án" + msgid "Export Profile" msgstr "Xuất hồ sơ" +msgid "Filter Commands" +msgstr "Lọc lệnh" + msgid "Paste Params" msgstr "Dán các đối số" @@ -1500,6 +1587,9 @@ msgstr "Thuộc tính:" msgid "Signal:" msgstr "Tín hiệu:" +msgid "No description available." +msgstr "Không có mô tả." + msgid "%d match." msgstr "%d khớp." @@ -1591,11 +1681,22 @@ msgid "Thumbnail..." msgstr "Ảnh thu nhỏ..." msgid "Edit Filters" -msgstr "Chỉnh sửa Lọc" +msgstr "Sửa bộ lọc" msgid "Language:" msgstr "Ngôn ngữ:" +msgctxt "Locale" +msgid "Script:" +msgstr "Hệ chữ viết:" + +msgctxt "Locale" +msgid "Script" +msgstr "Hệ chữ viết" + +msgid "Filter Messages" +msgstr "Lọc thông điệp" + msgid "Clear Output" msgstr "Xoá đầu ra" @@ -1732,7 +1833,7 @@ msgid "Save Scene As..." msgstr "Lưu Cảnh thành..." msgid "Current scene not saved. Open anyway?" -msgstr "Cảnh hiện tại chưa lưu. Kệ mở luôn?" +msgstr "Cảnh hiện tại chưa được lưu. Vẫn mở chứ?" msgid "Nothing to undo." msgstr "Không có gì để hoàn tác." @@ -1804,17 +1905,6 @@ msgstr "" "Vô hiệu hoá phần bổ trợ tại '%s' để ngăn những lỗi về sau." msgid "" -"Unable to load addon script from path: '%s' Base type is not EditorPlugin." -msgstr "" -"Không thể tải tập lệnh bổ trợ từ đường dẫn: '%s' Kiểu cơ sở không phải " -"EditorPlugin." - -msgid "Unable to load addon script from path: '%s' Script is not in tool mode." -msgstr "" -"Không thể tải tập lệnh bổ trợ từ đường dẫn: '%s' Tập lệnh không ở trong chế " -"độ công cụ." - -msgid "" "Scene '%s' was automatically imported, so it can't be modified.\n" "To make changes to it, a new inherited scene can be created." msgstr "" @@ -1921,6 +2011,9 @@ msgstr "Mở gần đây" msgid "Save Scene" msgstr "Lưu Cảnh" +msgid "Export As..." +msgstr "Xuất thành..." + msgid "MeshLibrary..." msgstr "Thư viện hình lưới..." @@ -1931,10 +2024,10 @@ msgid "Quit" msgstr "Thoát" msgid "Project" -msgstr "Dự Án" +msgstr "Dự án" msgid "Project Settings..." -msgstr "Cài đặt Dự Án..." +msgstr "Cài đặt Dự án..." msgid "Version Control" msgstr "Theo dõi phiên bản" @@ -2120,7 +2213,7 @@ msgid "Warning!" msgstr "Cảnh báo!" msgid "Main Script:" -msgstr "Tập lệnhchính:" +msgstr "Tập lệnh chính:" msgid "Edit Plugin" msgstr "Chỉnh sửa Tiện ích" @@ -2228,6 +2321,9 @@ msgstr "Cài đặt Trình biên tập" msgid "General" msgstr "Tổng quan" +msgid "Filter Settings" +msgstr "Thiết đặt bộ lọc" + msgid "The editor must be restarted for changes to take effect." msgstr "Thay đổi sẽ được áp dụng sau khi Trình biên tập khởi động lại." @@ -2240,6 +2336,9 @@ msgstr "Tất cả thiết bị" msgid "Device" msgstr "Thiết bị" +msgid "Filter by event..." +msgstr "Lọc theo sự kiện..." + msgid "Storing File:" msgstr "Lưu trữ tệp tin:" @@ -2430,6 +2529,9 @@ msgstr "Xuất các cảnh đã chọn (cùng các phần phụ thuộc)" msgid "Export selected resources (and dependencies)" msgstr "Xuất tài nguyên đã chọn (cùng các phần phụ thuộc)" +msgid "Export as dedicated server" +msgstr "Xuất một máy chủ chuyên biệt" + msgid "Export Mode:" msgstr "Chế độ xuất:" @@ -2528,6 +2630,9 @@ msgstr "Chỉnh sửa các phần phụ thuộc..." msgid "View Owners..." msgstr "Xem các scene sở hữu..." +msgid "Script..." +msgstr "Tập lệnh..." + msgid "Add to Favorites" msgstr "Thêm vào Ưa thích" @@ -2564,6 +2669,9 @@ msgstr "Đổi tên..." msgid "Re-Scan Filesystem" msgstr "Quét lại hệ thống tập tin" +msgid "Filter Files" +msgstr "Lọc tệp" + msgid "" "Scanning Files,\n" "Please Wait..." @@ -2785,8 +2893,11 @@ msgstr "Cảnh báo cấu hình nút:" msgid "Open in Editor" msgstr "Mở trong Trình biên soạn" +msgid "This script is currently running in the editor." +msgstr "Tập lệnh hiện đang chạy trong trình chỉnh sửa." + msgid "Open Script:" -msgstr "Mở Tệp lệnh:" +msgstr "Mở tập lệnh:" msgid "" "Node is locked.\n" @@ -2943,6 +3054,9 @@ msgstr "Tên tập lệnh:" msgid "Activate now?" msgstr "Kích hoạt bây giờ?" +msgid "Script extension is valid." +msgstr "Phần mở rộng tập lệnh hợp lệ." + msgid "Create Polygon" msgstr "Tạo Polygon" @@ -3766,6 +3880,10 @@ msgstr "" msgid "Synchronize Script Changes" msgstr "Đồng bộ hóa thay đổi trong tập lệnh" +msgctxt "Locale" +msgid "Add Script" +msgstr "Thêm hệ chữ viết" + msgid " - Variation" msgstr " - Biến" @@ -4289,6 +4407,12 @@ msgstr "Bật/tắt sắp xếp danh sách phương thức theo bảng chữ cá msgid "Sort" msgstr "Sắp xếp" +msgid "Next Script" +msgstr "Tệp lệnh tiếp theo" + +msgid "Previous Script" +msgstr "Tệp lệnh trước đó" + msgid "File" msgstr "Tệp" @@ -4302,7 +4426,7 @@ msgid "Save All" msgstr "Lưu tất cả" msgid "Copy Script Path" -msgstr "Sao chép đường dẫn tệp lệnh" +msgstr "Sao chép đường dẫn tập lệnh" msgid "Theme" msgstr "Tông màu" @@ -4343,6 +4467,9 @@ msgstr "Tới tài liệu được chỉnh sửa trước đó." msgid "Go to next edited document." msgstr "Tới tài liệu được chỉnh sửa tiếp theo." +msgid "Make the script editor floating." +msgstr "Làm cho tập lệnh nổi lên." + msgid "Discard" msgstr "Hủy" @@ -4356,6 +4483,9 @@ msgstr "" msgid "Search Results" msgstr "Kết quả tìm kiếm" +msgid "Save changes to the following script(s) before quitting?" +msgstr "Lưu thay đổi trong các tập lệnh sau trước khi thoát không?" + msgid "Clear Recent Scripts" msgstr "Dọn các tệp lệnh gần đây" @@ -4766,6 +4896,9 @@ msgstr "Xóa cổng vào" msgid "Remove Output Port" msgstr "Xóa cổng ra" +msgid "Set Comment Description" +msgstr "Đặt mô tả chú thích" + msgid "Set Input Default Port" msgstr "Đặt cổng đầu vào mặc định" @@ -5125,6 +5258,9 @@ msgstr "Đường dẫn cài đặt Dự án:" msgid "Renderer:" msgstr "Trình kết xuất hình ảnh:" +msgid "This project was last edited in a different Godot version: " +msgstr "Dự án này đã được sửa đổi trong một phiên bản Godot khác: " + msgid "Error: Project is missing on the filesystem." msgstr "Lỗi: Dự án bị thiếu trên hệ thống tệp tin." @@ -5134,10 +5270,25 @@ msgstr "Dự án bị lỗi" msgid "Local" msgstr "Cục bộ" +msgid "Local Projects" +msgstr "Dự án cục bộ" + msgid "Can't open project at '%s'." msgstr "Không thể mở dự án tại '%s'." msgid "" +"Warning: This project was last edited in Godot %s. Opening will change it to " +"Godot %s.\n" +"\n" +msgstr "" +"Cảnh báo: Dự án này đã được sửa lần cuối bằng Godot %s. Mở dự án này sẽ thay " +"đổi nó sang Godot %s.\n" +"\n" + +msgid "Open anyway? Project will be modified." +msgstr "Vẫn mở chứ? Dự án sẽ được sửa đổi." + +msgid "" "Can't run project: no main scene defined.\n" "Please edit the project and set the main scene in the Project Settings under " "the \"Application\" category." @@ -5155,6 +5306,9 @@ msgstr "" msgid "Are you sure to run %d projects at once?" msgstr "Bạn có chắc chắn chạy các dự án %d cùng lúc?" +msgid "Remove this project from the list?" +msgstr "Loại bỏ dự án này ra khỏi danh sách chứ?" + msgid "" "Remove all missing projects from the list?\n" "The project folders' contents won't be modified." @@ -5186,6 +5340,21 @@ msgstr "Quét" msgid "Loading, please wait..." msgstr "Đang tải, đợi xíu..." +msgid "Filter Projects" +msgstr "Lọc các dự án" + +msgid "Last Edited" +msgstr "Sửa đổi lần cuối" + +msgid "Tags" +msgstr "Nhãn" + +msgid "Manage Tags" +msgstr "Quản lí nhãn" + +msgid "Remove Missing" +msgstr "Loại bỏ bị mất" + msgid "About" msgstr "Về chúng tôi" @@ -5404,6 +5573,9 @@ msgstr "Lỗi khi lưu scene." msgid "Error duplicating scene to save it." msgstr "Lỗi khi nhân bản cảnh để lưu." +msgid "Instantiate Script" +msgstr "Khởi tạo tập lệnh" + msgid "Sub-Resources" msgstr "Tài nguyên phụ" @@ -5425,6 +5597,15 @@ msgstr "" msgid "Can't paste root node into the same scene." msgstr "Không thể dán Nút Gốc vào cùng một Cảnh." +msgid "Add Child Node..." +msgstr "Thêm nút con..." + +msgid "Attach Script..." +msgstr "Đính kèm tập lệnh..." + +msgid "Extend Script..." +msgstr "Mở rộng tập lệnh..." + msgid "Reparent to New Node" msgstr "Thay nút mẹ thành nút mới" @@ -5438,7 +5619,10 @@ msgid "Add/Create a New Node." msgstr "Thêm/Tạo một nút mới." msgid "Attach a new or existing script to the selected node." -msgstr "Đính kèm một tập lệnh cho nút đã chọn." +msgstr "Đính kèm một tập lệnh mới hoặc đã tồn tại cho nút đã chọn." + +msgid "Detach the script from the selected node." +msgstr "Xoá tập lệnh khỏi nút đã chọn." msgid "Remote" msgstr "Từ xa" @@ -5465,14 +5649,17 @@ msgid "Template:" msgstr "Bản mẫu:" msgid "Error - Could not create script in filesystem." -msgstr "Lỗi - Không thể tạo tệp lệnh trong hệ thống tệp tin." +msgstr "Lỗi - Không thể tạo tập lệnh trong hệ thống tệp." msgid "Error loading script from %s" -msgstr "Lỗi nạp tệp lệnh từ %s" +msgstr "Lỗi nạp tập lệnh từ %s" msgid "Open Script / Choose Location" msgstr "Mở tệp lệnh / Chọn vị trí" +msgid "Open Script" +msgstr "Mở tập lệnh" + msgid "Invalid inherited parent name or path." msgstr "Tên hoặc đường dẫn nút mẹ được kế thừa không hợp lệ." @@ -5489,8 +5676,14 @@ msgstr "Tệp lệnh tích hợp (vào tệp cảnh)." msgid "Will load an existing script file." msgstr "Sẽ nạp một tệp lệnh đã tồn tại." +msgid "Script file already exists." +msgstr "Tệp tập lệnh đã tồn tại." + +msgid "Script path/name is valid." +msgstr "Tên/đường dẫn tập lệnh hợp lệ." + msgid "Will create a new script file." -msgstr "Sẽ tạo một tệp lệnh mới." +msgstr "Sẽ tạo một tệp tập lệnh mới." msgid "Built-in Script:" msgstr "Tệp lệnh có sẵn:" @@ -5531,6 +5724,13 @@ msgstr "Định dạng từ điển không hợp lệ (tệp lệnh không hợp msgid "Invalid instance dictionary (invalid subclasses)" msgstr "Từ điển không hợp lệ (Lớp con không hợp lệ)" +msgid "" +"Invalid type argument for is_instance_of(), should be a TYPE_* constant, a " +"class or a script." +msgstr "" +"Hàm convert() có loại đối số không hợp lệ, hãy sử dụng hằng TYPE_*, một lớp " +"hoặc một tập lệnh." + msgid "Next Plane" msgstr "Mặt phẳng tiếp theo" @@ -5678,6 +5878,9 @@ msgstr "Thiếu định danh." msgid "The character '%s' is not allowed in Identifier." msgstr "Không được phép có kí tự '%s' trong Định danh." +msgid "Uploading scripts..." +msgstr "Đang tải lên tập lệnh..." + msgid "Stop HTTP Server" msgstr "Dừng Máy chủ HTTP" diff --git a/editor/translations/editor/zh_CN.po b/editor/translations/editor/zh_CN.po index 94f4177039..d07c26b59a 100644 --- a/editor/translations/editor/zh_CN.po +++ b/editor/translations/editor/zh_CN.po @@ -101,8 +101,8 @@ msgstr "" "Project-Id-Version: Chinese (Simplified) (Godot Engine)\n" "Report-Msgid-Bugs-To: https://github.com/godotengine/godot\n" "POT-Creation-Date: 2018-01-20 12:15+0200\n" -"PO-Revision-Date: 2023-11-22 04:14+0000\n" -"Last-Translator: Haoyu Qiu <timothyqiu32@gmail.com>\n" +"PO-Revision-Date: 2023-12-07 14:59+0000\n" +"Last-Translator: 风青山 <idleman@yeah.net>\n" "Language-Team: Chinese (Simplified) <https://hosted.weblate.org/projects/" "godot-engine/godot/zh_Hans/>\n" "Language: zh_CN\n" @@ -110,7 +110,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Weblate 5.2\n" +"X-Generator: Weblate 5.3-dev\n" msgid "Main Thread" msgstr "主线程" @@ -3384,28 +3384,28 @@ msgid "Export Mesh Library" msgstr "导出网格库" msgid "Unable to enable addon plugin at: '%s' parsing of config failed." -msgstr "无法在 “%s” 上启用加载项插件:配置解析失败。" +msgstr "无法启用位于 “%s” 的附加插件:配置解析失败。" msgid "Unable to find script field for addon plugin at: '%s'." -msgstr "无法在 “%s” 上找到加载项的 script 字段。" +msgstr "无法在 “%s” 上找到附加组件的 script 字段。" msgid "Unable to load addon script from path: '%s'." -msgstr "无法从路径 “%s” 中加载加载项脚本。" +msgstr "无法从路径 “%s” 加载附加组件脚本。" msgid "" "Unable to load addon script from path: '%s'. This might be due to a code " "error in that script.\n" "Disabling the addon at '%s' to prevent further errors." msgstr "" -"无法从路径“%s”加载加载项脚本:该脚本可能有代码错误。\n" -"禁用加载项“%s”可阻止其进一步报错。" +"无法从路径 “%s” 加载附加组件脚本:该脚本可能有代码错误。\n" +"正在禁用位于 “%s” 的附加组件以阻止其进一步报错。" msgid "" -"Unable to load addon script from path: '%s' Base type is not EditorPlugin." -msgstr "无法从路径 “%s” 加载加载项脚本:基类型不是 EditorPlugin。" +"Unable to load addon script from path: '%s'. Base type is not 'EditorPlugin'." +msgstr "无法从路径 “%s” 加载附加组件脚本。基类型不是 “EditorPlugin”。" -msgid "Unable to load addon script from path: '%s' Script is not in tool mode." -msgstr "无法从路径 “%s” 加载插件脚本:脚本不在工具模式下。" +msgid "Unable to load addon script from path: '%s'. Script is not in tool mode." +msgstr "无法从路径 “%s” 加载附加组件脚本。脚本不在工具模式下。" msgid "" "Scene '%s' was automatically imported, so it can't be modified.\n" @@ -13584,12 +13584,53 @@ msgstr "名称“%s”是为着色器语言保留的关键字。" msgid "Add Shader Global Parameter" msgstr "添加着色器全局参数" +msgid "" +"This project uses meshes with an outdated mesh format from previous Godot " +"versions. The engine needs to update the format in order to use those meshes. " +"Please use the 'Upgrade Mesh Surfaces' tool from the 'Project > Tools' menu. " +"You can ignore this message and keep using outdated meshes, but keep in mind " +"that this leads to increased load times every time you load the project." +msgstr "" +"该项目使用的网格具有过时的之前 Godot 版本中的网格格式。引擎需要更新格式才能使" +"用这些网格。请使用“项目 > 工具”菜单中的“升级网格表面”工具。你可以忽略该消息并" +"继续使用过时的网格,但请记住,这会导致每次加载项目时加载时间增加。" + +msgid "" +"This project uses meshes with an outdated mesh format. Check the output log." +msgstr "该项目使用具有过时网格格式的网格。请检查输出日志。" + msgid "Upgrading All Meshes in Project" msgstr "正在升级项目中的所有网格" msgid "Attempting to re-save " msgstr "正在尝试重新保存 " +msgid "Attempting to remove " +msgstr "正在尝试移除 " + +msgid "" +"The mesh format has changed in Godot 4.2, which affects both imported meshes " +"and meshes authored inside of Godot. The engine needs to update the format in " +"order to use those meshes.\n" +"\n" +"If your project predates Godot 4.2 and contains meshes, we recommend you run " +"this one time conversion tool. This update will restart the editor and may " +"take several minutes. Upgrading will make the meshes incompatible with " +"previous versions of Godot.\n" +"\n" +"You can still use your existing meshes as is. The engine will update each " +"mesh in memory, but the update will not be saved. Choosing this option will " +"lead to slower load times every time this project is loaded." +msgstr "" +"Godot 4.2 更改了网格的格式,导入的网格和在 Godot 中制作的网格都会受到影响。引" +"擎需要对格式进行升级才能使用这些网格。\n" +"\n" +"如果你的项目早于 Godot 4.2 并且包含网格,我们建议运行这个一次性转换工具。升级" +"时将会重启编辑器,可能需要几分钟时间。更新后的网格不再兼容以前版本的 Godot。\n" +"\n" +"你仍然可以保留现有网格。引擎会在内存中升级各个网格,但是不会保存更新。选择这个" +"选项将会使得每次加载这个项目的时间变长。" + msgid "Restart & Upgrade" msgstr "重启并升级" diff --git a/editor/translations/editor/zh_TW.po b/editor/translations/editor/zh_TW.po index 085a018867..74a5324d67 100644 --- a/editor/translations/editor/zh_TW.po +++ b/editor/translations/editor/zh_TW.po @@ -3324,13 +3324,6 @@ msgstr "" "將停用擴充套件'%s'以避免進一步的錯誤。" msgid "" -"Unable to load addon script from path: '%s' Base type is not EditorPlugin." -msgstr "無法自路徑「%s」載入擴充腳本,基礎型別非 EditorPlugin。" - -msgid "Unable to load addon script from path: '%s' Script is not in tool mode." -msgstr "無法自路徑載入擴充腳本「%s」,腳本不在工具模式下。" - -msgid "" "Scene '%s' was automatically imported, so it can't be modified.\n" "To make changes to it, a new inherited scene can be created." msgstr "" diff --git a/editor/translations/properties/it.po b/editor/translations/properties/it.po index 24c3122939..44b8fa414b 100644 --- a/editor/translations/properties/it.po +++ b/editor/translations/properties/it.po @@ -81,13 +81,14 @@ # Francesco <martefrinbell@gmail.com>, 2023. # E D <th3-crasin3ss@hotmail.com>, 2023. # Frankie McEyes <mceyes@protonmail.com>, 2023. +# Samuele Righi <blackdestinyx145@gmail.com>, 2023. msgid "" msgstr "" "Project-Id-Version: Godot Engine properties\n" "Report-Msgid-Bugs-To: https://github.com/godotengine/godot\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2023-10-17 06:43+0000\n" -"Last-Translator: Frankie McEyes <mceyes@protonmail.com>\n" +"PO-Revision-Date: 2023-12-11 09:59+0000\n" +"Last-Translator: Samuele Righi <blackdestinyx145@gmail.com>\n" "Language-Team: Italian <https://hosted.weblate.org/projects/godot-engine/" "godot-properties/it/>\n" "Language: it\n" @@ -95,7 +96,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 5.1\n" +"X-Generator: Weblate 5.3-dev\n" msgid "Application" msgstr "Applicazione" @@ -214,6 +215,9 @@ msgstr "Generale" msgid "iOS" msgstr "iOS" +msgid "Mix With Others" +msgstr "Mischia Con Altri" + msgid "Editor" msgstr "Editor" @@ -286,6 +290,9 @@ msgstr "Server Multithreading" msgid "Internationalization" msgstr "Lingue" +msgid "Force Right to Left Layout Direction" +msgstr "Forza la Direzione del Layout da Destra a Sinistra" + msgid "GUI" msgstr "Interfaccia Grafica" diff --git a/editor/translations/properties/ja.po b/editor/translations/properties/ja.po index 094d72e904..47df70e320 100644 --- a/editor/translations/properties/ja.po +++ b/editor/translations/properties/ja.po @@ -53,13 +53,14 @@ # matsu7089 <32781959+matsu7089@users.noreply.github.com>, 2023. # Koji Horaguchi <koji.horaguchi@gmail.com>, 2023. # ueshita <nalto32@gmail.com>, 2023. +# Komaru Bomaru <ohmanhowwillithink@gmail.com>, 2023. msgid "" msgstr "" "Project-Id-Version: Godot Engine properties\n" "Report-Msgid-Bugs-To: https://github.com/godotengine/godot\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2023-11-12 18:58+0000\n" -"Last-Translator: ueshita <nalto32@gmail.com>\n" +"PO-Revision-Date: 2023-12-06 04:32+0000\n" +"Last-Translator: Komaru Bomaru <ohmanhowwillithink@gmail.com>\n" "Language-Team: Japanese <https://hosted.weblate.org/projects/godot-engine/" "godot-properties/ja/>\n" "Language: ja\n" @@ -67,7 +68,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Weblate 5.2-dev\n" +"X-Generator: Weblate 5.3-dev\n" msgid "Application" msgstr "アプリケーション" @@ -1653,6 +1654,9 @@ msgstr "圧縮" msgid "Language" msgstr "言語:" +msgid "Embolden" +msgstr "太字" + msgid "Transform" msgstr "トランスフォーム" @@ -1662,6 +1666,9 @@ msgstr "リターゲット" msgid "Make Unique" msgstr "ユニーク化" +msgid "Fix Silhouette" +msgstr "シルエットを直す" + msgid "Filter" msgstr "フィルター" @@ -1677,6 +1684,9 @@ msgstr "作成元" msgid "Delimiter" msgstr "区切り文字" +msgid "Rows" +msgstr "列" + msgid "High Quality" msgstr "高品質" @@ -1779,6 +1789,12 @@ msgstr "高度な設定" msgid "Precision" msgstr "精度" +msgid "Plane Downsampling" +msgstr "平面ダウンサンプリング" + +msgid "Convexhull Downsampling" +msgstr "凸包ダウンサンプリング" + msgid "Height" msgstr "高さ" @@ -1929,6 +1945,9 @@ msgstr "圧縮モードの変更" msgid "SVG" msgstr "SVG" +msgid "Convert Colors With Editor Theme" +msgstr "エディタのテーマで色を変える" + msgid "Atlas File" msgstr "アトラスファイル" @@ -2055,6 +2074,9 @@ msgstr "テクスチャ" msgid "Separation" msgstr "分離" +msgid "Alternative ID" +msgstr "代替ID" + msgid "Speed" msgstr "速さ" @@ -2073,6 +2095,9 @@ msgstr "SSH 秘密鍵パス" msgid "Main Run Args" msgstr "メイン実行引数" +msgid "Naming" +msgstr "名付ける" + msgid "Reimport Missing Imported Files" msgstr "見つからないインポート済みファイルを再インポートする" @@ -2082,6 +2107,12 @@ msgstr "シーンツリーのルート選択対象を表示" msgid "Use Favorites Root Selection" msgstr "お気に入りのルート選択対象を使用" +msgid "Max Chars per Second" +msgstr "秒当たり字" + +msgid "Max Warnings per Second" +msgstr "秒当たり警報" + msgid "File Logging" msgstr "ファイルロギング" @@ -2328,6 +2359,12 @@ msgstr "ポリゴン" msgid "Depth" msgstr "Depth(深度/奥行)" +msgid "Path Interval" +msgstr "パス合間" + +msgid "Path Simplify Angle" +msgstr "パスの角度を容易にする" + msgid "CSG" msgstr "CSG" @@ -2355,6 +2392,9 @@ msgstr "Smart Resolveを有効化" msgid "Use Thread" msgstr "スレッドを使用" +msgid "glTF" +msgstr "glTF" + msgid "Color" msgstr "色" @@ -2370,6 +2410,9 @@ msgstr "鏡面反射係数" msgid "Mass" msgstr "質量" +msgid "Angular Velocity" +msgstr "角速度" + msgid "Json" msgstr "JSON" diff --git a/editor/translations/properties/ko.po b/editor/translations/properties/ko.po index 65223e17c1..ebd2c4bdf5 100644 --- a/editor/translations/properties/ko.po +++ b/editor/translations/properties/ko.po @@ -55,8 +55,8 @@ msgstr "" "Project-Id-Version: Godot Engine properties\n" "Report-Msgid-Bugs-To: https://github.com/godotengine/godot\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2023-11-10 08:03+0000\n" -"Last-Translator: Myeongjin Lee <aranet100@gmail.com>\n" +"PO-Revision-Date: 2023-12-04 01:29+0000\n" +"Last-Translator: nulta <un5450@naver.com>\n" "Language-Team: Korean <https://hosted.weblate.org/projects/godot-engine/godot-" "properties/ko/>\n" "Language: ko\n" @@ -64,7 +64,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Weblate 5.2-dev\n" +"X-Generator: Weblate 5.3-dev\n" msgid "Application" msgstr "어플리케이션" @@ -2014,7 +2014,7 @@ msgid "Resolution" msgstr "해상도" msgid "Max Num Vertices per Convex Hull" -msgstr "컨벡스 헐 당 최대 꼭지점 수" +msgstr "컨벡스 헐 당 최대 꼭짓점 수" msgid "Plane Downsampling" msgstr "평면 다운샘플링" @@ -2032,7 +2032,7 @@ msgid "Max Convex Hulls" msgstr "최대 컨벡스 헐" msgid "Project Hull Vertices" -msgstr "프로젝트 헐 꼭지점" +msgstr "프로젝트 헐 꼭짓점" msgid "Primitive" msgstr "프리미티브" @@ -2155,7 +2155,7 @@ msgid "Import Script" msgstr "스크립트 가져오기" msgid "Normal Map" -msgstr "일반 맵" +msgstr "노멀 맵" msgid "Roughness" msgstr "굵기" @@ -4822,7 +4822,7 @@ msgid "UV" msgstr "UV" msgid "Vertex Colors" -msgstr "꼭짓점 색" +msgstr "버텍스 컬러" msgid "Polygons" msgstr "폴리곤" @@ -4900,7 +4900,7 @@ msgid "Navigation Visibility Mode" msgstr "네비게이션 가시성 모드" msgid "Texture Normal" -msgstr "일반 텍스처" +msgstr "노멀 텍스처" msgid "Texture Pressed" msgstr "눌림 텍스처" @@ -6385,7 +6385,7 @@ msgid "Snap 2D Transforms to Pixel" msgstr "2D 변형을 픽셀에 스냅" msgid "Snap 2D Vertices to Pixel" -msgstr "2D 꼭지점을 픽셀에 스냅" +msgstr "2D 꼭짓점을 픽셀에 스냅" msgid "VRS" msgstr "VRS" @@ -6733,7 +6733,7 @@ msgid "Disable Ambient Light" msgstr "환경광 비활성화" msgid "Vertex Color" -msgstr "꼭짓점 색" +msgstr "버텍스 컬러" msgid "Use as Albedo" msgstr "알베도로 사용" @@ -7476,8 +7476,14 @@ msgstr "프래그먼트" msgid "Cull" msgstr "컬링" +msgid "Skip Vertex Transform" +msgstr "버텍스 변형 건너뛰기" + +msgid "World Vertex Coords" +msgstr "전역 꼭짓점 좌표" + msgid "Vertex Lighting" -msgstr "꼭짓점 조명" +msgstr "버텍스 라이팅" msgid "Shadow Atlas" msgstr "그림자 아틀라스" @@ -7497,6 +7503,9 @@ msgstr "GI" msgid "Overrides" msgstr "오버라이드" +msgid "Force Vertex Shading" +msgstr "버텍스 셰이딩 강제" + msgid "Global Shader Variables" msgstr "전역 셰이더 변수" diff --git a/editor/translations/properties/pl.po b/editor/translations/properties/pl.po index b2acad70ab..9c392350e7 100644 --- a/editor/translations/properties/pl.po +++ b/editor/translations/properties/pl.po @@ -82,13 +82,15 @@ # ThomsikDev <szczopek7@outlook.com>, 2023. # johnny1029 <jkste07@gmail.com>, 2023. # Marcin Zieliński <czolgista83@gmail.com>, 2023. +# Aleksander Łagowiec <mineolek10@users.noreply.hosted.weblate.org>, 2023. msgid "" msgstr "" "Project-Id-Version: Godot Engine properties\n" "Report-Msgid-Bugs-To: https://github.com/godotengine/godot\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2023-11-21 15:04+0000\n" -"Last-Translator: Tomek <kobewi4e@gmail.com>\n" +"PO-Revision-Date: 2023-12-06 04:32+0000\n" +"Last-Translator: Aleksander Łagowiec <mineolek10@users.noreply.hosted.weblate." +"org>\n" "Language-Team: Polish <https://hosted.weblate.org/projects/godot-engine/godot-" "properties/pl/>\n" "Language: pl\n" @@ -97,7 +99,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " "|| n%100>=20) ? 1 : 2;\n" -"X-Generator: Weblate 5.2\n" +"X-Generator: Weblate 5.3-dev\n" msgid "Application" msgstr "Aplikacja" @@ -234,6 +236,9 @@ msgstr "iOS" msgid "Session Category" msgstr "Kategoria sesji" +msgid "Mix With Others" +msgstr "Mieszaj z Innymi" + msgid "Editor" msgstr "Edytor" @@ -294,6 +299,9 @@ msgstr "Formaty" msgid "Zstd" msgstr "Zstd" +msgid "Long Distance Matching" +msgstr "Dopasowanie Na Odległość" + msgid "Compression Level" msgstr "Poziom kompresji" @@ -333,6 +341,9 @@ msgstr "Internacjonalizacja" msgid "Force Right to Left Layout Direction" msgstr "Wymuś kierunek układu od prawej do lewej" +msgid "Root Node Layout Direction" +msgstr "Kierunek układu węzła głównego" + msgid "GUI" msgstr "GUI" @@ -360,14 +371,26 @@ msgstr "Zastosuj nadpróbkowanie" msgid "Rendering Device" msgstr "Urządzenie renderujące" +msgid "Staging Buffer" +msgstr "Bufor przejściowy" + msgid "Block Size (KB)" msgstr "Rozmiar bloku (KB)" msgid "Max Size (MB)" msgstr "Maks. rozmiar (MB)" +msgid "Texture Upload Region Size Px" +msgstr "Rozmiar regionu przesyłania tekstur (w piks.)" + +msgid "Pipeline Cache" +msgstr "Pamięć podręczna Pipeline-u" + +msgid "Save Chunk Size (MB)" +msgstr "Zapisz rozmiar fragmentu (MB)" + msgid "Vulkan" -msgstr "Vulkan" +msgstr "Vulkan (API)" msgid "Max Descriptors per Pool" msgstr "Maks. liczba deskryptorów na pulę" @@ -399,8 +422,11 @@ msgstr "Wygładzanie delty" msgid "Print Error Messages" msgstr "Wyświetlaj komunikaty o błędach" +msgid "Physics Ticks per Second" +msgstr "Ticki Fizyki na Sekundę" + msgid "Max Physics Steps per Frame" -msgstr "Maksymalna liczba kroków fizyki na klatkę" +msgstr "Maksymalna Liczba Kroków Fizyki na Klatkę" msgid "Max FPS" msgstr "Maks. FPS" diff --git a/editor/translations/properties/pt.po b/editor/translations/properties/pt.po index 8846fccaa4..76fcbf5f6a 100644 --- a/editor/translations/properties/pt.po +++ b/editor/translations/properties/pt.po @@ -36,13 +36,14 @@ # Hevinis <meauriocardoso@gmail.com>, 2023. # Ricardo Caetano <ricardofilipecaetano@gmail.com>, 2023. # Henricky Corazza Serrão <henrickyserrao@gmail.com>, 2023. +# Ricardo Bustamante <ricardobqueiroz@gmail.com>, 2023. msgid "" msgstr "" "Project-Id-Version: Godot Engine properties\n" "Report-Msgid-Bugs-To: https://github.com/godotengine/godot\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2023-11-16 16:21+0000\n" -"Last-Translator: Henricky Corazza Serrão <henrickyserrao@gmail.com>\n" +"PO-Revision-Date: 2023-12-07 23:30+0000\n" +"Last-Translator: Ricardo Bustamante <ricardobqueiroz@gmail.com>\n" "Language-Team: Portuguese <https://hosted.weblate.org/projects/godot-engine/" "godot-properties/pt/>\n" "Language: pt\n" @@ -50,7 +51,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 5.2\n" +"X-Generator: Weblate 5.3-dev\n" msgid "Application" msgstr "Aplicação" @@ -184,6 +185,9 @@ msgstr "Força de Panning 3D" msgid "iOS" msgstr "iOS" +msgid "Mix With Others" +msgstr "Misturar Com Outros" + msgid "Editor" msgstr "Editor" @@ -280,6 +284,9 @@ msgstr "Fontes Dinâmicas" msgid "Use Oversampling" msgstr "Usar Sobreamostragem" +msgid "Pipeline Cache" +msgstr "Cache de Pipeline" + msgid "Vulkan" msgstr "Vulkan" @@ -319,6 +326,9 @@ msgstr "Usar Entrada Acumulada" msgid "Input Devices" msgstr "Dispositivos de Entrada" +msgid "Legacy Just Pressed Behavior" +msgstr "Comportamento de Acabou de Pressionar (Legado)" + msgid "Device" msgstr "Aparelho" @@ -379,6 +389,9 @@ msgstr "Valor do Eixo" msgid "Index" msgstr "Índice" +msgid "Double Tap" +msgstr "Toque Duplo" + msgid "Action" msgstr "Ação" @@ -766,6 +779,9 @@ msgstr "FBX" msgid "FBX2glTF Path" msgstr "Caminho para FBX2glTF" +msgid "Oidn Denoise Path" +msgstr "Caminho Oidn de Remoção de Ruído" + msgid "Docks" msgstr "Painéis" @@ -1087,6 +1103,9 @@ msgstr "Tamanho do Contorno dos Ossos" msgid "Viewport Border Color" msgstr "Cor da borda do Viewport" +msgid "Use Integer Zoom by Default" +msgstr "Usar Zoom Inteiro por Padrão" + msgid "Sub Editors Panning Scheme" msgstr "Esquema de painéis do Sub Editor" @@ -1741,6 +1760,12 @@ msgstr "Caminho de Log" msgid "Driver" msgstr "Driver" +msgid "Nvidia Disable Threaded Optimization" +msgstr "Nvidia Desabilitar Otimização em Threads" + +msgid "Force Angle on Devices" +msgstr "Forçar Ângulo em Dispositivos" + msgid "Include Text Server Data" msgstr "Incluir dados de Servidor de Texto" @@ -2041,6 +2066,9 @@ msgstr "Massa" msgid "Angular Velocity" msgstr "Velocidade Angular" +msgid "Is Trigger" +msgstr "É Gatilho" + msgid "Json" msgstr "Json" @@ -2419,6 +2447,9 @@ msgstr "Depurar Senha de Armazenamento de Chaves" msgid "Force System User" msgstr "Forçar Usuário do Sistema" +msgid "Use Wi-Fi for Remote Debug" +msgstr "Use Wi-Fi para Depuração Remota" + msgid "Launcher Icons" msgstr "Ícones do Inicializador" @@ -2449,17 +2480,20 @@ msgstr "Plugins" msgid "Architectures" msgstr "Arquiteturas" +msgid "Keystore" +msgstr "Keystore" + msgid "Debug User" -msgstr "Depurar Utilizador" +msgstr "Usuário de Depuração" msgid "Debug Password" -msgstr "Depurar Palavra-passe" +msgstr "Senha de Depuração" msgid "Release User" -msgstr "Liberar Utilizador" +msgstr "Usuário de Lançamento" msgid "Release Password" -msgstr "Liberar Palavra-passe" +msgstr "Senha de Lançamento" msgid "Code" msgstr "Código" @@ -2479,6 +2513,9 @@ msgstr "Categoria da App" msgid "Exclude From Recents" msgstr "Excluir de Recentes" +msgid "Show in Android Tv" +msgstr "Mostrar em Android TV" + msgid "Graphics" msgstr "Gráficos" @@ -3520,6 +3557,9 @@ msgstr "Flags" msgid "Billboard" msgstr "'Billboard'" +msgid "Double Sided" +msgstr "Dois Lados" + msgid "No Depth Test" msgstr "Sem Teste de Profundidade" @@ -3823,6 +3863,9 @@ msgstr "Nó Raiz" msgid "Root Motion" msgstr "Movimento Raiz" +msgid "Method" +msgstr "Método" + msgid "Reset" msgstr "Repor" @@ -3898,6 +3941,12 @@ msgstr "Forma do Cursor Predefinida" msgid "Scroll Offset" msgstr "Deslocamento da Rolagem" +msgid "Show Grid" +msgstr "Mostrar Grade" + +msgid "Toolbar Menu" +msgstr "Menu de Ferramentas" + msgid "Title" msgstr "Titulo" @@ -4123,6 +4172,9 @@ msgstr "'MSAA 2D'" msgid "MSAA 3D" msgstr "'MSAA 3D'" +msgid "HDR 2D" +msgstr "HDR 2D" + msgid "Use Debanding" msgstr "Usar Debanding" @@ -4285,6 +4337,9 @@ msgstr "1" msgid "2" msgstr "2" +msgid "3" +msgstr "3" + msgid "4" msgstr "4" diff --git a/main/main.cpp b/main/main.cpp index 281ef9a0d6..ef78f45496 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -510,6 +510,7 @@ void Main::print_help(const char *p_binary) { OS::get_singleton()->print(" The target directory must exist.\n"); OS::get_singleton()->print(" --export-debug <preset> <path> Export the project in debug mode using the given preset and output path. See --export-release description for other considerations.\n"); OS::get_singleton()->print(" --export-pack <preset> <path> Export the project data only using the given preset and output path. The <path> extension determines whether it will be in PCK or ZIP format.\n"); + OS::get_singleton()->print(" --install-android-build-template Install the android build template. Used in conjunction with --export-release or --export-debug.\n"); #ifndef DISABLE_DEPRECATED OS::get_singleton()->print(" --convert-3to4 [<max_file_kb>] [<max_line_size>]\n"); OS::get_singleton()->print(" Converts project from Godot 3.x to Godot 4.x.\n"); @@ -765,12 +766,13 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph // Benchmark tracking must be done after `OS::get_singleton()->initialize()` as on some // platforms, it's used to set up the time utilities. - OS::get_singleton()->benchmark_begin_measure("startup_begin"); + OS::get_singleton()->benchmark_begin_measure("Startup", "Total"); + OS::get_singleton()->benchmark_begin_measure("Startup", "Setup"); engine = memnew(Engine); MAIN_PRINT("Main: Initialize CORE"); - OS::get_singleton()->benchmark_begin_measure("core"); + OS::get_singleton()->benchmark_begin_measure("Startup", "Core"); register_core_types(); register_core_driver_types(); @@ -1758,7 +1760,6 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph #endif String default_driver = driver_hints.get_slice(",", 0); - String default_driver_macos = driver_hints_angle.get_slice(",", 1); GLOBAL_DEF_RST_NOVAL("rendering/gl_compatibility/driver", default_driver); GLOBAL_DEF_RST_NOVAL(PropertyInfo(Variant::STRING, "rendering/gl_compatibility/driver.windows", PROPERTY_HINT_ENUM, driver_hints_angle), default_driver); @@ -1766,7 +1767,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph GLOBAL_DEF_RST_NOVAL(PropertyInfo(Variant::STRING, "rendering/gl_compatibility/driver.web", PROPERTY_HINT_ENUM, driver_hints), default_driver); GLOBAL_DEF_RST_NOVAL(PropertyInfo(Variant::STRING, "rendering/gl_compatibility/driver.android", PROPERTY_HINT_ENUM, driver_hints), default_driver); GLOBAL_DEF_RST_NOVAL(PropertyInfo(Variant::STRING, "rendering/gl_compatibility/driver.ios", PROPERTY_HINT_ENUM, driver_hints), default_driver); - GLOBAL_DEF_RST_NOVAL(PropertyInfo(Variant::STRING, "rendering/gl_compatibility/driver.macos", PROPERTY_HINT_ENUM, driver_hints_angle), default_driver_macos); + GLOBAL_DEF_RST_NOVAL(PropertyInfo(Variant::STRING, "rendering/gl_compatibility/driver.macos", PROPERTY_HINT_ENUM, driver_hints_angle), default_driver); GLOBAL_DEF_RST("rendering/gl_compatibility/nvidia_disable_threaded_optimization", true); GLOBAL_DEF_RST("rendering/gl_compatibility/fallback_to_angle", true); @@ -2180,11 +2181,13 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph Thread::release_main_thread(); // If setup2() is called from another thread, that one will become main thread, so preventively release this one. set_current_thread_safe_for_nodes(false); + OS::get_singleton()->benchmark_end_measure("Startup", "Core"); + if (p_second_phase) { return setup2(); } - OS::get_singleton()->benchmark_end_measure("core"); + OS::get_singleton()->benchmark_end_measure("Startup", "Setup"); return OK; error: @@ -2238,7 +2241,8 @@ error: memdelete(message_queue); } - OS::get_singleton()->benchmark_end_measure("core"); + OS::get_singleton()->benchmark_end_measure("Startup", "Core"); + OS::get_singleton()->benchmark_end_measure("Startup", "Setup"); OS::get_singleton()->finalize_core(); locale = String(); @@ -2272,25 +2276,10 @@ Error Main::setup2() { // Print engine name and version print_line(String(VERSION_NAME) + " v" + get_full_version_string() + " - " + String(VERSION_WEBSITE)); - OS::get_singleton()->benchmark_begin_measure("servers"); - - tsman = memnew(TextServerManager); - - if (tsman) { - Ref<TextServerDummy> ts; - ts.instantiate(); - tsman->add_interface(ts); - } - - physics_server_3d_manager = memnew(PhysicsServer3DManager); - physics_server_2d_manager = memnew(PhysicsServer2DManager); - - register_server_types(); - initialize_modules(MODULE_INITIALIZATION_LEVEL_SERVERS); - GDExtensionManager::get_singleton()->initialize_extensions(GDExtension::INITIALIZATION_LEVEL_SERVERS); - #ifdef TOOLS_ENABLED if (editor || project_manager || cmdline_tool) { + OS::get_singleton()->benchmark_begin_measure("Startup", "Initialize Early Settings"); + EditorPaths::create(); // Editor setting class is not available, load config directly. @@ -2352,16 +2341,49 @@ Error Main::setup2() { return FAILED; } } + + OS::get_singleton()->benchmark_end_measure("Startup", "Initialize Early Settings"); } #endif + OS::get_singleton()->benchmark_begin_measure("Startup", "Servers"); + + tsman = memnew(TextServerManager); + if (tsman) { + Ref<TextServerDummy> ts; + ts.instantiate(); + tsman->add_interface(ts); + } + + physics_server_3d_manager = memnew(PhysicsServer3DManager); + physics_server_2d_manager = memnew(PhysicsServer2DManager); + + register_server_types(); + { + OS::get_singleton()->benchmark_begin_measure("Servers", "Modules and Extensions"); + + initialize_modules(MODULE_INITIALIZATION_LEVEL_SERVERS); + GDExtensionManager::get_singleton()->initialize_extensions(GDExtension::INITIALIZATION_LEVEL_SERVERS); + + OS::get_singleton()->benchmark_end_measure("Servers", "Modules and Extensions"); + } + /* Initialize Input */ - input = memnew(Input); + { + OS::get_singleton()->benchmark_begin_measure("Servers", "Input"); + + input = memnew(Input); + OS::get_singleton()->initialize_joypads(); + + OS::get_singleton()->benchmark_end_measure("Servers", "Input"); + } /* Initialize Display Server */ { + OS::get_singleton()->benchmark_begin_measure("Servers", "Display"); + String display_driver = DisplayServer::get_create_function_name(display_driver_idx); Vector2i *window_position = nullptr; @@ -2395,10 +2417,12 @@ Error Main::setup2() { ERR_PRINT("Unable to create DisplayServer, all display drivers failed."); return err; } - } - if (display_server->has_feature(DisplayServer::FEATURE_ORIENTATION)) { - display_server->screen_set_orientation(window_orientation); + if (display_server->has_feature(DisplayServer::FEATURE_ORIENTATION)) { + display_server->screen_set_orientation(window_orientation); + } + + OS::get_singleton()->benchmark_end_measure("Servers", "Display"); } if (GLOBAL_GET("debug/settings/stdout/print_fps") || print_fps) { @@ -2426,48 +2450,58 @@ Error Main::setup2() { /* Initialize Pen Tablet Driver */ { + OS::get_singleton()->benchmark_begin_measure("Servers", "Tablet Driver"); + GLOBAL_DEF_RST_NOVAL("input_devices/pen_tablet/driver", ""); GLOBAL_DEF_RST_NOVAL(PropertyInfo(Variant::STRING, "input_devices/pen_tablet/driver.windows", PROPERTY_HINT_ENUM, "wintab,winink"), ""); - } - if (tablet_driver.is_empty()) { // specified in project.godot - tablet_driver = GLOBAL_GET("input_devices/pen_tablet/driver"); - if (tablet_driver.is_empty()) { - tablet_driver = DisplayServer::get_singleton()->tablet_get_driver_name(0); + if (tablet_driver.is_empty()) { // specified in project.godot + tablet_driver = GLOBAL_GET("input_devices/pen_tablet/driver"); + if (tablet_driver.is_empty()) { + tablet_driver = DisplayServer::get_singleton()->tablet_get_driver_name(0); + } } - } - for (int i = 0; i < DisplayServer::get_singleton()->tablet_get_driver_count(); i++) { - if (tablet_driver == DisplayServer::get_singleton()->tablet_get_driver_name(i)) { - DisplayServer::get_singleton()->tablet_set_current_driver(DisplayServer::get_singleton()->tablet_get_driver_name(i)); - break; + for (int i = 0; i < DisplayServer::get_singleton()->tablet_get_driver_count(); i++) { + if (tablet_driver == DisplayServer::get_singleton()->tablet_get_driver_name(i)) { + DisplayServer::get_singleton()->tablet_set_current_driver(DisplayServer::get_singleton()->tablet_get_driver_name(i)); + break; + } } - } - if (DisplayServer::get_singleton()->tablet_get_current_driver().is_empty()) { - DisplayServer::get_singleton()->tablet_set_current_driver(DisplayServer::get_singleton()->tablet_get_driver_name(0)); - } + if (DisplayServer::get_singleton()->tablet_get_current_driver().is_empty()) { + DisplayServer::get_singleton()->tablet_set_current_driver(DisplayServer::get_singleton()->tablet_get_driver_name(0)); + } - print_verbose("Using \"" + tablet_driver + "\" pen tablet driver..."); + print_verbose("Using \"" + tablet_driver + "\" pen tablet driver..."); + + OS::get_singleton()->benchmark_end_measure("Servers", "Tablet Driver"); + } /* Initialize Rendering Server */ - rendering_server = memnew(RenderingServerDefault(OS::get_singleton()->get_render_thread_mode() == OS::RENDER_SEPARATE_THREAD)); + { + OS::get_singleton()->benchmark_begin_measure("Servers", "Rendering"); - rendering_server->init(); - //rendering_server->call_set_use_vsync(OS::get_singleton()->_use_vsync); - rendering_server->set_render_loop_enabled(!disable_render_loop); + rendering_server = memnew(RenderingServerDefault(OS::get_singleton()->get_render_thread_mode() == OS::RENDER_SEPARATE_THREAD)); - if (profile_gpu || (!editor && bool(GLOBAL_GET("debug/settings/stdout/print_gpu_profile")))) { - rendering_server->set_print_gpu_profile(true); - } + rendering_server->init(); + //rendering_server->call_set_use_vsync(OS::get_singleton()->_use_vsync); + rendering_server->set_render_loop_enabled(!disable_render_loop); - if (Engine::get_singleton()->get_write_movie_path() != String()) { - movie_writer = MovieWriter::find_writer_for_file(Engine::get_singleton()->get_write_movie_path()); - if (movie_writer == nullptr) { - ERR_PRINT("Can't find movie writer for file type, aborting: " + Engine::get_singleton()->get_write_movie_path()); - Engine::get_singleton()->set_write_movie_path(String()); + if (profile_gpu || (!editor && bool(GLOBAL_GET("debug/settings/stdout/print_gpu_profile")))) { + rendering_server->set_print_gpu_profile(true); + } + + if (Engine::get_singleton()->get_write_movie_path() != String()) { + movie_writer = MovieWriter::find_writer_for_file(Engine::get_singleton()->get_write_movie_path()); + if (movie_writer == nullptr) { + ERR_PRINT("Can't find movie writer for file type, aborting: " + Engine::get_singleton()->get_write_movie_path()); + Engine::get_singleton()->set_write_movie_path(String()); + } } + + OS::get_singleton()->benchmark_end_measure("Servers", "Rendering"); } #ifdef UNIX_ENABLED @@ -2477,210 +2511,246 @@ Error Main::setup2() { } #endif - OS::get_singleton()->initialize_joypads(); - /* Initialize Audio Driver */ - AudioDriverManager::initialize(audio_driver_idx); + { + OS::get_singleton()->benchmark_begin_measure("Servers", "Audio"); - print_line(" "); //add a blank line for readability + AudioDriverManager::initialize(audio_driver_idx); - // right moment to create and initialize the audio server + print_line(" "); // Add a blank line for readability. - audio_server = memnew(AudioServer); - audio_server->init(); + // Right moment to create and initialize the audio server. + audio_server = memnew(AudioServer); + audio_server->init(); - // also init our xr_server from here - xr_server = memnew(XRServer); + OS::get_singleton()->benchmark_end_measure("Servers", "Audio"); + } + + /* Initialize XR Server */ + + { + OS::get_singleton()->benchmark_begin_measure("Servers", "XR"); + + xr_server = memnew(XRServer); + + OS::get_singleton()->benchmark_end_measure("Servers", "XR"); + } + + OS::get_singleton()->benchmark_end_measure("Startup", "Servers"); register_core_singletons(); - MAIN_PRINT("Main: Setup Logo"); + /* Initialize the main window and boot screen */ + + { + OS::get_singleton()->benchmark_begin_measure("Startup", "Setup Window and Boot"); + + MAIN_PRINT("Main: Setup Logo"); #if !defined(TOOLS_ENABLED) && (defined(WEB_ENABLED) || defined(ANDROID_ENABLED)) - bool show_logo = false; + bool show_logo = false; #else - bool show_logo = true; + bool show_logo = true; #endif - if (init_windowed) { - //do none.. - } else if (init_maximized) { - DisplayServer::get_singleton()->window_set_mode(DisplayServer::WINDOW_MODE_MAXIMIZED); - } else if (init_fullscreen) { - DisplayServer::get_singleton()->window_set_mode(DisplayServer::WINDOW_MODE_FULLSCREEN); - } - if (init_always_on_top) { - DisplayServer::get_singleton()->window_set_flag(DisplayServer::WINDOW_FLAG_ALWAYS_ON_TOP, true); - } + if (init_windowed) { + //do none.. + } else if (init_maximized) { + DisplayServer::get_singleton()->window_set_mode(DisplayServer::WINDOW_MODE_MAXIMIZED); + } else if (init_fullscreen) { + DisplayServer::get_singleton()->window_set_mode(DisplayServer::WINDOW_MODE_FULLSCREEN); + } + if (init_always_on_top) { + DisplayServer::get_singleton()->window_set_flag(DisplayServer::WINDOW_FLAG_ALWAYS_ON_TOP, true); + } - MAIN_PRINT("Main: Load Boot Image"); + MAIN_PRINT("Main: Load Boot Image"); - Color clear = GLOBAL_DEF_BASIC("rendering/environment/defaults/default_clear_color", Color(0.3, 0.3, 0.3)); - RenderingServer::get_singleton()->set_default_clear_color(clear); + Color clear = GLOBAL_DEF_BASIC("rendering/environment/defaults/default_clear_color", Color(0.3, 0.3, 0.3)); + RenderingServer::get_singleton()->set_default_clear_color(clear); - if (show_logo) { //boot logo! - const bool boot_logo_image = GLOBAL_DEF_BASIC("application/boot_splash/show_image", true); - const String boot_logo_path = String(GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "application/boot_splash/image", PROPERTY_HINT_FILE, "*.png"), String())).strip_edges(); - const bool boot_logo_scale = GLOBAL_DEF_BASIC("application/boot_splash/fullsize", true); - const bool boot_logo_filter = GLOBAL_DEF_BASIC("application/boot_splash/use_filter", true); + if (show_logo) { //boot logo! + const bool boot_logo_image = GLOBAL_DEF_BASIC("application/boot_splash/show_image", true); + const String boot_logo_path = String(GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "application/boot_splash/image", PROPERTY_HINT_FILE, "*.png"), String())).strip_edges(); + const bool boot_logo_scale = GLOBAL_DEF_BASIC("application/boot_splash/fullsize", true); + const bool boot_logo_filter = GLOBAL_DEF_BASIC("application/boot_splash/use_filter", true); - Ref<Image> boot_logo; + Ref<Image> boot_logo; - if (boot_logo_image) { - if (!boot_logo_path.is_empty()) { - boot_logo.instantiate(); - Error load_err = ImageLoader::load_image(boot_logo_path, boot_logo); - if (load_err) { - ERR_PRINT("Non-existing or invalid boot splash at '" + boot_logo_path + "'. Loading default splash."); + if (boot_logo_image) { + if (!boot_logo_path.is_empty()) { + boot_logo.instantiate(); + Error load_err = ImageLoader::load_image(boot_logo_path, boot_logo); + if (load_err) { + ERR_PRINT("Non-existing or invalid boot splash at '" + boot_logo_path + "'. Loading default splash."); + } } + } else { + // Create a 1×1 transparent image. This will effectively hide the splash image. + boot_logo.instantiate(); + boot_logo->initialize_data(1, 1, false, Image::FORMAT_RGBA8); + boot_logo->set_pixel(0, 0, Color(0, 0, 0, 0)); } - } else { - // Create a 1×1 transparent image. This will effectively hide the splash image. - boot_logo.instantiate(); - boot_logo->initialize_data(1, 1, false, Image::FORMAT_RGBA8); - boot_logo->set_pixel(0, 0, Color(0, 0, 0, 0)); - } - Color boot_bg_color = GLOBAL_GET("application/boot_splash/bg_color"); + Color boot_bg_color = GLOBAL_GET("application/boot_splash/bg_color"); #if defined(TOOLS_ENABLED) && !defined(NO_EDITOR_SPLASH) - boot_bg_color = - GLOBAL_DEF_BASIC("application/boot_splash/bg_color", - (editor || project_manager) ? boot_splash_editor_bg_color : boot_splash_bg_color); + boot_bg_color = + GLOBAL_DEF_BASIC("application/boot_splash/bg_color", + (editor || project_manager) ? boot_splash_editor_bg_color : boot_splash_bg_color); #endif - if (boot_logo.is_valid()) { - RenderingServer::get_singleton()->set_boot_image(boot_logo, boot_bg_color, boot_logo_scale, - boot_logo_filter); + if (boot_logo.is_valid()) { + RenderingServer::get_singleton()->set_boot_image(boot_logo, boot_bg_color, boot_logo_scale, + boot_logo_filter); - } else { + } else { #ifndef NO_DEFAULT_BOOT_LOGO - MAIN_PRINT("Main: Create bootsplash"); + MAIN_PRINT("Main: Create bootsplash"); #if defined(TOOLS_ENABLED) && !defined(NO_EDITOR_SPLASH) - Ref<Image> splash = (editor || project_manager) ? memnew(Image(boot_splash_editor_png)) : memnew(Image(boot_splash_png)); + Ref<Image> splash = (editor || project_manager) ? memnew(Image(boot_splash_editor_png)) : memnew(Image(boot_splash_png)); #else - Ref<Image> splash = memnew(Image(boot_splash_png)); + Ref<Image> splash = memnew(Image(boot_splash_png)); #endif - MAIN_PRINT("Main: ClearColor"); - RenderingServer::get_singleton()->set_default_clear_color(boot_bg_color); - MAIN_PRINT("Main: Image"); - RenderingServer::get_singleton()->set_boot_image(splash, boot_bg_color, false); + MAIN_PRINT("Main: ClearColor"); + RenderingServer::get_singleton()->set_default_clear_color(boot_bg_color); + MAIN_PRINT("Main: Image"); + RenderingServer::get_singleton()->set_boot_image(splash, boot_bg_color, false); #endif - } + } #if defined(TOOLS_ENABLED) && defined(MACOS_ENABLED) - if (OS::get_singleton()->get_bundle_icon_path().is_empty()) { - Ref<Image> icon = memnew(Image(app_icon_png)); - DisplayServer::get_singleton()->set_icon(icon); - } + if (OS::get_singleton()->get_bundle_icon_path().is_empty()) { + Ref<Image> icon = memnew(Image(app_icon_png)); + DisplayServer::get_singleton()->set_icon(icon); + } #endif - } + } + + MAIN_PRINT("Main: Clear Color"); - DisplayServer::set_early_window_clear_color_override(false); + DisplayServer::set_early_window_clear_color_override(false); + RenderingServer::get_singleton()->set_default_clear_color( + GLOBAL_GET("rendering/environment/defaults/default_clear_color")); - MAIN_PRINT("Main: DCC"); - RenderingServer::get_singleton()->set_default_clear_color( - GLOBAL_GET("rendering/environment/defaults/default_clear_color")); + GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "application/config/icon", PROPERTY_HINT_FILE, "*.png,*.webp,*.svg"), String()); + GLOBAL_DEF(PropertyInfo(Variant::STRING, "application/config/macos_native_icon", PROPERTY_HINT_FILE, "*.icns"), String()); + GLOBAL_DEF(PropertyInfo(Variant::STRING, "application/config/windows_native_icon", PROPERTY_HINT_FILE, "*.ico"), String()); - GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "application/config/icon", PROPERTY_HINT_FILE, "*.png,*.webp,*.svg"), String()); - GLOBAL_DEF(PropertyInfo(Variant::STRING, "application/config/macos_native_icon", PROPERTY_HINT_FILE, "*.icns"), String()); - GLOBAL_DEF(PropertyInfo(Variant::STRING, "application/config/windows_native_icon", PROPERTY_HINT_FILE, "*.ico"), String()); + MAIN_PRINT("Main: Touch Input"); - Input *id = Input::get_singleton(); - if (id) { - agile_input_event_flushing = GLOBAL_DEF("input_devices/buffering/agile_event_flushing", false); + Input *id = Input::get_singleton(); + if (id) { + agile_input_event_flushing = GLOBAL_DEF("input_devices/buffering/agile_event_flushing", false); - if (bool(GLOBAL_DEF_BASIC("input_devices/pointing/emulate_touch_from_mouse", false)) && - !(editor || project_manager)) { - if (!DisplayServer::get_singleton()->is_touchscreen_available()) { - //only if no touchscreen ui hint, set emulation - id->set_emulate_touch_from_mouse(true); + if (bool(GLOBAL_DEF_BASIC("input_devices/pointing/emulate_touch_from_mouse", false)) && + !(editor || project_manager)) { + if (!DisplayServer::get_singleton()->is_touchscreen_available()) { + //only if no touchscreen ui hint, set emulation + id->set_emulate_touch_from_mouse(true); + } } + + id->set_emulate_mouse_from_touch(bool(GLOBAL_DEF_BASIC("input_devices/pointing/emulate_mouse_from_touch", true))); } - id->set_emulate_mouse_from_touch(bool(GLOBAL_DEF_BASIC("input_devices/pointing/emulate_mouse_from_touch", true))); - } + GLOBAL_DEF_BASIC("input_devices/pointing/android/enable_long_press_as_right_click", false); + GLOBAL_DEF_BASIC("input_devices/pointing/android/enable_pan_and_scale_gestures", false); - GLOBAL_DEF_BASIC("input_devices/pointing/android/enable_long_press_as_right_click", false); - GLOBAL_DEF_BASIC("input_devices/pointing/android/enable_pan_and_scale_gestures", false); + OS::get_singleton()->benchmark_end_measure("Startup", "Setup Window and Boot"); + } MAIN_PRINT("Main: Load Translations and Remaps"); - translation_server->setup(); //register translations, load them, etc. - if (!locale.is_empty()) { - translation_server->set_locale(locale); - } - translation_server->load_translations(); - ResourceLoader::load_translation_remaps(); //load remaps for resources + /* Setup translations and remaps */ - ResourceLoader::load_path_remaps(); - - MAIN_PRINT("Main: Load TextServer"); + { + OS::get_singleton()->benchmark_begin_measure("Startup", "Translations and Remaps"); - /* Enum text drivers */ - GLOBAL_DEF_RST("internationalization/rendering/text_driver", ""); - String text_driver_options; - for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) { - const String driver_name = TextServerManager::get_singleton()->get_interface(i)->get_name(); - if (driver_name == "Dummy") { - // Dummy text driver cannot draw any text, making the editor unusable if selected. - continue; + translation_server->setup(); //register translations, load them, etc. + if (!locale.is_empty()) { + translation_server->set_locale(locale); } - if (!text_driver_options.is_empty() && text_driver_options.find(",") == -1) { - // Not the first option; add a comma before it as a separator for the property hint. - text_driver_options += ","; - } - text_driver_options += driver_name; - } - ProjectSettings::get_singleton()->set_custom_property_info(PropertyInfo(Variant::STRING, "internationalization/rendering/text_driver", PROPERTY_HINT_ENUM, text_driver_options)); + translation_server->load_translations(); + ResourceLoader::load_translation_remaps(); //load remaps for resources - /* Determine text driver */ - if (text_driver.is_empty()) { - text_driver = GLOBAL_GET("internationalization/rendering/text_driver"); + ResourceLoader::load_path_remaps(); + + OS::get_singleton()->benchmark_end_measure("Startup", "Translations and Remaps"); } - if (!text_driver.is_empty()) { - /* Load user selected text server. */ + MAIN_PRINT("Main: Load TextServer"); + + /* Setup Text Server */ + + { + OS::get_singleton()->benchmark_begin_measure("Startup", "Text Server"); + + /* Enum text drivers */ + GLOBAL_DEF_RST("internationalization/rendering/text_driver", ""); + String text_driver_options; for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) { - if (TextServerManager::get_singleton()->get_interface(i)->get_name() == text_driver) { - text_driver_idx = i; - break; + const String driver_name = TextServerManager::get_singleton()->get_interface(i)->get_name(); + if (driver_name == "Dummy") { + // Dummy text driver cannot draw any text, making the editor unusable if selected. + continue; + } + if (!text_driver_options.is_empty() && text_driver_options.find(",") == -1) { + // Not the first option; add a comma before it as a separator for the property hint. + text_driver_options += ","; } + text_driver_options += driver_name; } - } + ProjectSettings::get_singleton()->set_custom_property_info(PropertyInfo(Variant::STRING, "internationalization/rendering/text_driver", PROPERTY_HINT_ENUM, text_driver_options)); - if (text_driver_idx < 0) { - /* If not selected, use one with the most features available. */ - int max_features = 0; - for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) { - uint32_t features = TextServerManager::get_singleton()->get_interface(i)->get_features(); - int feature_number = 0; - while (features) { - feature_number += features & 1; - features >>= 1; + /* Determine text driver */ + if (text_driver.is_empty()) { + text_driver = GLOBAL_GET("internationalization/rendering/text_driver"); + } + + if (!text_driver.is_empty()) { + /* Load user selected text server. */ + for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) { + if (TextServerManager::get_singleton()->get_interface(i)->get_name() == text_driver) { + text_driver_idx = i; + break; + } } - if (feature_number >= max_features) { - max_features = feature_number; - text_driver_idx = i; + } + + if (text_driver_idx < 0) { + /* If not selected, use one with the most features available. */ + int max_features = 0; + for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) { + uint32_t features = TextServerManager::get_singleton()->get_interface(i)->get_features(); + int feature_number = 0; + while (features) { + feature_number += features & 1; + features >>= 1; + } + if (feature_number >= max_features) { + max_features = feature_number; + text_driver_idx = i; + } } } - } - if (text_driver_idx >= 0) { - Ref<TextServer> ts = TextServerManager::get_singleton()->get_interface(text_driver_idx); - TextServerManager::get_singleton()->set_primary_interface(ts); - if (ts->has_feature(TextServer::FEATURE_USE_SUPPORT_DATA)) { - ts->load_support_data("res://" + ts->get_support_data_filename()); + if (text_driver_idx >= 0) { + Ref<TextServer> ts = TextServerManager::get_singleton()->get_interface(text_driver_idx); + TextServerManager::get_singleton()->set_primary_interface(ts); + if (ts->has_feature(TextServer::FEATURE_USE_SUPPORT_DATA)) { + ts->load_support_data("res://" + ts->get_support_data_filename()); + } + } else { + ERR_FAIL_V_MSG(ERR_CANT_CREATE, "TextServer: Unable to create TextServer interface."); } - } else { - ERR_FAIL_V_MSG(ERR_CANT_CREATE, "TextServer: Unable to create TextServer interface."); - } - OS::get_singleton()->benchmark_end_measure("servers"); + OS::get_singleton()->benchmark_end_measure("Startup", "Text Server"); + } MAIN_PRINT("Main: Load Scene Types"); - OS::get_singleton()->benchmark_begin_measure("scene"); + OS::get_singleton()->benchmark_begin_measure("Startup", "Scene"); // Initialize ThemeDB early so that scene types can register their theme items. // Default theme will be initialized later, after modules and ScriptServer are ready. @@ -2691,23 +2761,42 @@ Error Main::setup2() { register_scene_singletons(); - initialize_modules(MODULE_INITIALIZATION_LEVEL_SCENE); - GDExtensionManager::get_singleton()->initialize_extensions(GDExtension::INITIALIZATION_LEVEL_SCENE); + { + OS::get_singleton()->benchmark_begin_measure("Scene", "Modules and Extensions"); + + initialize_modules(MODULE_INITIALIZATION_LEVEL_SCENE); + GDExtensionManager::get_singleton()->initialize_extensions(GDExtension::INITIALIZATION_LEVEL_SCENE); + + OS::get_singleton()->benchmark_end_measure("Scene", "Modules and Extensions"); + } + + OS::get_singleton()->benchmark_end_measure("Startup", "Scene"); #ifdef TOOLS_ENABLED ClassDB::set_current_api(ClassDB::API_EDITOR); register_editor_types(); - initialize_modules(MODULE_INITIALIZATION_LEVEL_EDITOR); - GDExtensionManager::get_singleton()->initialize_extensions(GDExtension::INITIALIZATION_LEVEL_EDITOR); + + { + OS::get_singleton()->benchmark_begin_measure("Editor", "Modules and Extensions"); + + initialize_modules(MODULE_INITIALIZATION_LEVEL_EDITOR); + GDExtensionManager::get_singleton()->initialize_extensions(GDExtension::INITIALIZATION_LEVEL_EDITOR); + + OS::get_singleton()->benchmark_end_measure("Editor", "Modules and Extensions"); + } ClassDB::set_current_api(ClassDB::API_CORE); #endif - MAIN_PRINT("Main: Load Modules"); + MAIN_PRINT("Main: Load Platforms"); + + OS::get_singleton()->benchmark_begin_measure("Startup", "Platforms"); register_platform_apis(); + OS::get_singleton()->benchmark_end_measure("Startup", "Platforms"); + GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "display/mouse_cursor/custom_image", PROPERTY_HINT_FILE, "*.png,*.webp"), String()); GLOBAL_DEF_BASIC("display/mouse_cursor/custom_image_hotspot", Vector2()); GLOBAL_DEF_BASIC("display/mouse_cursor/tooltip_position_offset", Point2(10, 10)); @@ -2721,12 +2810,18 @@ Error Main::setup2() { } } + OS::get_singleton()->benchmark_begin_measure("Startup", "Finalize Setup"); + camera_server = CameraServer::create(); MAIN_PRINT("Main: Load Physics"); initialize_physics(); + + MAIN_PRINT("Main: Load Navigation"); + initialize_navigation_server(); + register_server_singletons(); // This loads global classes, so it must happen before custom loaders and savers are registered @@ -2758,6 +2853,8 @@ Error Main::setup2() { rendering_server->global_shader_parameters_load_settings(!editor); } + OS::get_singleton()->benchmark_end_measure("Startup", "Finalize Setup"); + _start_success = true; ClassDB::set_current_api(ClassDB::API_NONE); //no more APIs are registered at this point @@ -2766,7 +2863,7 @@ Error Main::setup2() { print_verbose("EDITOR API HASH: " + uitos(ClassDB::get_api_hash(ClassDB::API_EDITOR))); MAIN_PRINT("Main: Done"); - OS::get_singleton()->benchmark_end_measure("scene"); + OS::get_singleton()->benchmark_end_measure("Startup", "Setup"); return OK; } @@ -2794,6 +2891,7 @@ bool Main::start() { String _export_preset; bool export_debug = false; bool export_pack_only = false; + bool install_android_build_template = false; #ifdef MODULE_GDSCRIPT_ENABLED String gdscript_docs_path; #endif @@ -2826,6 +2924,8 @@ bool Main::start() { editor = true; } else if (args[i] == "-p" || args[i] == "--project-manager") { project_manager = true; + } else if (args[i] == "--install-android-build-template") { + install_android_build_template = true; #endif // TOOLS_ENABLED } else if (args[i].length() && args[i][0] != '-' && positional_arg.is_empty()) { positional_arg = args[i]; @@ -2988,24 +3088,28 @@ bool Main::start() { return false; } - if (dump_gdextension_interface) { - GDExtensionInterfaceDump::generate_gdextension_interface_file("gdextension_interface.h"); - } + // GDExtension API and interface. + { + if (dump_gdextension_interface) { + GDExtensionInterfaceDump::generate_gdextension_interface_file("gdextension_interface.h"); + } - if (dump_extension_api) { - Engine::get_singleton()->set_editor_hint(true); // "extension_api.json" should always contains editor singletons. - GDExtensionAPIDump::generate_extension_json_file("extension_api.json", include_docs_in_extension_api_dump); - } + if (dump_extension_api) { + Engine::get_singleton()->set_editor_hint(true); // "extension_api.json" should always contains editor singletons. + GDExtensionAPIDump::generate_extension_json_file("extension_api.json", include_docs_in_extension_api_dump); + } - if (dump_gdextension_interface || dump_extension_api) { - OS::get_singleton()->set_exit_code(EXIT_SUCCESS); - return false; - } + if (dump_gdextension_interface || dump_extension_api) { + OS::get_singleton()->set_exit_code(EXIT_SUCCESS); + return false; + } - if (validate_extension_api) { - bool valid = GDExtensionAPIDump::validate_extension_json_file(validate_extension_api_file) == OK; - OS::get_singleton()->set_exit_code(valid ? EXIT_SUCCESS : EXIT_FAILURE); - return false; + if (validate_extension_api) { + Engine::get_singleton()->set_editor_hint(true); // "extension_api.json" should always contains editor singletons. + bool valid = GDExtensionAPIDump::validate_extension_json_file(validate_extension_api_file) == OK; + OS::get_singleton()->set_exit_code(valid ? EXIT_SUCCESS : EXIT_FAILURE); + return false; + } } #ifndef DISABLE_DEPRECATED @@ -3166,7 +3270,7 @@ bool Main::start() { if (!project_manager && !editor) { // game if (!game_path.is_empty() || !script.is_empty()) { //autoload - OS::get_singleton()->benchmark_begin_measure("load_autoloads"); + OS::get_singleton()->benchmark_begin_measure("Startup", "Load Autoloads"); HashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); //first pass, add the constants so they exist before any script is loaded @@ -3192,31 +3296,30 @@ bool Main::start() { scn.instantiate(); scn->set_path(info.path); scn->reload_from_file(); - ERR_CONTINUE_MSG(!scn.is_valid(), vformat("Can't autoload: %s.", info.path)); + ERR_CONTINUE_MSG(!scn.is_valid(), vformat("Failed to instantiate an autoload, can't load from path: %s.", info.path)); if (scn.is_valid()) { n = scn->instantiate(); } } else { Ref<Resource> res = ResourceLoader::load(info.path); - ERR_CONTINUE_MSG(res.is_null(), vformat("Can't autoload: %s.", info.path)); + ERR_CONTINUE_MSG(res.is_null(), vformat("Failed to instantiate an autoload, can't load from path: %s.", info.path)); Ref<Script> script_res = res; if (script_res.is_valid()) { StringName ibt = script_res->get_instance_base_type(); bool valid_type = ClassDB::is_parent_class(ibt, "Node"); - ERR_CONTINUE_MSG(!valid_type, vformat("Script does not inherit from Node: %s.", info.path)); + ERR_CONTINUE_MSG(!valid_type, vformat("Failed to instantiate an autoload, script '%s' does not inherit from 'Node'.", info.path)); Object *obj = ClassDB::instantiate(ibt); - - ERR_CONTINUE_MSG(!obj, vformat("Cannot instance script for autoload, expected 'Node' inheritance, got: %s.")); + ERR_CONTINUE_MSG(!obj, vformat("Failed to instantiate an autoload, cannot instantiate '%s'.", ibt)); n = Object::cast_to<Node>(obj); n->set_script(script_res); } } - ERR_CONTINUE_MSG(!n, vformat("Path in autoload not a node or script: %s.", info.path)); + ERR_CONTINUE_MSG(!n, vformat("Failed to instantiate an autoload, path is not pointing to a scene or a script: %s.", info.path)); n->set_name(info.name); //defer so references are all valid on _ready() @@ -3232,7 +3335,7 @@ bool Main::start() { for (Node *E : to_add) { sml->get_root()->add_child(E); } - OS::get_singleton()->benchmark_end_measure("load_autoloads"); + OS::get_singleton()->benchmark_end_measure("Startup", "Load Autoloads"); } } @@ -3271,16 +3374,16 @@ bool Main::start() { EditorNode *editor_node = nullptr; if (editor) { - OS::get_singleton()->benchmark_begin_measure("editor"); + OS::get_singleton()->benchmark_begin_measure("Startup", "Editor"); editor_node = memnew(EditorNode); sml->get_root()->add_child(editor_node); if (!_export_preset.is_empty()) { - editor_node->export_preset(_export_preset, positional_arg, export_debug, export_pack_only); + editor_node->export_preset(_export_preset, positional_arg, export_debug, export_pack_only, install_android_build_template); game_path = ""; // Do not load anything. } - OS::get_singleton()->benchmark_end_measure("editor"); + OS::get_singleton()->benchmark_end_measure("Startup", "Editor"); } #endif sml->set_auto_accept_quit(GLOBAL_GET("application/config/auto_accept_quit")); @@ -3418,7 +3521,7 @@ bool Main::start() { if (!project_manager && !editor) { // game - OS::get_singleton()->benchmark_begin_measure("game_load"); + OS::get_singleton()->benchmark_begin_measure("Startup", "Load Game"); // Load SSL Certificates from Project Settings (or builtin). Crypto::load_default_certificates(GLOBAL_GET("network/tls/certificate_bundle_override")); @@ -3460,19 +3563,19 @@ bool Main::start() { } } - OS::get_singleton()->benchmark_end_measure("game_load"); + OS::get_singleton()->benchmark_end_measure("Startup", "Load Game"); } #ifdef TOOLS_ENABLED if (project_manager) { - OS::get_singleton()->benchmark_begin_measure("project_manager"); + OS::get_singleton()->benchmark_begin_measure("Startup", "Project Manager"); Engine::get_singleton()->set_editor_hint(true); ProjectManager *pmanager = memnew(ProjectManager); ProgressDialog *progress_dialog = memnew(ProgressDialog); pmanager->add_child(progress_dialog); sml->get_root()->add_child(pmanager); DisplayServer::get_singleton()->set_context(DisplayServer::CONTEXT_PROJECTMAN); - OS::get_singleton()->benchmark_end_measure("project_manager"); + OS::get_singleton()->benchmark_end_measure("Startup", "Project Manager"); } if (project_manager || editor) { @@ -3500,7 +3603,7 @@ bool Main::start() { } } - OS::get_singleton()->benchmark_end_measure("startup_begin"); + OS::get_singleton()->benchmark_end_measure("Startup", "Total"); OS::get_singleton()->benchmark_dump(); return true; @@ -3749,7 +3852,7 @@ void Main::force_redraw() { * The order matters as some of those steps are linked with each other. */ void Main::cleanup(bool p_force) { - OS::get_singleton()->benchmark_begin_measure("Main::cleanup"); + OS::get_singleton()->benchmark_begin_measure("Shutdown", "Total"); if (!p_force) { ERR_FAIL_COND(!_start_success); } @@ -3890,7 +3993,7 @@ void Main::cleanup(bool p_force) { uninitialize_modules(MODULE_INITIALIZATION_LEVEL_CORE); unregister_core_types(); - OS::get_singleton()->benchmark_end_measure("Main::cleanup"); + OS::get_singleton()->benchmark_end_measure("Shutdown", "Total"); OS::get_singleton()->benchmark_dump(); OS::get_singleton()->finalize_core(); diff --git a/methods.py b/methods.py index 83fb0445f0..7c511af930 100644 --- a/methods.py +++ b/methods.py @@ -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" diff --git a/misc/extension_api_validation/4.1-stable.expected b/misc/extension_api_validation/4.1-stable_4.2-stable.expected index d51523bd38..d51523bd38 100644 --- a/misc/extension_api_validation/4.1-stable.expected +++ b/misc/extension_api_validation/4.1-stable_4.2-stable.expected diff --git a/misc/extension_api_validation/4.2-stable.expected b/misc/extension_api_validation/4.2-stable.expected new file mode 100644 index 0000000000..0538195e9e --- /dev/null +++ b/misc/extension_api_validation/4.2-stable.expected @@ -0,0 +1,9 @@ +This file contains the expected output of --validate-extension-api when run against the extension_api.json of the +4.2-stable tag (the basename of this file). + +Only lines that start with "Validate extension JSON:" matter, everything else is considered a comment and ignored. They +should instead be used to justify these changes and describe how users should work around these changes. + +Add new entries at the end of the file. + +## Changes between 4.2-stable and 4.3-stable diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp index 9c178997c5..6082b468f7 100644 --- a/modules/csg/csg_shape.cpp +++ b/modules/csg/csg_shape.cpp @@ -2226,7 +2226,7 @@ void CSGPolygon3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "path_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Path3D"), "set_path_node", "get_path_node"); ADD_PROPERTY(PropertyInfo(Variant::INT, "path_interval_type", PROPERTY_HINT_ENUM, "Distance,Subdivide"), "set_path_interval_type", "get_path_interval_type"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_interval", PROPERTY_HINT_RANGE, "0.01,1.0,0.01,exp,or_greater"), "set_path_interval", "get_path_interval"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_simplify_angle", PROPERTY_HINT_RANGE, "0.0,180.0,0.1,exp"), "set_path_simplify_angle", "get_path_simplify_angle"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_simplify_angle", PROPERTY_HINT_RANGE, "0.0,180.0,0.1"), "set_path_simplify_angle", "get_path_simplify_angle"); ADD_PROPERTY(PropertyInfo(Variant::INT, "path_rotation", PROPERTY_HINT_ENUM, "Polygon,Path,PathFollow"), "set_path_rotation", "get_path_rotation"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "path_local"), "set_path_local", "is_path_local"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "path_continuous_u"), "set_path_continuous_u", "is_path_continuous_u"); diff --git a/modules/dds/texture_loader_dds.cpp b/modules/dds/texture_loader_dds.cpp index 481bb46c24..7baf8506a6 100644 --- a/modules/dds/texture_loader_dds.cpp +++ b/modules/dds/texture_loader_dds.cpp @@ -44,21 +44,32 @@ enum { DDSD_MIPMAPCOUNT = 0x00020000, DDPF_FOURCC = 0x00000004, DDPF_ALPHAPIXELS = 0x00000001, - DDPF_INDEXED = 0x00000020, - DDPF_RGB = 0x00000040, + DDPF_RGB = 0x00000040 }; +enum DDSFourCC { + DDFCC_DXT1 = PF_FOURCC("DXT1"), + DDFCC_DXT3 = PF_FOURCC("DXT3"), + DDFCC_DXT5 = PF_FOURCC("DXT5"), + DDFCC_ATI1 = PF_FOURCC("ATI1"), + DDFCC_BC4U = PF_FOURCC("BC4U"), + DDFCC_ATI2 = PF_FOURCC("ATI2"), + DDFCC_BC5U = PF_FOURCC("BC5U"), + DDFCC_A2XY = PF_FOURCC("A2XY") +}; + +// The legacy bitmasked format names here represent the actual data layout in the files, +// while their official names are flipped (e.g. RGBA8 layout is officially called ABGR8). enum DDSFormat { DDS_DXT1, DDS_DXT3, DDS_DXT5, DDS_ATI1, DDS_ATI2, - DDS_A2XY, DDS_BGRA8, DDS_BGR8, - DDS_RGBA8, //flipped in dds - DDS_RGB8, //flipped in dds + DDS_RGBA8, + DDS_RGB8, DDS_BGR5A1, DDS_BGR565, DDS_BGR10A2, @@ -70,28 +81,26 @@ enum DDSFormat { struct DDSFormatInfo { const char *name = nullptr; bool compressed = false; - bool palette = false; uint32_t divisor = 0; uint32_t block_size = 0; Image::Format format = Image::Format::FORMAT_BPTC_RGBA; }; static const DDSFormatInfo dds_format_info[DDS_MAX] = { - { "DXT1/BC1", true, false, 4, 8, Image::FORMAT_DXT1 }, - { "DXT3/BC2", true, false, 4, 16, Image::FORMAT_DXT3 }, - { "DXT5/BC3", true, false, 4, 16, Image::FORMAT_DXT5 }, - { "ATI1/BC4", true, false, 4, 8, Image::FORMAT_RGTC_R }, - { "ATI2/3DC/BC5", true, false, 4, 16, Image::FORMAT_RGTC_RG }, - { "A2XY/DXN/BC5", true, false, 4, 16, Image::FORMAT_RGTC_RG }, - { "BGRA8", false, false, 1, 4, Image::FORMAT_RGBA8 }, - { "BGR8", false, false, 1, 3, Image::FORMAT_RGB8 }, - { "RGBA8", false, false, 1, 4, Image::FORMAT_RGBA8 }, - { "RGB8", false, false, 1, 3, Image::FORMAT_RGB8 }, - { "BGR5A1", false, false, 1, 2, Image::FORMAT_RGBA8 }, - { "BGR565", false, false, 1, 2, Image::FORMAT_RGB8 }, - { "BGR10A2", false, false, 1, 4, Image::FORMAT_RGBA8 }, - { "GRAYSCALE", false, false, 1, 1, Image::FORMAT_L8 }, - { "GRAYSCALE_ALPHA", false, false, 1, 2, Image::FORMAT_LA8 } + { "DXT1/BC1", true, 4, 8, Image::FORMAT_DXT1 }, + { "DXT3/BC2", true, 4, 16, Image::FORMAT_DXT3 }, + { "DXT5/BC3", true, 4, 16, Image::FORMAT_DXT5 }, + { "ATI1/BC4", true, 4, 8, Image::FORMAT_RGTC_R }, + { "ATI2/A2XY/BC5", true, 4, 16, Image::FORMAT_RGTC_RG }, + { "BGRA8", false, 1, 4, Image::FORMAT_RGBA8 }, + { "BGR8", false, 1, 3, Image::FORMAT_RGB8 }, + { "RGBA8", false, 1, 4, Image::FORMAT_RGBA8 }, + { "RGB8", false, 1, 3, Image::FORMAT_RGB8 }, + { "BGR5A1", false, 1, 2, Image::FORMAT_RGBA8 }, + { "BGR565", false, 1, 2, Image::FORMAT_RGB8 }, + { "BGR10A2", false, 1, 4, Image::FORMAT_RGBA8 }, + { "GRAYSCALE", false, 1, 1, Image::FORMAT_L8 }, + { "GRAYSCALE_ALPHA", false, 1, 2, Image::FORMAT_LA8 } }; Ref<Resource> ResourceFormatDDS::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) { @@ -121,15 +130,14 @@ Ref<Resource> ResourceFormatDDS::load(const String &p_path, const String &p_orig /* uint32_t depth = */ f->get_32(); uint32_t mipmaps = f->get_32(); - //skip 11 + // Skip reserved. for (int i = 0; i < 11; i++) { f->get_32(); } - //validate - + // Validate. // We don't check DDSD_CAPS or DDSD_PIXELFORMAT, as they're mandatory when writing, - // but non-mandatory when reading (as some writers don't set them)... + // but non-mandatory when reading (as some writers don't set them). if (magic != DDS_MAGIC || hsize != 124) { ERR_FAIL_V_MSG(Ref<Resource>(), "Invalid or unsupported DDS texture file '" + p_path + "'."); } @@ -145,65 +153,81 @@ Ref<Resource> ResourceFormatDDS::load(const String &p_path, const String &p_orig /* uint32_t caps_1 = */ f->get_32(); /* uint32_t caps_2 = */ f->get_32(); - /* uint32_t caps_ddsx = */ f->get_32(); + /* uint32_t caps_3 = */ f->get_32(); + /* uint32_t caps_4 = */ f->get_32(); - //reserved skip - f->get_32(); + // Skip reserved. f->get_32(); - /* - print_line("DDS width: "+itos(width)); - print_line("DDS height: "+itos(height)); - print_line("DDS mipmaps: "+itos(mipmaps)); + if (f->get_position() < 128) { + f->seek(128); + } - printf("fourcc: %x fflags: %x, rgbbits: %x, fsize: %x\n",format_fourcc,format_flags,format_rgb_bits,format_size); - printf("rmask: %x gmask: %x, bmask: %x, amask: %x\n",format_red_mask,format_green_mask,format_blue_mask,format_alpha_mask); - */ + DDSFormat dds_format = DDS_MAX; - //must avoid this later - while (f->get_position() < 128) { - f->get_8(); - } + if (format_flags & DDPF_FOURCC) { + // FourCC formats. + switch (format_fourcc) { + case DDFCC_DXT1: { + dds_format = DDS_DXT1; + } break; + case DDFCC_DXT3: { + dds_format = DDS_DXT3; + } break; + case DDFCC_DXT5: { + dds_format = DDS_DXT5; + } break; + case DDFCC_ATI1: + case DDFCC_BC4U: { + dds_format = DDS_ATI1; + } break; + case DDFCC_ATI2: + case DDFCC_BC5U: + case DDFCC_A2XY: { + dds_format = DDS_ATI2; + } break; + + default: { + ERR_FAIL_V_MSG(Ref<Resource>(), "Unrecognized or unsupported FourCC in DDS '" + p_path + "'."); + } + } + + } else if (format_flags & DDPF_RGB) { + // Channel-bitmasked formats. + if (format_flags & DDPF_ALPHAPIXELS) { + // With alpha. + if (format_rgb_bits == 32 && format_red_mask == 0xff0000 && format_green_mask == 0xff00 && format_blue_mask == 0xff && format_alpha_mask == 0xff000000) { + dds_format = DDS_BGRA8; + } else if (format_rgb_bits == 32 && format_red_mask == 0xff && format_green_mask == 0xff00 && format_blue_mask == 0xff0000 && format_alpha_mask == 0xff000000) { + dds_format = DDS_RGBA8; + } else if (format_rgb_bits == 16 && format_red_mask == 0x00007c00 && format_green_mask == 0x000003e0 && format_blue_mask == 0x0000001f && format_alpha_mask == 0x00008000) { + dds_format = DDS_BGR5A1; + } else if (format_rgb_bits == 32 && format_red_mask == 0x3ff00000 && format_green_mask == 0xffc00 && format_blue_mask == 0x3ff && format_alpha_mask == 0xc0000000) { + dds_format = DDS_BGR10A2; + } + + } else { + // Without alpha. + if (format_rgb_bits == 24 && format_red_mask == 0xff0000 && format_green_mask == 0xff00 && format_blue_mask == 0xff) { + dds_format = DDS_BGR8; + } else if (format_rgb_bits == 24 && format_red_mask == 0xff && format_green_mask == 0xff00 && format_blue_mask == 0xff0000) { + dds_format = DDS_RGB8; + } else if (format_rgb_bits == 16 && format_red_mask == 0x0000f800 && format_green_mask == 0x000007e0 && format_blue_mask == 0x0000001f) { + dds_format = DDS_BGR565; + } + } - DDSFormat dds_format; - - if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("DXT1")) { - dds_format = DDS_DXT1; - } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("DXT3")) { - dds_format = DDS_DXT3; - - } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("DXT5")) { - dds_format = DDS_DXT5; - } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("ATI1")) { - dds_format = DDS_ATI1; - } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("ATI2")) { - dds_format = DDS_ATI2; - } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("A2XY")) { - dds_format = DDS_A2XY; - - } else if (format_flags & DDPF_RGB && format_flags & DDPF_ALPHAPIXELS && format_rgb_bits == 32 && format_red_mask == 0xff0000 && format_green_mask == 0xff00 && format_blue_mask == 0xff && format_alpha_mask == 0xff000000) { - dds_format = DDS_BGRA8; - } else if (format_flags & DDPF_RGB && !(format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 24 && format_red_mask == 0xff0000 && format_green_mask == 0xff00 && format_blue_mask == 0xff) { - dds_format = DDS_BGR8; - } else if (format_flags & DDPF_RGB && format_flags & DDPF_ALPHAPIXELS && format_rgb_bits == 32 && format_red_mask == 0xff && format_green_mask == 0xff00 && format_blue_mask == 0xff0000 && format_alpha_mask == 0xff000000) { - dds_format = DDS_RGBA8; - } else if (format_flags & DDPF_RGB && !(format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 24 && format_red_mask == 0xff && format_green_mask == 0xff00 && format_blue_mask == 0xff0000) { - dds_format = DDS_RGB8; - - } else if (format_flags & DDPF_RGB && format_flags & DDPF_ALPHAPIXELS && format_rgb_bits == 16 && format_red_mask == 0x00007c00 && format_green_mask == 0x000003e0 && format_blue_mask == 0x0000001f && format_alpha_mask == 0x00008000) { - dds_format = DDS_BGR5A1; - } else if (format_flags & DDPF_RGB && format_flags & DDPF_ALPHAPIXELS && format_rgb_bits == 32 && format_red_mask == 0x3ff00000 && format_green_mask == 0xffc00 && format_blue_mask == 0x3ff && format_alpha_mask == 0xc0000000) { - dds_format = DDS_BGR10A2; - } else if (format_flags & DDPF_RGB && !(format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 16 && format_red_mask == 0x0000f800 && format_green_mask == 0x000007e0 && format_blue_mask == 0x0000001f) { - dds_format = DDS_BGR565; - } else if (!(format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 8 && format_red_mask == 0xff) { - dds_format = DDS_LUMINANCE; - } else if ((format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 16 && format_red_mask == 0xff && format_alpha_mask == 0xff00) { - dds_format = DDS_LUMINANCE_ALPHA; - } else if (format_flags & DDPF_INDEXED && format_rgb_bits == 8) { - dds_format = DDS_BGR565; } else { - //printf("unrecognized fourcc %x format_flags: %x - rgbbits %i - red_mask %x green mask %x blue mask %x alpha mask %x\n", format_fourcc, format_flags, format_rgb_bits, format_red_mask, format_green_mask, format_blue_mask, format_alpha_mask); + // Other formats. + if (format_flags & DDPF_ALPHAPIXELS && format_rgb_bits == 16 && format_red_mask == 0xff && format_alpha_mask == 0xff00) { + dds_format = DDS_LUMINANCE_ALPHA; + } else if (!(format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 8 && format_red_mask == 0xff) { + dds_format = DDS_LUMINANCE; + } + } + + // No format detected, error. + if (dds_format == DDS_MAX) { ERR_FAIL_V_MSG(Ref<Resource>(), "Unrecognized or unsupported color layout in DDS '" + p_path + "'."); } @@ -218,17 +242,17 @@ Ref<Resource> ResourceFormatDDS::load(const String &p_path, const String &p_orig uint32_t h = height; if (info.compressed) { - //compressed bc - + // BC compressed. uint32_t size = MAX(info.divisor, w) / info.divisor * MAX(info.divisor, h) / info.divisor * info.block_size; + ERR_FAIL_COND_V(size != pitch, Ref<Resource>()); ERR_FAIL_COND_V(!(flags & DDSD_LINEARSIZE), Ref<Resource>()); for (uint32_t i = 1; i < mipmaps; i++) { w = MAX(1u, w >> 1); h = MAX(1u, h >> 1); + uint32_t bsize = MAX(info.divisor, w) / info.divisor * MAX(info.divisor, h) / info.divisor * info.block_size; - //printf("%i x %i - block: %i\n",w,h,bsize); size += bsize; } @@ -236,50 +260,8 @@ Ref<Resource> ResourceFormatDDS::load(const String &p_path, const String &p_orig uint8_t *wb = src_data.ptrw(); f->get_buffer(wb, size); - } else if (info.palette) { - //indexed - ERR_FAIL_COND_V(!(flags & DDSD_PITCH), Ref<Resource>()); - ERR_FAIL_COND_V(format_rgb_bits != 8, Ref<Resource>()); - - uint32_t size = pitch * height; - ERR_FAIL_COND_V(size != width * height * info.block_size, Ref<Resource>()); - - uint8_t palette[256 * 4]; - f->get_buffer(palette, 256 * 4); - - int colsize = 3; - for (int i = 0; i < 256; i++) { - if (palette[i * 4 + 3] < 255) { - colsize = 4; - } - } - - int w2 = width; - int h2 = height; - - for (uint32_t i = 1; i < mipmaps; i++) { - w2 = (w2 + 1) >> 1; - h2 = (h2 + 1) >> 1; - size += w2 * h2 * info.block_size; - } - - src_data.resize(size + 256 * colsize); - uint8_t *wb = src_data.ptrw(); - f->get_buffer(wb, size); - - for (int i = 0; i < 256; i++) { - int dst_ofs = size + i * colsize; - int src_ofs = i * 4; - wb[dst_ofs + 0] = palette[src_ofs + 2]; - wb[dst_ofs + 1] = palette[src_ofs + 1]; - wb[dst_ofs + 2] = palette[src_ofs + 0]; - if (colsize == 4) { - wb[dst_ofs + 3] = palette[src_ofs + 3]; - } - } } else { - //uncompressed generic... - + // Generic uncompressed. uint32_t size = width * height * info.block_size; for (uint32_t i = 1; i < mipmaps; i++) { @@ -288,6 +270,7 @@ Ref<Resource> ResourceFormatDDS::load(const String &p_path, const String &p_orig size += w * h * info.block_size; } + // Calculate the space these formats will take up after decoding. if (dds_format == DDS_BGR565) { size = size * 3 / 2; } else if (dds_format == DDS_BGR5A1) { @@ -298,9 +281,10 @@ Ref<Resource> ResourceFormatDDS::load(const String &p_path, const String &p_orig uint8_t *wb = src_data.ptrw(); f->get_buffer(wb, size); + // Decode nonstandard formats. switch (dds_format) { case DDS_BGR5A1: { - // TO RGBA + // To RGBA8. int colcount = size / 4; for (int i = colcount - 1; i >= 0; i--) { @@ -311,13 +295,16 @@ Ref<Resource> ResourceFormatDDS::load(const String &p_path, const String &p_orig uint8_t b = wb[src_ofs] & 0x1F; uint8_t g = (wb[src_ofs] >> 5) | ((wb[src_ofs + 1] & 0x3) << 3); uint8_t r = (wb[src_ofs + 1] >> 2) & 0x1F; + wb[dst_ofs + 0] = r << 3; wb[dst_ofs + 1] = g << 3; wb[dst_ofs + 2] = b << 3; wb[dst_ofs + 3] = a ? 255 : 0; } + } break; case DDS_BGR565: { + // To RGB8. int colcount = size / 3; for (int i = colcount - 1; i >= 0; i--) { @@ -327,21 +314,24 @@ Ref<Resource> ResourceFormatDDS::load(const String &p_path, const String &p_orig uint8_t b = wb[src_ofs] & 0x1F; uint8_t g = (wb[src_ofs] >> 5) | ((wb[src_ofs + 1] & 0x7) << 3); uint8_t r = wb[src_ofs + 1] >> 3; + wb[dst_ofs + 0] = r << 3; wb[dst_ofs + 1] = g << 2; - wb[dst_ofs + 2] = b << 3; //b<<3; + wb[dst_ofs + 2] = b << 3; } } break; case DDS_BGR10A2: { - // TO RGBA + // To RGBA8. int colcount = size / 4; - for (int i = colcount - 1; i >= 0; i--) { + for (int i = 0; i < colcount; i++) { int ofs = i * 4; uint32_t w32 = uint32_t(wb[ofs + 0]) | (uint32_t(wb[ofs + 1]) << 8) | (uint32_t(wb[ofs + 2]) << 16) | (uint32_t(wb[ofs + 3]) << 24); + // This method follows the 'standard' way of decoding 10-bit dds files, + // which means the ones created with DirectXTex will be loaded incorrectly. uint8_t a = (w32 & 0xc0000000) >> 24; uint8_t r = (w32 & 0x3ff00000) >> 22; uint8_t g = (w32 & 0xffc00) >> 12; @@ -350,10 +340,12 @@ Ref<Resource> ResourceFormatDDS::load(const String &p_path, const String &p_orig wb[ofs + 0] = r; wb[ofs + 1] = g; wb[ofs + 2] = b; - wb[ofs + 3] = a == 0xc0 ? 255 : a; //0xc0 should be opaque + wb[ofs + 3] = a == 0xc0 ? 255 : a; // 0xc0 should be opaque. } + } break; case DDS_BGRA8: { + // To RGBA8. int colcount = size / 4; for (int i = 0; i < colcount; i++) { @@ -362,44 +354,12 @@ Ref<Resource> ResourceFormatDDS::load(const String &p_path, const String &p_orig } break; case DDS_BGR8: { + // To RGB8. int colcount = size / 3; for (int i = 0; i < colcount; i++) { SWAP(wb[i * 3 + 0], wb[i * 3 + 2]); } - } break; - case DDS_RGBA8: { - /* do nothing either - int colcount = size/4; - - for(int i=0;i<colcount;i++) { - uint8_t r = wb[i*4+1]; - uint8_t g = wb[i*4+2]; - uint8_t b = wb[i*4+3]; - uint8_t a = wb[i*4+0]; - - wb[i*4+0]=r; - wb[i*4+1]=g; - wb[i*4+2]=b; - wb[i*4+3]=a; - } - */ - } break; - case DDS_RGB8: { - // do nothing - /* - int colcount = size/3; - - for(int i=0;i<colcount;i++) { - SWAP( wb[i*3+0],wb[i*3+2] ); - }*/ - } break; - case DDS_LUMINANCE: { - // do nothing i guess? - - } break; - case DDS_LUMINANCE_ALPHA: { - // do nothing i guess? } break; diff --git a/modules/gdscript/editor/gdscript_docgen.cpp b/modules/gdscript/editor/gdscript_docgen.cpp index c3979dd290..00179109a3 100644 --- a/modules/gdscript/editor/gdscript_docgen.cpp +++ b/modules/gdscript/editor/gdscript_docgen.cpp @@ -64,8 +64,8 @@ void GDScriptDocGen::_doctype_from_gdtype(const GDType &p_gdtype, String &r_type r_type = p_is_return ? "void" : "null"; return; } - if (p_gdtype.builtin_type == Variant::ARRAY && p_gdtype.has_container_element_type()) { - _doctype_from_gdtype(p_gdtype.get_container_element_type(), r_type, r_enum); + if (p_gdtype.builtin_type == Variant::ARRAY && p_gdtype.has_container_element_type(0)) { + _doctype_from_gdtype(p_gdtype.get_container_element_type(0), r_type, r_enum); if (!r_enum.is_empty()) { r_type = "int[]"; r_enum += "[]"; diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp index 8dbd262b22..3df07f9794 100644 --- a/modules/gdscript/editor/gdscript_highlighter.cpp +++ b/modules/gdscript/editor/gdscript_highlighter.cpp @@ -494,7 +494,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l in_function_arg_dicts = 0; } - if (expect_type && (prev_is_char || str[j] == '=') && str[j] != '[' && str[j] != '.') { + if (expect_type && (prev_is_char || str[j] == '=') && str[j] != '[' && str[j] != ',' && str[j] != '.') { expect_type = false; } diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 87f21bf568..ec1682d470 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -2329,6 +2329,19 @@ void GDScriptLanguage::reload_all_scripts() { } elem = elem->next(); } + +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + // Reload all pointers to existing singletons so that tool scripts can work with the reloaded extensions. + List<Engine::Singleton> singletons; + Engine::get_singleton()->get_singletons(&singletons); + for (const Engine::Singleton &E : singletons) { + if (globals.has(E.name)) { + _add_global(E.name, E.ptr); + } + } + } +#endif } //as scripts are going to be reloaded, must proceed without locking here diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 983a19470a..55bd0f97d5 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -685,10 +685,10 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type result.builtin_type = GDScriptParser::get_builtin_type(first); if (result.builtin_type == Variant::ARRAY) { - GDScriptParser::DataType container_type = type_from_metatype(resolve_datatype(p_type->container_type)); + GDScriptParser::DataType container_type = type_from_metatype(resolve_datatype(p_type->get_container_type_or_null(0))); if (container_type.kind != GDScriptParser::DataType::VARIANT) { container_type.is_constant = false; - result.set_container_element_type(container_type); + result.set_container_element_type(0, container_type); } } } else if (class_exists(first)) { @@ -829,8 +829,16 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type } } - if (result.builtin_type != Variant::ARRAY && p_type->container_type != nullptr) { - push_error("Only arrays can specify the collection element type.", p_type); + if (!p_type->container_types.is_empty()) { + if (result.builtin_type == Variant::ARRAY) { + if (p_type->container_types.size() != 1) { + push_error("Arrays require exactly one collection element type.", p_type); + return bad_type; + } + } else { + push_error("Only arrays can specify collection element types.", p_type); + return bad_type; + } } p_type->set_datatype(result); @@ -1891,8 +1899,8 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi if (p_assignable->initializer->type == GDScriptParser::Node::ARRAY) { GDScriptParser::ArrayNode *array = static_cast<GDScriptParser::ArrayNode *>(p_assignable->initializer); - if (has_specified_type && specified_type.has_container_element_type()) { - update_array_literal_element_type(array, specified_type.get_container_element_type()); + if (has_specified_type && specified_type.has_container_element_type(0)) { + update_array_literal_element_type(array, specified_type.get_container_element_type(0)); } } @@ -1955,7 +1963,7 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi } else { push_error(vformat(R"(Cannot assign a value of type %s to %s "%s" with specified type %s.)", initializer_type.to_string(), p_kind, p_assignable->identifier->name, specified_type.to_string()), p_assignable->initializer); } - } else if (specified_type.has_container_element_type() && !initializer_type.has_container_element_type()) { + } else if (specified_type.has_container_element_type(0) && !initializer_type.has_container_element_type(0)) { mark_node_unsafe(p_assignable->initializer); #ifdef DEBUG_ENABLED } else if (specified_type.builtin_type == Variant::INT && initializer_type.builtin_type == Variant::FLOAT) { @@ -2127,8 +2135,8 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) { if (list_type.is_variant()) { variable_type.kind = GDScriptParser::DataType::VARIANT; mark_node_unsafe(p_for->list); - } else if (list_type.has_container_element_type()) { - variable_type = list_type.get_container_element_type(); + } else if (list_type.has_container_element_type(0)) { + variable_type = list_type.get_container_element_type(0); variable_type.type_source = list_type.type_source; } else if (list_type.is_typed_container_type()) { variable_type = list_type.get_typed_container_type(); @@ -2377,8 +2385,8 @@ void GDScriptAnalyzer::resolve_return(GDScriptParser::ReturnNode *p_return) { result.builtin_type = Variant::NIL; result.is_constant = true; } else { - if (p_return->return_value->type == GDScriptParser::Node::ARRAY && has_expected_type && expected_type.has_container_element_type()) { - update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_return->return_value), expected_type.get_container_element_type()); + if (p_return->return_value->type == GDScriptParser::Node::ARRAY && has_expected_type && expected_type.has_container_element_type(0)) { + update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_return->return_value), expected_type.get_container_element_type(0)); } if (has_expected_type && expected_type.is_hard_type() && p_return->return_value->is_constant) { update_const_expression_builtin_type(p_return->return_value, expected_type, "return"); @@ -2598,7 +2606,7 @@ void GDScriptAnalyzer::update_const_expression_builtin_type(GDScriptParser::Expr // This function determines which type is that (if any). void GDScriptAnalyzer::update_array_literal_element_type(GDScriptParser::ArrayNode *p_array, const GDScriptParser::DataType &p_element_type) { GDScriptParser::DataType expected_type = p_element_type; - expected_type.unset_container_element_type(); // Nested types (like `Array[Array[int]]`) are not currently supported. + expected_type.container_element_types.clear(); // Nested types (like `Array[Array[int]]`) are not currently supported. for (int i = 0; i < p_array->elements.size(); i++) { GDScriptParser::ExpressionNode *element_node = p_array->elements[i]; @@ -2621,7 +2629,7 @@ void GDScriptAnalyzer::update_array_literal_element_type(GDScriptParser::ArrayNo } GDScriptParser::DataType array_type = p_array->get_datatype(); - array_type.set_container_element_type(expected_type); + array_type.set_container_element_type(0, expected_type); p_array->set_datatype(array_type); } @@ -2668,8 +2676,8 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig } // Check if assigned value is an array literal, so we can make it a typed array too if appropriate. - if (p_assignment->assigned_value->type == GDScriptParser::Node::ARRAY && assignee_type.is_hard_type() && assignee_type.has_container_element_type()) { - update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_assignment->assigned_value), assignee_type.get_container_element_type()); + if (p_assignment->assigned_value->type == GDScriptParser::Node::ARRAY && assignee_type.is_hard_type() && assignee_type.has_container_element_type(0)) { + update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_assignment->assigned_value), assignee_type.get_container_element_type(0)); } if (p_assignment->operation == GDScriptParser::AssignmentNode::OP_NONE && assignee_type.is_hard_type() && p_assignment->assigned_value->is_constant) { @@ -2747,7 +2755,7 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig // weak non-variant assignee and incompatible result downgrades_assignee = true; } - } else if (assignee_type.has_container_element_type() && !op_type.has_container_element_type()) { + } else if (assignee_type.has_container_element_type(0) && !op_type.has_container_element_type(0)) { // typed array assignee and untyped array result mark_node_unsafe(p_assignment); } @@ -3311,8 +3319,8 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a // If the function requires typed arrays we must make literals be typed. for (const KeyValue<int, GDScriptParser::ArrayNode *> &E : arrays) { int index = E.key; - if (index < par_types.size() && par_types[index].is_hard_type() && par_types[index].has_container_element_type()) { - update_array_literal_element_type(E.value, par_types[index].get_container_element_type()); + if (index < par_types.size() && par_types[index].is_hard_type() && par_types[index].has_container_element_type(0)) { + update_array_literal_element_type(E.value, par_types[index].get_container_element_type(0)); } } validate_call_arg(par_types, default_arg_count, method_flags.has_flag(METHOD_FLAG_VARARG), p_call); @@ -3444,8 +3452,8 @@ void GDScriptAnalyzer::reduce_cast(GDScriptParser::CastNode *p_cast) { } } - if (p_cast->operand->type == GDScriptParser::Node::ARRAY && cast_type.has_container_element_type()) { - update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_cast->operand), cast_type.get_container_element_type()); + if (p_cast->operand->type == GDScriptParser::Node::ARRAY && cast_type.has_container_element_type(0)) { + update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_cast->operand), cast_type.get_container_element_type(0)); } if (!cast_type.is_variant()) { @@ -4432,8 +4440,8 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri break; // Can have an element type. case Variant::ARRAY: - if (base_type.has_container_element_type()) { - result_type = base_type.get_container_element_type(); + if (base_type.has_container_element_type(0)) { + result_type = base_type.get_container_element_type(0); result_type.type_source = base_type.type_source; } else { result_type.kind = GDScriptParser::DataType::VARIANT; @@ -4597,7 +4605,7 @@ Variant GDScriptAnalyzer::make_expression_reduced_value(GDScriptParser::Expressi } Variant GDScriptAnalyzer::make_array_reduced_value(GDScriptParser::ArrayNode *p_array, bool &is_reduced) { - Array array = p_array->get_datatype().has_container_element_type() ? make_array_from_element_datatype(p_array->get_datatype().get_container_element_type()) : Array(); + Array array = p_array->get_datatype().has_container_element_type(0) ? make_array_from_element_datatype(p_array->get_datatype().get_container_element_type(0)) : Array(); array.resize(p_array->elements.size()); for (int i = 0; i < p_array->elements.size(); i++) { @@ -4719,8 +4727,8 @@ Variant GDScriptAnalyzer::make_variable_default_value(GDScriptParser::VariableNo GDScriptParser::DataType datatype = p_variable->get_datatype(); if (datatype.is_hard_type()) { if (datatype.kind == GDScriptParser::DataType::BUILTIN && datatype.builtin_type != Variant::OBJECT) { - if (datatype.builtin_type == Variant::ARRAY && datatype.has_container_element_type()) { - result = make_array_from_element_datatype(datatype.get_container_element_type()); + if (datatype.builtin_type == Variant::ARRAY && datatype.has_container_element_type(0)) { + result = make_array_from_element_datatype(datatype.get_container_element_type(0)); } else { VariantInternal::initialize(&result, datatype.builtin_type); } @@ -4747,11 +4755,11 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_va if (p_value.get_type() == Variant::ARRAY) { const Array &array = p_value; if (array.get_typed_script()) { - result.set_container_element_type(type_from_metatype(make_script_meta_type(array.get_typed_script()))); + result.set_container_element_type(0, type_from_metatype(make_script_meta_type(array.get_typed_script()))); } else if (array.get_typed_class_name()) { - result.set_container_element_type(type_from_metatype(make_native_meta_type(array.get_typed_class_name()))); + result.set_container_element_type(0, type_from_metatype(make_native_meta_type(array.get_typed_class_name()))); } else if (array.get_typed_builtin() != Variant::NIL) { - result.set_container_element_type(type_from_metatype(make_builtin_meta_type((Variant::Type)array.get_typed_builtin()))); + result.set_container_element_type(0, type_from_metatype(make_builtin_meta_type((Variant::Type)array.get_typed_builtin()))); } } else if (p_value.get_type() == Variant::OBJECT) { // Object is treated as a native type, not a builtin type. @@ -4873,7 +4881,7 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_property(const PropertyInfo ERR_FAIL_V_MSG(result, "Could not find element type from property hint of a typed array."); } elem_type.is_constant = false; - result.set_container_element_type(elem_type); + result.set_container_element_type(0, elem_type); } else if (p_property.type == Variant::INT) { // Check if it's enum. if ((p_property.usage & PROPERTY_USAGE_CLASS_IS_ENUM) && p_property.class_name != StringName()) { @@ -5225,7 +5233,7 @@ GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator bool hard_operation = p_a.is_hard_type() && p_b.is_hard_type(); if (p_operation == Variant::OP_ADD && a_type == Variant::ARRAY && b_type == Variant::ARRAY) { - if (p_a.has_container_element_type() && p_b.has_container_element_type() && p_a.get_container_element_type() == p_b.get_container_element_type()) { + if (p_a.has_container_element_type(0) && p_b.has_container_element_type(0) && p_a.get_container_element_type(0) == p_b.get_container_element_type(0)) { r_valid = true; result = p_a; result.type_source = hard_operation ? GDScriptParser::DataType::ANNOTATED_INFERRED : GDScriptParser::DataType::INFERRED; @@ -5276,8 +5284,8 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ } if (valid && p_target.builtin_type == Variant::ARRAY && p_source.builtin_type == Variant::ARRAY) { // Check the element type. - if (p_target.has_container_element_type() && p_source.has_container_element_type()) { - valid = p_target.get_container_element_type() == p_source.get_container_element_type(); + if (p_target.has_container_element_type(0) && p_source.has_container_element_type(0)) { + valid = p_target.get_container_element_type(0) == p_source.get_container_element_type(0); } } return valid; diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp index 25e20c0e76..27766115d5 100644 --- a/modules/gdscript/gdscript_byte_codegen.cpp +++ b/modules/gdscript/gdscript_byte_codegen.cpp @@ -623,8 +623,8 @@ void GDScriptByteCodeGenerator::write_binary_operator(const Address &p_target, V void GDScriptByteCodeGenerator::write_type_test(const Address &p_target, const Address &p_source, const GDScriptDataType &p_type) { switch (p_type.kind) { case GDScriptDataType::BUILTIN: { - if (p_type.builtin_type == Variant::ARRAY && p_type.has_container_element_type()) { - const GDScriptDataType &element_type = p_type.get_container_element_type(); + if (p_type.builtin_type == Variant::ARRAY && p_type.has_container_element_type(0)) { + const GDScriptDataType &element_type = p_type.get_container_element_type(0); append_opcode(GDScriptFunction::OPCODE_TYPE_TEST_ARRAY); append(p_target); append(p_source); @@ -878,8 +878,8 @@ void GDScriptByteCodeGenerator::write_get_static_variable(const Address &p_targe void GDScriptByteCodeGenerator::write_assign_with_conversion(const Address &p_target, const Address &p_source) { switch (p_target.type.kind) { case GDScriptDataType::BUILTIN: { - if (p_target.type.builtin_type == Variant::ARRAY && p_target.type.has_container_element_type()) { - const GDScriptDataType &element_type = p_target.type.get_container_element_type(); + if (p_target.type.builtin_type == Variant::ARRAY && p_target.type.has_container_element_type(0)) { + const GDScriptDataType &element_type = p_target.type.get_container_element_type(0); append_opcode(GDScriptFunction::OPCODE_ASSIGN_TYPED_ARRAY); append(p_target); append(p_source); @@ -924,8 +924,8 @@ void GDScriptByteCodeGenerator::write_assign_with_conversion(const Address &p_ta } void GDScriptByteCodeGenerator::write_assign(const Address &p_target, const Address &p_source) { - if (p_target.type.kind == GDScriptDataType::BUILTIN && p_target.type.builtin_type == Variant::ARRAY && p_target.type.has_container_element_type()) { - const GDScriptDataType &element_type = p_target.type.get_container_element_type(); + if (p_target.type.kind == GDScriptDataType::BUILTIN && p_target.type.builtin_type == Variant::ARRAY && p_target.type.has_container_element_type(0)) { + const GDScriptDataType &element_type = p_target.type.get_container_element_type(0); append_opcode(GDScriptFunction::OPCODE_ASSIGN_TYPED_ARRAY); append(p_target); append(p_source); @@ -1666,9 +1666,9 @@ void GDScriptByteCodeGenerator::write_return(const Address &p_return_value) { // If this is a typed function, then we need to check for potential conversions. if (function->return_type.has_type) { - if (function->return_type.kind == GDScriptDataType::BUILTIN && function->return_type.builtin_type == Variant::ARRAY && function->return_type.has_container_element_type()) { + if (function->return_type.kind == GDScriptDataType::BUILTIN && function->return_type.builtin_type == Variant::ARRAY && function->return_type.has_container_element_type(0)) { // Typed array. - const GDScriptDataType &element_type = function->return_type.get_container_element_type(); + const GDScriptDataType &element_type = function->return_type.get_container_element_type(0); append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_ARRAY); append(p_return_value); append(get_constant_pos(element_type.script_type) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS)); @@ -1691,8 +1691,8 @@ void GDScriptByteCodeGenerator::write_return(const Address &p_return_value) { } else { switch (function->return_type.kind) { case GDScriptDataType::BUILTIN: { - if (function->return_type.builtin_type == Variant::ARRAY && function->return_type.has_container_element_type()) { - const GDScriptDataType &element_type = function->return_type.get_container_element_type(); + if (function->return_type.builtin_type == Variant::ARRAY && function->return_type.has_container_element_type(0)) { + const GDScriptDataType &element_type = function->return_type.get_container_element_type(0); append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_ARRAY); append(p_return_value); append(get_constant_pos(element_type.script_type) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS)); diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 7980f020b8..9560f670e6 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -196,8 +196,8 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D } } - if (p_datatype.has_container_element_type()) { - result.set_container_element_type(_gdtype_from_datatype(p_datatype.get_container_element_type(), p_owner, false)); + for (int i = 0; i < p_datatype.container_element_types.size(); i++) { + result.set_container_element_type(i, _gdtype_from_datatype(p_datatype.get_container_element_type_or_variant(i), p_owner, false)); } return result; @@ -507,8 +507,8 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code values.push_back(val); } - if (array_type.has_container_element_type()) { - gen->write_construct_typed_array(result, array_type.get_container_element_type(), values); + if (array_type.has_container_element_type(0)) { + gen->write_construct_typed_array(result, array_type.get_container_element_type(0), values); } else { gen->write_construct_array(result, values); } @@ -2133,8 +2133,8 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui initialized = true; } else if (local_type.has_type) { // Initialize with default for type. - if (local_type.has_container_element_type()) { - codegen.generator->write_construct_typed_array(local, local_type.get_container_element_type(), Vector<GDScriptCodeGenerator::Address>()); + if (local_type.has_container_element_type(0)) { + codegen.generator->write_construct_typed_array(local, local_type.get_container_element_type(0), Vector<GDScriptCodeGenerator::Address>()); initialized = true; } else if (local_type.kind == GDScriptDataType::BUILTIN) { codegen.generator->write_construct(local, local_type.builtin_type, Vector<GDScriptCodeGenerator::Address>()); @@ -2276,8 +2276,8 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_ GDScriptCodeGenerator::Address dst_address(GDScriptCodeGenerator::Address::MEMBER, codegen.script->member_indices[field->identifier->name].index, field_type); - if (field_type.has_container_element_type()) { - codegen.generator->write_construct_typed_array(dst_address, field_type.get_container_element_type(), Vector<GDScriptCodeGenerator::Address>()); + if (field_type.has_container_element_type(0)) { + codegen.generator->write_construct_typed_array(dst_address, field_type.get_container_element_type(0), Vector<GDScriptCodeGenerator::Address>()); } else if (field_type.kind == GDScriptDataType::BUILTIN) { codegen.generator->write_construct(dst_address, field_type.builtin_type, Vector<GDScriptCodeGenerator::Address>()); } @@ -2466,9 +2466,9 @@ GDScriptFunction *GDScriptCompiler::_make_static_initializer(Error &r_error, GDS if (field_type.has_type) { codegen.generator->write_newline(field->start_line); - if (field_type.has_container_element_type()) { + if (field_type.has_container_element_type(0)) { GDScriptCodeGenerator::Address temp = codegen.add_temporary(field_type); - codegen.generator->write_construct_typed_array(temp, field_type.get_container_element_type(), Vector<GDScriptCodeGenerator::Address>()); + codegen.generator->write_construct_typed_array(temp, field_type.get_container_element_type(0), Vector<GDScriptCodeGenerator::Address>()); codegen.generator->write_set_static_variable(temp, class_addr, p_script->static_variables_indices[field->identifier->name].index); codegen.generator->pop_temporary(); } else if (field_type.kind == GDScriptDataType::BUILTIN) { diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 7c342cc41a..faaff53344 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -1083,6 +1083,12 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base List<PropertyInfo> members; scr->get_script_property_list(&members); for (const PropertyInfo &E : members) { + if (E.usage & (PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SUBGROUP)) { + continue; + } + if (E.name.contains("/")) { + continue; + } int location = p_recursion_depth + _get_property_location(scr->get_class_name(), E.name); ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, location); r_result.insert(option.display, option); @@ -1152,7 +1158,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base List<PropertyInfo> pinfo; ClassDB::get_property_list(type, &pinfo); for (const PropertyInfo &E : pinfo) { - if (E.usage & (PROPERTY_USAGE_GROUP | PROPERTY_USAGE_CATEGORY)) { + if (E.usage & (PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SUBGROUP)) { continue; } if (E.name.contains("/")) { @@ -1213,6 +1219,9 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base } for (const PropertyInfo &E : members) { + if (E.usage & (PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SUBGROUP)) { + continue; + } if (!String(E.name).contains("/")) { ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER); if (GDScriptParser::theme_color_names.has(E.name)) { @@ -2667,6 +2676,11 @@ static bool _get_subscript_type(GDScriptParser::CompletionContext &p_context, co if (p_context.base == nullptr) { return false; } + if (p_subscript->base->datatype.type_source == GDScriptParser::DataType::ANNOTATED_EXPLICIT) { + // Annotated type takes precedence. + return false; + } + const GDScriptParser::GetNodeNode *get_node = nullptr; switch (p_subscript->base->type) { @@ -2715,10 +2729,19 @@ static bool _get_subscript_type(GDScriptParser::CompletionContext &p_context, co if (r_base != nullptr) { *r_base = node; } - r_base_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; - r_base_type.kind = GDScriptParser::DataType::NATIVE; + + r_base_type.type_source = GDScriptParser::DataType::INFERRED; r_base_type.builtin_type = Variant::OBJECT; r_base_type.native_type = node->get_class_name(); + + Ref<Script> scr = node->get_script(); + if (scr.is_null()) { + r_base_type.kind = GDScriptParser::DataType::NATIVE; + } else { + r_base_type.kind = GDScriptParser::DataType::SCRIPT; + r_base_type.script_type = scr; + } + return true; } } diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h index c9b543fbb9..e63fa17cfe 100644 --- a/modules/gdscript/gdscript_function.h +++ b/modules/gdscript/gdscript_function.h @@ -45,10 +45,9 @@ class GDScriptInstance; class GDScript; class GDScriptDataType { -private: - GDScriptDataType *container_element_type = nullptr; - public: + Vector<GDScriptDataType> container_element_types; + enum Kind { UNINITIALIZED, BUILTIN, @@ -76,19 +75,20 @@ public: case BUILTIN: { Variant::Type var_type = p_variant.get_type(); bool valid = builtin_type == var_type; - if (valid && builtin_type == Variant::ARRAY && has_container_element_type()) { + if (valid && builtin_type == Variant::ARRAY && has_container_element_type(0)) { Array array = p_variant; if (array.is_typed()) { + GDScriptDataType array_container_type = get_container_element_type(0); Variant::Type array_builtin_type = (Variant::Type)array.get_typed_builtin(); StringName array_native_type = array.get_typed_class_name(); Ref<Script> array_script_type_ref = array.get_typed_script(); if (array_script_type_ref.is_valid()) { - valid = (container_element_type->kind == SCRIPT || container_element_type->kind == GDSCRIPT) && container_element_type->script_type == array_script_type_ref.ptr(); + valid = (array_container_type.kind == SCRIPT || array_container_type.kind == GDSCRIPT) && array_container_type.script_type == array_script_type_ref.ptr(); } else if (array_native_type != StringName()) { - valid = container_element_type->kind == NATIVE && container_element_type->native_type == array_native_type; + valid = array_container_type.kind == NATIVE && array_container_type.native_type == array_native_type; } else { - valid = container_element_type->kind == BUILTIN && container_element_type->builtin_type == array_builtin_type; + valid = array_container_type.kind == BUILTIN && array_container_type.builtin_type == array_builtin_type; } } else { valid = false; @@ -147,24 +147,32 @@ public: return false; } - void set_container_element_type(const GDScriptDataType &p_element_type) { - container_element_type = memnew(GDScriptDataType(p_element_type)); + void set_container_element_type(int p_index, const GDScriptDataType &p_element_type) { + ERR_FAIL_COND(p_index < 0); + while (p_index >= container_element_types.size()) { + container_element_types.push_back(GDScriptDataType()); + } + container_element_types.write[p_index] = GDScriptDataType(p_element_type); + } + + GDScriptDataType get_container_element_type(int p_index) const { + ERR_FAIL_INDEX_V(p_index, container_element_types.size(), GDScriptDataType()); + return container_element_types[p_index]; } - GDScriptDataType get_container_element_type() const { - ERR_FAIL_NULL_V(container_element_type, GDScriptDataType()); - return *container_element_type; + GDScriptDataType get_container_element_type_or_variant(int p_index) const { + if (p_index < 0 || p_index >= container_element_types.size()) { + return GDScriptDataType(); + } + return container_element_types[p_index]; } - bool has_container_element_type() const { - return container_element_type != nullptr; + bool has_container_element_type(int p_index) const { + return p_index >= 0 && p_index < container_element_types.size(); } - void unset_container_element_type() { - if (container_element_type) { - memdelete(container_element_type); - } - container_element_type = nullptr; + bool has_container_element_types() const { + return !container_element_types.is_empty(); } GDScriptDataType() = default; @@ -176,19 +184,14 @@ public: native_type = p_other.native_type; script_type = p_other.script_type; script_type_ref = p_other.script_type_ref; - unset_container_element_type(); - if (p_other.has_container_element_type()) { - set_container_element_type(p_other.get_container_element_type()); - } + container_element_types = p_other.container_element_types; } GDScriptDataType(const GDScriptDataType &p_other) { *this = p_other; } - ~GDScriptDataType() { - unset_container_element_type(); - } + ~GDScriptDataType() {} }; class GDScriptFunction { diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index db7b3e7ace..ea7abc9ded 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -3337,14 +3337,21 @@ GDScriptParser::TypeNode *GDScriptParser::parse_type(bool p_allow_void) { if (match(GDScriptTokenizer::Token::BRACKET_OPEN)) { // Typed collection (like Array[int]). - type->container_type = parse_type(false); // Don't allow void for array element type. - if (type->container_type == nullptr) { - push_error(R"(Expected type for collection after "[".)"); - complete_extents(type); - type = nullptr; - } else if (type->container_type->container_type != nullptr) { - push_error("Nested typed collections are not supported."); - } + bool first_pass = true; + do { + TypeNode *container_type = parse_type(false); // Don't allow void for element type. + if (container_type == nullptr) { + push_error(vformat(R"(Expected type for collection after "%s".)", first_pass ? "[" : ",")); + complete_extents(type); + type = nullptr; + break; + } else if (container_type->container_types.size() > 0) { + push_error("Nested typed collections are not supported."); + } else { + type->container_types.append(container_type); + } + first_pass = false; + } while (match(GDScriptTokenizer::Token::COMMA)); consume(GDScriptTokenizer::Token::BRACKET_CLOSE, R"(Expected closing "]" after collection type.)"); if (type != nullptr) { complete_extents(type); @@ -3996,8 +4003,8 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node variable->export_info.type = Variant::INT; } } else if (p_annotation->name == SNAME("@export_multiline")) { - if (export_type.builtin_type == Variant::ARRAY && export_type.has_container_element_type()) { - DataType inner_type = export_type.get_container_element_type(); + if (export_type.builtin_type == Variant::ARRAY && export_type.has_container_element_type(0)) { + DataType inner_type = export_type.get_container_element_type(0); if (inner_type.builtin_type != Variant::STRING) { push_error(vformat(R"("%s" annotation on arrays requires a string type but type "%s" was given instead.)", p_annotation->name.operator String(), inner_type.to_string()), variable); return false; @@ -4033,8 +4040,8 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node bool is_array = false; - if (export_type.builtin_type == Variant::ARRAY && export_type.has_container_element_type()) { - export_type = export_type.get_container_element_type(); // Use inner type for. + if (export_type.builtin_type == Variant::ARRAY && export_type.has_container_element_type(0)) { + export_type = export_type.get_container_element_type(0); // Use inner type for. is_array = true; } @@ -4344,8 +4351,8 @@ String GDScriptParser::DataType::to_string() const { if (builtin_type == Variant::NIL) { return "null"; } - if (builtin_type == Variant::ARRAY && has_container_element_type()) { - return vformat("Array[%s]", container_element_type->to_string()); + if (builtin_type == Variant::ARRAY && has_container_element_type(0)) { + return vformat("Array[%s]", container_element_types[0].to_string()); } return Variant::get_type_name(builtin_type); case NATIVE: @@ -4398,36 +4405,36 @@ PropertyInfo GDScriptParser::DataType::to_property_info(const String &p_name) co switch (kind) { case BUILTIN: result.type = builtin_type; - if (builtin_type == Variant::ARRAY && has_container_element_type()) { - const DataType *elem_type = container_element_type; - switch (elem_type->kind) { + if (builtin_type == Variant::ARRAY && has_container_element_type(0)) { + const DataType elem_type = get_container_element_type(0); + switch (elem_type.kind) { case BUILTIN: result.hint = PROPERTY_HINT_ARRAY_TYPE; - result.hint_string = Variant::get_type_name(elem_type->builtin_type); + result.hint_string = Variant::get_type_name(elem_type.builtin_type); break; case NATIVE: result.hint = PROPERTY_HINT_ARRAY_TYPE; - result.hint_string = elem_type->native_type; + result.hint_string = elem_type.native_type; break; case SCRIPT: result.hint = PROPERTY_HINT_ARRAY_TYPE; - if (elem_type->script_type.is_valid() && elem_type->script_type->get_global_name() != StringName()) { - result.hint_string = elem_type->script_type->get_global_name(); + if (elem_type.script_type.is_valid() && elem_type.script_type->get_global_name() != StringName()) { + result.hint_string = elem_type.script_type->get_global_name(); } else { - result.hint_string = elem_type->native_type; + result.hint_string = elem_type.native_type; } break; case CLASS: result.hint = PROPERTY_HINT_ARRAY_TYPE; - if (elem_type->class_type != nullptr && elem_type->class_type->get_global_name() != StringName()) { - result.hint_string = elem_type->class_type->get_global_name(); + if (elem_type.class_type != nullptr && elem_type.class_type->get_global_name() != StringName()) { + result.hint_string = elem_type.class_type->get_global_name(); } else { - result.hint_string = elem_type->native_type; + result.hint_string = elem_type.native_type; } break; case ENUM: result.hint = PROPERTY_HINT_ARRAY_TYPE; - result.hint_string = String(elem_type->native_type).replace("::", "."); + result.hint_string = String(elem_type.native_type).replace("::", "."); break; case VARIANT: case RESOLVING: diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 4b46b98baa..f48ad48de0 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -101,11 +101,9 @@ public: struct WhileNode; class DataType { - private: - // Private access so we can control memory management. - DataType *container_element_type = nullptr; - public: + Vector<DataType> container_element_types; + enum Kind { BUILTIN, NATIVE, @@ -152,24 +150,39 @@ public: _FORCE_INLINE_ String to_string_strict() const { return is_hard_type() ? to_string() : "Variant"; } PropertyInfo to_property_info(const String &p_name) const; - _FORCE_INLINE_ void set_container_element_type(const DataType &p_type) { - container_element_type = memnew(DataType(p_type)); + _FORCE_INLINE_ static DataType get_variant_type() { // Default DataType for container elements. + DataType datatype; + datatype.kind = VARIANT; + datatype.type_source = INFERRED; + return datatype; } - _FORCE_INLINE_ DataType get_container_element_type() const { - ERR_FAIL_NULL_V(container_element_type, DataType()); - return *container_element_type; + _FORCE_INLINE_ void set_container_element_type(int p_index, const DataType &p_type) { + ERR_FAIL_COND(p_index < 0); + while (p_index >= container_element_types.size()) { + container_element_types.push_back(get_variant_type()); + } + container_element_types.write[p_index] = DataType(p_type); } - _FORCE_INLINE_ bool has_container_element_type() const { - return container_element_type != nullptr; + _FORCE_INLINE_ DataType get_container_element_type(int p_index) const { + ERR_FAIL_INDEX_V(p_index, container_element_types.size(), get_variant_type()); + return container_element_types[p_index]; } - _FORCE_INLINE_ void unset_container_element_type() { - if (container_element_type) { - memdelete(container_element_type); - }; - container_element_type = nullptr; + _FORCE_INLINE_ DataType get_container_element_type_or_variant(int p_index) const { + if (p_index < 0 || p_index >= container_element_types.size()) { + return get_variant_type(); + } + return container_element_types[p_index]; + } + + _FORCE_INLINE_ bool has_container_element_type(int p_index) const { + return p_index >= 0 && p_index < container_element_types.size(); + } + + _FORCE_INLINE_ bool has_container_element_types() const { + return !container_element_types.is_empty(); } bool is_typed_container_type() const; @@ -229,10 +242,7 @@ public: class_type = p_other.class_type; method_info = p_other.method_info; enum_values = p_other.enum_values; - unset_container_element_type(); - if (p_other.has_container_element_type()) { - set_container_element_type(p_other.get_container_element_type()); - } + container_element_types = p_other.container_element_types; } DataType() = default; @@ -241,9 +251,7 @@ public: *this = p_other; } - ~DataType() { - unset_container_element_type(); - } + ~DataType() {} }; struct ParserError { @@ -1183,7 +1191,11 @@ public: struct TypeNode : public Node { Vector<IdentifierNode *> type_chain; - TypeNode *container_type = nullptr; + Vector<TypeNode *> container_types; + + TypeNode *get_container_type_or_null(int p_index) const { + return p_index >= 0 && p_index < container_types.size() ? container_types[p_index] : nullptr; + } TypeNode() { type = TYPE; diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp index d31411b26b..2c0b8df9ac 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/modules/gdscript/gdscript_vm.cpp @@ -91,8 +91,8 @@ Variant GDScriptFunction::_get_default_variant_for_data_type(const GDScriptDataT if (p_data_type.builtin_type == Variant::ARRAY) { Array array; // Typed array. - if (p_data_type.has_container_element_type()) { - const GDScriptDataType &element_type = p_data_type.get_container_element_type(); + if (p_data_type.has_container_element_type(0)) { + const GDScriptDataType &element_type = p_data_type.get_container_element_type(0); array.set_typed(element_type.builtin_type, element_type.native_type, element_type.script_type); } diff --git a/modules/gdscript/tests/gdscript_test_runner.cpp b/modules/gdscript/tests/gdscript_test_runner.cpp index f91dc83f2c..361ca276bb 100644 --- a/modules/gdscript/tests/gdscript_test_runner.cpp +++ b/modules/gdscript/tests/gdscript_test_runner.cpp @@ -78,31 +78,30 @@ void init_autoloads() { scn.instantiate(); scn->set_path(info.path); scn->reload_from_file(); - ERR_CONTINUE_MSG(!scn.is_valid(), vformat("Can't autoload: %s.", info.path)); + ERR_CONTINUE_MSG(!scn.is_valid(), vformat("Failed to instantiate an autoload, can't load from path: %s.", info.path)); if (scn.is_valid()) { n = scn->instantiate(); } } else { Ref<Resource> res = ResourceLoader::load(info.path); - ERR_CONTINUE_MSG(res.is_null(), vformat("Can't autoload: %s.", info.path)); + ERR_CONTINUE_MSG(res.is_null(), vformat("Failed to instantiate an autoload, can't load from path: %s.", info.path)); Ref<Script> scr = res; if (scr.is_valid()) { StringName ibt = scr->get_instance_base_type(); bool valid_type = ClassDB::is_parent_class(ibt, "Node"); - ERR_CONTINUE_MSG(!valid_type, vformat("Script does not inherit from Node: %s.", info.path)); + ERR_CONTINUE_MSG(!valid_type, vformat("Failed to instantiate an autoload, script '%s' does not inherit from 'Node'.", info.path)); Object *obj = ClassDB::instantiate(ibt); - - ERR_CONTINUE_MSG(!obj, vformat("Cannot instance script for Autoload, expected 'Node' inheritance, got: %s.", ibt)); + ERR_CONTINUE_MSG(!obj, vformat("Failed to instantiate an autoload, cannot instantiate '%s'.", ibt)); n = Object::cast_to<Node>(obj); n->set_script(scr); } } - ERR_CONTINUE_MSG(!n, vformat("Path in autoload not a node or script: %s.", info.path)); + ERR_CONTINUE_MSG(!n, vformat("Failed to instantiate an autoload, path is not pointing to a scene or a script: %s.", info.path)); n->set_name(info.name); for (int i = 0; i < ScriptServer::get_language_count(); i++) { diff --git a/modules/navigation/godot_navigation_server.cpp b/modules/navigation/godot_navigation_server.cpp index 6a3bf6793e..5a28f8b8ef 100644 --- a/modules/navigation/godot_navigation_server.cpp +++ b/modules/navigation/godot_navigation_server.cpp @@ -331,6 +331,13 @@ RID GodotNavigationServer::agent_get_map(RID p_agent) const { return RID(); } +Vector3 GodotNavigationServer::map_get_random_point(RID p_map, uint32_t p_navigation_layers, bool p_uniformly) const { + const NavMap *map = map_owner.get_or_null(p_map); + ERR_FAIL_NULL_V(map, Vector3()); + + return map->get_random_point(p_navigation_layers, p_uniformly); +} + RID GodotNavigationServer::region_create() { MutexLock lock(operations_mutex); @@ -498,6 +505,13 @@ Vector3 GodotNavigationServer::region_get_connection_pathway_end(RID p_region, i return region->get_connection_pathway_end(p_connection_id); } +Vector3 GodotNavigationServer::region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const { + const NavRegion *region = region_owner.get_or_null(p_region); + ERR_FAIL_NULL_V(region, Vector3()); + + return region->get_random_point(p_navigation_layers, p_uniformly); +} + RID GodotNavigationServer::link_create() { MutexLock lock(operations_mutex); diff --git a/modules/navigation/godot_navigation_server.h b/modules/navigation/godot_navigation_server.h index 4ead4fc398..3a76f83b09 100644 --- a/modules/navigation/godot_navigation_server.h +++ b/modules/navigation/godot_navigation_server.h @@ -140,6 +140,8 @@ public: virtual void map_force_update(RID p_map) override; + virtual Vector3 map_get_random_point(RID p_map, uint32_t p_navigation_layers, bool p_uniformly) const override; + virtual RID region_create() override; COMMAND_2(region_set_enabled, RID, p_region, bool, p_enabled); @@ -170,6 +172,7 @@ public: virtual int region_get_connections_count(RID p_region) const override; virtual Vector3 region_get_connection_pathway_start(RID p_region, int p_connection_id) const override; virtual Vector3 region_get_connection_pathway_end(RID p_region, int p_connection_id) const override; + virtual Vector3 region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const override; virtual RID link_create() override; COMMAND_2(link_set_map, RID, p_link, RID, p_map); diff --git a/modules/navigation/godot_navigation_server_2d.cpp b/modules/navigation/godot_navigation_server_2d.cpp index b54729e06f..142d6181a1 100644 --- a/modules/navigation/godot_navigation_server_2d.cpp +++ b/modules/navigation/godot_navigation_server_2d.cpp @@ -259,8 +259,12 @@ Vector<Vector2> FORWARD_5_R_C(vector_v3_to_v2, map_get_path, RID, p_map, Vector2 Vector2 FORWARD_2_R_C(v3_to_v2, map_get_closest_point, RID, p_map, const Vector2 &, p_point, rid_to_rid, v2_to_v3); RID FORWARD_2_C(map_get_closest_point_owner, RID, p_map, const Vector2 &, p_point, rid_to_rid, v2_to_v3); -RID FORWARD_0(region_create); +Vector2 GodotNavigationServer2D::map_get_random_point(RID p_map, uint32_t p_naviation_layers, bool p_uniformly) const { + Vector3 result = NavigationServer3D::get_singleton()->map_get_random_point(p_map, p_naviation_layers, p_uniformly); + return v3_to_v2(result); +} +RID FORWARD_0(region_create); void FORWARD_2(region_set_enabled, RID, p_region, bool, p_enabled, rid_to_rid, bool_to_bool); bool FORWARD_1_C(region_get_enabled, RID, p_region, rid_to_rid); void FORWARD_2(region_set_use_edge_connections, RID, p_region, bool, p_enabled, rid_to_rid, bool_to_bool); @@ -287,6 +291,11 @@ int FORWARD_1_C(region_get_connections_count, RID, p_region, rid_to_rid); Vector2 FORWARD_2_R_C(v3_to_v2, region_get_connection_pathway_start, RID, p_region, int, p_connection_id, rid_to_rid, int_to_int); Vector2 FORWARD_2_R_C(v3_to_v2, region_get_connection_pathway_end, RID, p_region, int, p_connection_id, rid_to_rid, int_to_int); +Vector2 GodotNavigationServer2D::region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const { + Vector3 result = NavigationServer3D::get_singleton()->region_get_random_point(p_region, p_navigation_layers, p_uniformly); + return v3_to_v2(result); +} + RID FORWARD_0(link_create); void FORWARD_2(link_set_map, RID, p_link, RID, p_map, rid_to_rid, rid_to_rid); diff --git a/modules/navigation/godot_navigation_server_2d.h b/modules/navigation/godot_navigation_server_2d.h index 337f5f40d8..88dee0ce69 100644 --- a/modules/navigation/godot_navigation_server_2d.h +++ b/modules/navigation/godot_navigation_server_2d.h @@ -76,6 +76,7 @@ public: virtual TypedArray<RID> map_get_agents(RID p_map) const override; virtual TypedArray<RID> map_get_obstacles(RID p_map) const override; virtual void map_force_update(RID p_map) override; + virtual Vector2 map_get_random_point(RID p_map, uint32_t p_navigation_layers, bool p_uniformly) const override; virtual RID region_create() override; virtual void region_set_enabled(RID p_region, bool p_enabled) override; @@ -98,6 +99,7 @@ public: virtual int region_get_connections_count(RID p_region) const override; virtual Vector2 region_get_connection_pathway_start(RID p_region, int p_connection_id) const override; virtual Vector2 region_get_connection_pathway_end(RID p_region, int p_connection_id) const override; + virtual Vector2 region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const override; virtual RID link_create() override; diff --git a/modules/navigation/nav_map.cpp b/modules/navigation/nav_map.cpp index ca1034dcc0..2add9f5ef5 100644 --- a/modules/navigation/nav_map.cpp +++ b/modules/navigation/nav_map.cpp @@ -769,6 +769,70 @@ void NavMap::remove_agent_as_controlled(NavAgent *agent) { } } +Vector3 NavMap::get_random_point(uint32_t p_navigation_layers, bool p_uniformly) const { + const LocalVector<NavRegion *> map_regions = get_regions(); + + if (map_regions.is_empty()) { + return Vector3(); + } + + LocalVector<const NavRegion *> accessible_regions; + + for (const NavRegion *region : map_regions) { + if (!region->get_enabled() || (p_navigation_layers & region->get_navigation_layers()) == 0) { + continue; + } + accessible_regions.push_back(region); + } + + if (accessible_regions.is_empty()) { + // All existing region polygons are disabled. + return Vector3(); + } + + if (p_uniformly) { + real_t accumulated_region_surface_area = 0; + RBMap<real_t, uint32_t> accessible_regions_area_map; + + for (uint32_t accessible_region_index = 0; accessible_region_index < accessible_regions.size(); accessible_region_index++) { + const NavRegion *region = accessible_regions[accessible_region_index]; + + real_t region_surface_area = region->get_surface_area(); + + if (region_surface_area == 0.0f) { + continue; + } + + accessible_regions_area_map[accumulated_region_surface_area] = accessible_region_index; + accumulated_region_surface_area += region_surface_area; + } + if (accessible_regions_area_map.is_empty() || accumulated_region_surface_area == 0) { + // All faces have no real surface / no area. + return Vector3(); + } + + real_t random_accessible_regions_area_map = Math::random(real_t(0), accumulated_region_surface_area); + + RBMap<real_t, uint32_t>::Iterator E = accessible_regions_area_map.find_closest(random_accessible_regions_area_map); + ERR_FAIL_COND_V(!E, Vector3()); + uint32_t random_region_index = E->value; + ERR_FAIL_INDEX_V(random_region_index, accessible_regions.size(), Vector3()); + + const NavRegion *random_region = accessible_regions[random_region_index]; + ERR_FAIL_NULL_V(random_region, Vector3()); + + return random_region->get_random_point(p_navigation_layers, p_uniformly); + + } else { + uint32_t random_region_index = Math::random(int(0), accessible_regions.size() - 1); + + const NavRegion *random_region = accessible_regions[random_region_index]; + ERR_FAIL_NULL_V(random_region, Vector3()); + + return random_region->get_random_point(p_navigation_layers, p_uniformly); + } +} + void NavMap::sync() { // Performance Monitor int _new_pm_region_count = regions.size(); @@ -1107,8 +1171,14 @@ void NavMap::_update_rvo_obstacles_tree_2d() { obstacle_vertex_count += obstacle->get_vertices().size(); } + // Cleaning old obstacles. + for (size_t i = 0; i < rvo_simulation_2d.obstacles_.size(); ++i) { + delete rvo_simulation_2d.obstacles_[i]; + } + rvo_simulation_2d.obstacles_.clear(); + // Cannot use LocalVector here as RVO library expects std::vector to build KdTree - std::vector<RVO2D::Obstacle2D *> raw_obstacles; + std::vector<RVO2D::Obstacle2D *> &raw_obstacles = rvo_simulation_2d.obstacles_; raw_obstacles.reserve(obstacle_vertex_count); // The following block is modified copy from RVO2D::AddObstacle() @@ -1128,6 +1198,11 @@ void NavMap::_update_rvo_obstacles_tree_2d() { real_t _obstacle_height = obstacle->get_height(); for (const Vector3 &_obstacle_vertex : _obstacle_vertices) { +#ifdef TOOLS_ENABLED + if (_obstacle_vertex.y != 0) { + WARN_PRINT_ONCE("Y coordinates of static obstacle vertices are ignored. Please use obstacle position Y to change elevation of obstacle."); + } +#endif rvo_2d_vertices.push_back(RVO2D::Vector2(_obstacle_vertex.x + _obstacle_position.x, _obstacle_vertex.z + _obstacle_position.z)); } diff --git a/modules/navigation/nav_map.h b/modules/navigation/nav_map.h index 5d78c14627..e8cbe7e247 100644 --- a/modules/navigation/nav_map.h +++ b/modules/navigation/nav_map.h @@ -190,6 +190,8 @@ public: return map_update_id; } + Vector3 get_random_point(uint32_t p_navigation_layers, bool p_uniformly) const; + void sync(); void step(real_t p_deltatime); void dispatch_callbacks(); diff --git a/modules/navigation/nav_region.cpp b/modules/navigation/nav_region.cpp index 09697c7be0..11b7759358 100644 --- a/modules/navigation/nav_region.cpp +++ b/modules/navigation/nav_region.cpp @@ -100,6 +100,88 @@ Vector3 NavRegion::get_connection_pathway_end(int p_connection_id) const { return connections[p_connection_id].pathway_end; } +Vector3 NavRegion::get_random_point(uint32_t p_navigation_layers, bool p_uniformly) const { + if (!get_enabled()) { + return Vector3(); + } + + const LocalVector<gd::Polygon> ®ion_polygons = get_polygons(); + + if (region_polygons.is_empty()) { + return Vector3(); + } + + if (p_uniformly) { + real_t accumulated_area = 0; + RBMap<real_t, uint32_t> region_area_map; + + for (uint32_t rp_index = 0; rp_index < region_polygons.size(); rp_index++) { + const gd::Polygon ®ion_polygon = region_polygons[rp_index]; + real_t polyon_area = region_polygon.surface_area; + + if (polyon_area == 0.0) { + continue; + } + region_area_map[accumulated_area] = rp_index; + accumulated_area += polyon_area; + } + if (region_area_map.is_empty() || accumulated_area == 0) { + // All polygons have no real surface / no area. + return Vector3(); + } + + real_t region_area_map_pos = Math::random(real_t(0), accumulated_area); + + RBMap<real_t, uint32_t>::Iterator region_E = region_area_map.find_closest(region_area_map_pos); + ERR_FAIL_COND_V(!region_E, Vector3()); + uint32_t rrp_polygon_index = region_E->value; + ERR_FAIL_INDEX_V(rrp_polygon_index, region_polygons.size(), Vector3()); + + const gd::Polygon &rr_polygon = region_polygons[rrp_polygon_index]; + + real_t accumulated_polygon_area = 0; + RBMap<real_t, uint32_t> polygon_area_map; + + for (uint32_t rpp_index = 2; rpp_index < rr_polygon.points.size(); rpp_index++) { + real_t face_area = Face3(rr_polygon.points[0].pos, rr_polygon.points[rpp_index - 1].pos, rr_polygon.points[rpp_index].pos).get_area(); + + if (face_area == 0.0) { + continue; + } + polygon_area_map[accumulated_polygon_area] = rpp_index; + accumulated_polygon_area += face_area; + } + if (polygon_area_map.is_empty() || accumulated_polygon_area == 0) { + // All faces have no real surface / no area. + return Vector3(); + } + + real_t polygon_area_map_pos = Math::random(real_t(0), accumulated_polygon_area); + + RBMap<real_t, uint32_t>::Iterator polygon_E = polygon_area_map.find_closest(polygon_area_map_pos); + ERR_FAIL_COND_V(!polygon_E, Vector3()); + uint32_t rrp_face_index = polygon_E->value; + ERR_FAIL_INDEX_V(rrp_face_index, rr_polygon.points.size(), Vector3()); + + const Face3 face(rr_polygon.points[0].pos, rr_polygon.points[rrp_face_index - 1].pos, rr_polygon.points[rrp_face_index].pos); + + Vector3 face_random_position = face.get_random_point_inside(); + return face_random_position; + + } else { + uint32_t rrp_polygon_index = Math::random(int(0), region_polygons.size() - 1); + + const gd::Polygon &rr_polygon = region_polygons[rrp_polygon_index]; + + uint32_t rrp_face_index = Math::random(int(2), rr_polygon.points.size() - 1); + + const Face3 face(rr_polygon.points[0].pos, rr_polygon.points[rrp_face_index - 1].pos, rr_polygon.points[rrp_face_index].pos); + + Vector3 face_random_position = face.get_random_point_inside(); + return face_random_position; + } +} + bool NavRegion::sync() { bool something_changed = polygons_dirty /* || something_dirty? */; @@ -113,6 +195,7 @@ void NavRegion::update_polygons() { return; } polygons.clear(); + surface_area = 0.0; polygons_dirty = false; if (map == nullptr) { @@ -147,21 +230,46 @@ void NavRegion::update_polygons() { polygons.resize(mesh->get_polygon_count()); + real_t _new_region_surface_area = 0.0; + // Build - for (size_t i(0); i < polygons.size(); i++) { - gd::Polygon &p = polygons[i]; - p.owner = this; + int navigation_mesh_polygon_index = 0; + for (gd::Polygon &polygon : polygons) { + polygon.owner = this; + polygon.surface_area = 0.0; - Vector<int> mesh_poly = mesh->get_polygon(i); - const int *indices = mesh_poly.ptr(); + Vector<int> navigation_mesh_polygon = mesh->get_polygon(navigation_mesh_polygon_index); + navigation_mesh_polygon_index += 1; + + int navigation_mesh_polygon_size = navigation_mesh_polygon.size(); + if (navigation_mesh_polygon_size < 3) { + continue; + } + + const int *indices = navigation_mesh_polygon.ptr(); bool valid(true); - p.points.resize(mesh_poly.size()); - p.edges.resize(mesh_poly.size()); - Vector3 center; + polygon.points.resize(navigation_mesh_polygon_size); + polygon.edges.resize(navigation_mesh_polygon_size); + + real_t _new_polygon_surface_area = 0.0; + + for (int j(2); j < navigation_mesh_polygon_size; j++) { + const Face3 face = Face3( + transform.xform(vertices_r[indices[0]]), + transform.xform(vertices_r[indices[j - 1]]), + transform.xform(vertices_r[indices[j]])); + + _new_polygon_surface_area += face.get_area(); + } + + polygon.surface_area = _new_polygon_surface_area; + _new_region_surface_area += _new_polygon_surface_area; + + Vector3 polygon_center; real_t sum(0); - for (int j(0); j < mesh_poly.size(); j++) { + for (int j(0); j < navigation_mesh_polygon_size; j++) { int idx = indices[j]; if (idx < 0 || idx >= len) { valid = false; @@ -169,10 +277,10 @@ void NavRegion::update_polygons() { } Vector3 point_position = transform.xform(vertices_r[idx]); - p.points[j].pos = point_position; - p.points[j].key = map->get_point_key(point_position); + polygon.points[j].pos = point_position; + polygon.points[j].key = map->get_point_key(point_position); - center += point_position; // Composing the center of the polygon + polygon_center += point_position; // Composing the center of the polygon if (j >= 2) { Vector3 epa = transform.xform(vertices_r[indices[j - 2]]); @@ -186,9 +294,11 @@ void NavRegion::update_polygons() { ERR_BREAK_MSG(!valid, "The navigation mesh set in this region is not valid!"); } - p.clockwise = sum > 0; - if (mesh_poly.size() != 0) { - p.center = center / real_t(mesh_poly.size()); + polygon.clockwise = sum > 0; + if (!navigation_mesh_polygon.is_empty()) { + polygon.center = polygon_center / real_t(navigation_mesh_polygon.size()); } } + + surface_area = _new_region_surface_area; } diff --git a/modules/navigation/nav_region.h b/modules/navigation/nav_region.h index 6a8ebe5336..a9cfc53c7e 100644 --- a/modules/navigation/nav_region.h +++ b/modules/navigation/nav_region.h @@ -50,6 +50,8 @@ class NavRegion : public NavBase { /// Cache LocalVector<gd::Polygon> polygons; + real_t surface_area = 0.0; + public: NavRegion() { type = NavigationUtilities::PathSegmentType::PATH_SEGMENT_TYPE_REGION; @@ -93,6 +95,10 @@ public: return polygons; } + Vector3 get_random_point(uint32_t p_navigation_layers, bool p_uniformly) const; + + real_t get_surface_area() const { return surface_area; }; + bool sync(); private: diff --git a/modules/navigation/nav_utils.h b/modules/navigation/nav_utils.h index 6ddd8b9078..aa5ccc96dc 100644 --- a/modules/navigation/nav_utils.h +++ b/modules/navigation/nav_utils.h @@ -112,6 +112,8 @@ struct Polygon { /// The center of this `Polygon` Vector3 center; + + real_t surface_area = 0.0; }; struct NavigationPoly { diff --git a/modules/openxr/action_map/openxr_action_map.cpp b/modules/openxr/action_map/openxr_action_map.cpp index 72866f1cf7..bbcb63a7e6 100644 --- a/modules/openxr/action_map/openxr_action_map.cpp +++ b/modules/openxr/action_map/openxr_action_map.cpp @@ -177,7 +177,6 @@ void OpenXRActionMap::create_default_action_sets() { Ref<OpenXRAction> trigger_touch = action_set->add_new_action("trigger_touch", "Trigger touching", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right"); Ref<OpenXRAction> grip = action_set->add_new_action("grip", "Grip", OpenXRAction::OPENXR_ACTION_FLOAT, "/user/hand/left,/user/hand/right"); Ref<OpenXRAction> grip_click = action_set->add_new_action("grip_click", "Grip click", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right"); - Ref<OpenXRAction> grip_touch = action_set->add_new_action("grip_touch", "Grip touching", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right"); Ref<OpenXRAction> grip_force = action_set->add_new_action("grip_force", "Grip force", OpenXRAction::OPENXR_ACTION_FLOAT, "/user/hand/left,/user/hand/right"); Ref<OpenXRAction> primary = action_set->add_new_action("primary", "Primary joystick/thumbstick/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2, "/user/hand/left,/user/hand/right"); Ref<OpenXRAction> primary_click = action_set->add_new_action("primary_click", "Primary joystick/thumbstick/trackpad click", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right"); diff --git a/modules/openxr/doc_classes/OpenXRInterface.xml b/modules/openxr/doc_classes/OpenXRInterface.xml index 6d1c215ffc..666d20d7e7 100644 --- a/modules/openxr/doc_classes/OpenXRInterface.xml +++ b/modules/openxr/doc_classes/OpenXRInterface.xml @@ -129,9 +129,11 @@ </member> <member name="foveation_dynamic" type="bool" setter="set_foveation_dynamic" getter="get_foveation_dynamic" default="false"> Enable dynamic foveation adjustment, the interface must be initialized before this is accessible. If enabled foveation will automatically adjusted between low and [member foveation_level]. + [b]Note:[/b] Only works on compatibility renderer. </member> <member name="foveation_level" type="int" setter="set_foveation_level" getter="get_foveation_level" default="0"> Set foveation level from 0 (off) to 3 (high), the interface must be initialized before this is accessible. + [b]Note:[/b] Only works on compatibility renderer. </member> <member name="render_target_size_multiplier" type="float" setter="set_render_target_size_multiplier" getter="get_render_target_size_multiplier" default="1.0"> The render size multiplier for the current HMD. Must be set before the interface has been initialized. diff --git a/modules/tinyexr/image_loader_tinyexr.cpp b/modules/tinyexr/image_loader_tinyexr.cpp index f64bb14e4a..8720ca56f6 100644 --- a/modules/tinyexr/image_loader_tinyexr.cpp +++ b/modules/tinyexr/image_loader_tinyexr.cpp @@ -68,6 +68,7 @@ Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, Ref<FileAccess> f, BitF if (ret != TINYEXR_SUCCESS) { if (err) { ERR_PRINT(String(err)); + FreeEXRErrorMessage(err); } return ERR_FILE_CORRUPT; } @@ -86,6 +87,7 @@ Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, Ref<FileAccess> f, BitF if (ret != TINYEXR_SUCCESS) { if (err) { ERR_PRINT(String(err)); + FreeEXRErrorMessage(err); } return ERR_FILE_CORRUPT; } diff --git a/modules/zip/zip_packer.cpp b/modules/zip/zip_packer.cpp index 5f623476fc..e67c65d4d1 100644 --- a/modules/zip/zip_packer.cpp +++ b/modules/zip/zip_packer.cpp @@ -72,7 +72,24 @@ Error ZIPPacker::start_file(const 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; } diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp index f0ee405b41..10d54e8d97 100644 --- a/platform/android/export/export_plugin.cpp +++ b/platform/android/export/export_plugin.cpp @@ -460,7 +460,7 @@ String EditorExportPlatformAndroid::get_valid_basename() const { if (is_digit(c) && first) { continue; } - if (is_ascii_alphanumeric_char(c)) { + if (is_ascii_identifier_char(c)) { name += String::chr(c); first = false; } @@ -537,13 +537,6 @@ bool EditorExportPlatformAndroid::is_package_name_valid(const String &p_package, return false; } - if (p_package.find("$genname") >= 0 && !is_project_name_valid()) { - if (r_error) { - *r_error = TTR("The project name does not meet the requirement for the package name format. Please explicitly specify the package name."); - } - return false; - } - return true; } @@ -2446,6 +2439,13 @@ bool EditorExportPlatformAndroid::has_valid_project_configuration(const Ref<Edit err += "\n"; } + String package_name = p_preset->get("package/unique_name"); + if (package_name.find("$genname") >= 0 && !is_project_name_valid()) { + // Warning only, so don't override `valid`. + err += vformat(TTR("The project name does not meet the requirement for the package name format and will be updated to \"%s\". Please explicitly specify the package name if needed."), get_valid_basename()); + err += "\n"; + } + r_error = err; return valid; } @@ -2782,6 +2782,12 @@ Error EditorExportPlatformAndroid::export_project(const Ref<EditorExportPreset> Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int export_format, bool should_sign, int p_flags) { ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); + const String base_dir = p_path.get_base_dir(); + if (!DirAccess::exists(base_dir)) { + add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Target folder does not exist or is inaccessible: \"%s\""), base_dir)); + return ERR_FILE_BAD_PATH; + } + String src_apk; Error err; @@ -2856,7 +2862,10 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP } const String assets_directory = get_assets_directory(p_preset, export_format); String sdk_path = EDITOR_GET("export/android/android_sdk_path"); - ERR_FAIL_COND_V_MSG(sdk_path.is_empty(), ERR_UNCONFIGURED, "Android SDK path must be configured in Editor Settings at 'export/android/android_sdk_path'."); + if (sdk_path.is_empty()) { + add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Android SDK path must be configured in Editor Settings at 'export/android/android_sdk_path'.")); + return ERR_UNCONFIGURED; + } print_verbose("Android sdk path: " + sdk_path); // TODO: should we use "package/name" or "application/config/name"? @@ -3106,10 +3115,6 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP } } - if (!DirAccess::exists(p_path.get_base_dir())) { - return ERR_FILE_BAD_PATH; - } - Ref<FileAccess> io_fa; zlib_filefunc_def io = zipio_create_io(&io_fa); @@ -3302,10 +3307,6 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP zipClose(unaligned_apk, nullptr); unzClose(pkg); - if (err != OK) { - CLEANUP_AND_RETURN(err); - } - // Let's zip-align (must be done before signing) static const int ZIP_ALIGNMENT = 4; @@ -3392,6 +3393,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP // file will invalidate the signature. err = sign_apk(p_preset, p_debug, p_path, ep); if (err != OK) { + // Message is supplied by the subroutine method. CLEANUP_AND_RETURN(err); } } diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt index 2278b46f6c..217e7a2b60 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt @@ -184,7 +184,7 @@ class Godot(private val context: Context) : SensorEventListener { return } - beginBenchmarkMeasure("Godot::onCreate") + beginBenchmarkMeasure("Startup", "Godot::onCreate") try { this.primaryHost = primaryHost val activity = requireActivity() @@ -286,7 +286,7 @@ class Godot(private val context: Context) : SensorEventListener { initializationStarted = false throw e } finally { - endBenchmarkMeasure("Godot::onCreate"); + endBenchmarkMeasure("Startup", "Godot::onCreate"); } } @@ -1016,13 +1016,13 @@ class Godot(private val context: Context) : SensorEventListener { } @Keep - private fun nativeBeginBenchmarkMeasure(label: String) { - beginBenchmarkMeasure(label) + private fun nativeBeginBenchmarkMeasure(scope: String, label: String) { + beginBenchmarkMeasure(scope, label) } @Keep - private fun nativeEndBenchmarkMeasure(label: String) { - endBenchmarkMeasure(label) + private fun nativeEndBenchmarkMeasure(scope: String, label: String) { + endBenchmarkMeasure(scope, label) } @Keep diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java b/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java index 120e1722e5..f1c029e7a1 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java @@ -172,7 +172,7 @@ public class GodotFragment extends Fragment implements IDownloaderClient, GodotH @Override public void onCreate(Bundle icicle) { - BenchmarkUtils.beginBenchmarkMeasure("GodotFragment::onCreate"); + BenchmarkUtils.beginBenchmarkMeasure("Startup", "GodotFragment::onCreate"); super.onCreate(icicle); final Activity activity = getActivity(); @@ -180,7 +180,7 @@ public class GodotFragment extends Fragment implements IDownloaderClient, GodotH godot = new Godot(requireContext()); performEngineInitialization(); - BenchmarkUtils.endBenchmarkMeasure("GodotFragment::onCreate"); + BenchmarkUtils.endBenchmarkMeasure("Startup", "GodotFragment::onCreate"); } private void performEngineInitialization() { diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/BenchmarkUtils.kt b/platform/android/java/lib/src/org/godotengine/godot/utils/BenchmarkUtils.kt index 1552c8f082..69748c0a8d 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/utils/BenchmarkUtils.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/utils/BenchmarkUtils.kt @@ -41,7 +41,7 @@ import org.godotengine.godot.io.file.FileAccessFlags import org.godotengine.godot.io.file.FileAccessHandler import org.json.JSONObject import java.nio.ByteBuffer -import java.util.concurrent.ConcurrentSkipListMap +import java.util.Collections /** * Contains benchmark related utilities methods @@ -51,44 +51,47 @@ private const val TAG = "GodotBenchmark" var useBenchmark = false var benchmarkFile = "" -private val startBenchmarkFrom = ConcurrentSkipListMap<String, Long>() -private val benchmarkTracker = ConcurrentSkipListMap<String, Double>() +private val startBenchmarkFrom = Collections.synchronizedMap(LinkedHashMap<Pair<String, String>, Long>()) +private val benchmarkTracker = Collections.synchronizedMap(LinkedHashMap<Pair<String, String>, Double>()) /** - * Start measuring and tracing the execution of a given section of code using the given label. + * Start measuring and tracing the execution of a given section of code using the given label + * within the given scope. * * Must be followed by a call to [endBenchmarkMeasure]. * * Note: Only enabled on 'editorDev' build variant. */ -fun beginBenchmarkMeasure(label: String) { +fun beginBenchmarkMeasure(scope: String, label: String) { if (BuildConfig.FLAVOR != "editor" || BuildConfig.BUILD_TYPE != "dev") { return } - startBenchmarkFrom[label] = SystemClock.elapsedRealtime() + val key = Pair(scope, label) + startBenchmarkFrom[key] = SystemClock.elapsedRealtime() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - Trace.beginAsyncSection(label, 0) + Trace.beginAsyncSection("[$scope] $label", 0) } } /** - * End measuring and tracing of the section of code with the given label. + * End measuring and tracing of the section of code with the given label within the given scope. * * Must be preceded by a call [beginBenchmarkMeasure] * * * Note: Only enabled on 'editorDev' build variant. */ -fun endBenchmarkMeasure(label: String) { +fun endBenchmarkMeasure(scope: String, label: String) { if (BuildConfig.FLAVOR != "editor" || BuildConfig.BUILD_TYPE != "dev") { return } - val startTime = startBenchmarkFrom[label] ?: return + val key = Pair(scope, label) + val startTime = startBenchmarkFrom[key] ?: return val total = SystemClock.elapsedRealtime() - startTime - benchmarkTracker[label] = total / 1000.0 + benchmarkTracker[key] = total / 1_000.0 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - Trace.endAsyncSection(label, 0) + Trace.endAsyncSection("[$scope] $label", 0) } } @@ -107,8 +110,16 @@ fun dumpBenchmark(fileAccessHandler: FileAccessHandler?, filepath: String? = ben return } + val results = LinkedHashMap<String, String>() + for (entry in benchmarkTracker) { + if (!results.containsKey(entry.key.first)) { + results[entry.key.first] = "" + } + results[entry.key.first] += "\t\t- ${entry.key.second}: ${entry.value * 1_000.0} msec.\n" + } + val printOut = - benchmarkTracker.map { "\t- ${it.key} : ${it.value} sec." }.joinToString("\n") + results.map { "\t- [${it.key}]\n ${it.value}" }.joinToString("\n") Log.i(TAG, "BENCHMARK:\n$printOut") if (fileAccessHandler != null && !filepath.isNullOrBlank()) { diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp index cb6ebf14a8..4e401e633e 100644 --- a/platform/android/java_godot_wrapper.cpp +++ b/platform/android/java_godot_wrapper.cpp @@ -78,8 +78,8 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_ _on_godot_main_loop_started = p_env->GetMethodID(godot_class, "onGodotMainLoopStarted", "()V"); _create_new_godot_instance = p_env->GetMethodID(godot_class, "createNewGodotInstance", "([Ljava/lang/String;)I"); _get_render_view = p_env->GetMethodID(godot_class, "getRenderView", "()Lorg/godotengine/godot/GodotRenderView;"); - _begin_benchmark_measure = p_env->GetMethodID(godot_class, "nativeBeginBenchmarkMeasure", "(Ljava/lang/String;)V"); - _end_benchmark_measure = p_env->GetMethodID(godot_class, "nativeEndBenchmarkMeasure", "(Ljava/lang/String;)V"); + _begin_benchmark_measure = p_env->GetMethodID(godot_class, "nativeBeginBenchmarkMeasure", "(Ljava/lang/String;Ljava/lang/String;)V"); + _end_benchmark_measure = p_env->GetMethodID(godot_class, "nativeEndBenchmarkMeasure", "(Ljava/lang/String;Ljava/lang/String;)V"); _dump_benchmark = p_env->GetMethodID(godot_class, "nativeDumpBenchmark", "(Ljava/lang/String;)V"); _get_gdextension_list_config_file = p_env->GetMethodID(godot_class, "getGDExtensionConfigFiles", "()[Ljava/lang/String;"); _has_feature = p_env->GetMethodID(godot_class, "hasFeature", "(Ljava/lang/String;)Z"); @@ -348,21 +348,23 @@ int GodotJavaWrapper::create_new_godot_instance(List<String> args) { } } -void GodotJavaWrapper::begin_benchmark_measure(const String &p_label) { +void GodotJavaWrapper::begin_benchmark_measure(const String &p_context, const String &p_label) { if (_begin_benchmark_measure) { JNIEnv *env = get_jni_env(); ERR_FAIL_NULL(env); + jstring j_context = env->NewStringUTF(p_context.utf8().get_data()); jstring j_label = env->NewStringUTF(p_label.utf8().get_data()); - env->CallVoidMethod(godot_instance, _begin_benchmark_measure, j_label); + env->CallVoidMethod(godot_instance, _begin_benchmark_measure, j_context, j_label); } } -void GodotJavaWrapper::end_benchmark_measure(const String &p_label) { +void GodotJavaWrapper::end_benchmark_measure(const String &p_context, const String &p_label) { if (_end_benchmark_measure) { JNIEnv *env = get_jni_env(); ERR_FAIL_NULL(env); + jstring j_context = env->NewStringUTF(p_context.utf8().get_data()); jstring j_label = env->NewStringUTF(p_label.utf8().get_data()); - env->CallVoidMethod(godot_instance, _end_benchmark_measure, j_label); + env->CallVoidMethod(godot_instance, _end_benchmark_measure, j_context, j_label); } } diff --git a/platform/android/java_godot_wrapper.h b/platform/android/java_godot_wrapper.h index 920df958c2..52043c6027 100644 --- a/platform/android/java_godot_wrapper.h +++ b/platform/android/java_godot_wrapper.h @@ -105,8 +105,8 @@ public: void vibrate(int p_duration_ms); String get_input_fallback_mapping(); int create_new_godot_instance(List<String> args); - void begin_benchmark_measure(const String &p_label); - void end_benchmark_measure(const String &p_label); + void begin_benchmark_measure(const String &p_context, const String &p_label); + void end_benchmark_measure(const String &p_context, const String &p_label); void dump_benchmark(const String &benchmark_file); // Return the list of gdextensions config file. diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp index 92dc5f909f..0d82bec75d 100644 --- a/platform/android/os_android.cpp +++ b/platform/android/os_android.cpp @@ -708,15 +708,15 @@ String OS_Android::get_config_path() const { return get_user_data_dir().path_join("config"); } -void OS_Android::benchmark_begin_measure(const String &p_what) { +void OS_Android::benchmark_begin_measure(const String &p_context, const String &p_what) { #ifdef TOOLS_ENABLED - godot_java->begin_benchmark_measure(p_what); + godot_java->begin_benchmark_measure(p_context, p_what); #endif } -void OS_Android::benchmark_end_measure(const String &p_what) { +void OS_Android::benchmark_end_measure(const String &p_context, const String &p_what) { #ifdef TOOLS_ENABLED - godot_java->end_benchmark_measure(p_what); + godot_java->end_benchmark_measure(p_context, p_what); #endif } diff --git a/platform/android/os_android.h b/platform/android/os_android.h index f88f3e0518..837ef8bad5 100644 --- a/platform/android/os_android.h +++ b/platform/android/os_android.h @@ -165,8 +165,8 @@ public: virtual Error setup_remote_filesystem(const String &p_server_host, int p_port, const String &p_password, String &r_project_path) override; - virtual void benchmark_begin_measure(const String &p_what) override; - virtual void benchmark_end_measure(const String &p_what) override; + virtual void benchmark_begin_measure(const String &p_context, const String &p_what) override; + virtual void benchmark_end_measure(const String &p_context, const String &p_what) override; virtual void benchmark_dump() override; virtual void load_platform_gdextensions() const override; diff --git a/platform/ios/display_server_ios.mm b/platform/ios/display_server_ios.mm index 2561c1c095..60da16ae8c 100644 --- a/platform/ios/display_server_ios.mm +++ b/platform/ios/display_server_ios.mm @@ -443,7 +443,11 @@ int DisplayServerIOS::screen_get_dpi(int p_screen) const { } float DisplayServerIOS::screen_get_refresh_rate(int p_screen) const { - return [UIScreen mainScreen].maximumFramesPerSecond; + float fps = [UIScreen mainScreen].maximumFramesPerSecond; + if ([NSProcessInfo processInfo].lowPowerModeEnabled) { + fps = 60; + } + return fps; } float DisplayServerIOS::screen_get_scale(int p_screen) const { diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp index c0e052865f..86c3feb962 100644 --- a/platform/ios/export/export_plugin.cpp +++ b/platform/ios/export/export_plugin.cpp @@ -620,7 +620,10 @@ Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_pr String sizes; Ref<DirAccess> da = DirAccess::open(p_iconset_dir); - ERR_FAIL_COND_V_MSG(da.is_null(), ERR_CANT_OPEN, "Cannot open directory '" + p_iconset_dir + "'."); + if (da.is_null()) { + add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat(TTR("Could not open a directory at path \"%s\"."), p_iconset_dir)); + return ERR_CANT_OPEN; + } Color boot_bg_color = GLOBAL_GET("application/boot_splash/bg_color"); @@ -692,12 +695,20 @@ Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_pr json_description += "]}"; Ref<FileAccess> json_file = FileAccess::open(p_iconset_dir + "Contents.json", FileAccess::WRITE); - ERR_FAIL_COND_V(json_file.is_null(), ERR_CANT_CREATE); + if (json_file.is_null()) { + add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat(TTR("Could not write to a file at path \"%s\"."), p_iconset_dir + "Contents.json")); + return ERR_CANT_CREATE; + } + CharString json_utf8 = json_description.utf8(); json_file->store_buffer((const uint8_t *)json_utf8.get_data(), json_utf8.length()); Ref<FileAccess> sizes_file = FileAccess::open(p_iconset_dir + "sizes", FileAccess::WRITE); - ERR_FAIL_COND_V(sizes_file.is_null(), ERR_CANT_CREATE); + if (sizes_file.is_null()) { + add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat(TTR("Could not write to a file at path \"%s\"."), p_iconset_dir + "sizes")); + return ERR_CANT_CREATE; + } + CharString sizes_utf8 = sizes.utf8(); sizes_file->store_buffer((const uint8_t *)sizes_utf8.get_data(), sizes_utf8.length()); @@ -1219,10 +1230,10 @@ Error EditorExportPlatformIOS::_export_additional_assets(const String &p_out_dir String asset = p_assets[f_idx]; if (asset.begins_with("res://")) { Error err = _copy_asset(p_out_dir, asset, nullptr, p_is_framework, p_should_embed, r_exported_assets); - ERR_FAIL_COND_V(err, err); + ERR_FAIL_COND_V(err != OK, err); } else if (ProjectSettings::get_singleton()->localize_path(asset).begins_with("res://")) { Error err = _copy_asset(p_out_dir, ProjectSettings::get_singleton()->localize_path(asset), nullptr, p_is_framework, p_should_embed, r_exported_assets); - ERR_FAIL_COND_V(err, err); + ERR_FAIL_COND_V(err != OK, err); } else { // either SDK-builtin or already a part of the export template IOSExportAsset exported_asset = { asset, p_is_framework, p_should_embed }; @@ -1306,8 +1317,7 @@ Error EditorExportPlatformIOS::_export_ios_plugins(const Ref<EditorExportPreset> // We shouldn't embed .xcframework that contains static libraries. // Static libraries are not embedded anyway. err = _copy_asset(dest_dir, plugin_main_binary, &plugin_binary_result_file, true, false, r_exported_assets); - - ERR_FAIL_COND_V(err, err); + ERR_FAIL_COND_V(err != OK, err); // Adding dependencies. // Use separate container for names to check for duplicates. @@ -1432,15 +1442,15 @@ Error EditorExportPlatformIOS::_export_ios_plugins(const Ref<EditorExportPreset> { // Export linked plugin dependency err = _export_additional_assets(dest_dir, plugin_linked_dependencies, true, false, r_exported_assets); - ERR_FAIL_COND_V(err, err); + ERR_FAIL_COND_V(err != OK, err); // Export embedded plugin dependency err = _export_additional_assets(dest_dir, plugin_embedded_dependencies, true, true, r_exported_assets); - ERR_FAIL_COND_V(err, err); + ERR_FAIL_COND_V(err != OK, err); // Export plugin files err = _export_additional_assets(dest_dir, plugin_files, false, false, r_exported_assets); - ERR_FAIL_COND_V(err, err); + ERR_FAIL_COND_V(err != OK, err); } // Update CPP @@ -1493,20 +1503,29 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p return _export_project_helper(p_preset, p_debug, p_path, p_flags, false, false); } -Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags, bool p_simulator, bool p_skip_ipa) { +Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags, bool p_simulator, bool p_oneclick) { ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); - String src_pkg_name; - String dest_dir = p_path.get_base_dir() + "/"; - String binary_name = p_path.get_file().get_basename(); + const String dest_dir = p_path.get_base_dir() + "/"; + const String binary_name = p_path.get_file().get_basename(); + const String binary_dir = dest_dir + binary_name; + + if (!DirAccess::exists(dest_dir)) { + add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Target folder does not exist or is inaccessible: \"%s\""), dest_dir)); + return ERR_FILE_BAD_PATH; + } bool export_project_only = p_preset->get("application/export_project_only"); + if (p_oneclick) { + export_project_only = false; // Skip for one-click deploy. + } EditorProgress ep("export", export_project_only ? TTR("Exporting for iOS (Project Files Only)") : TTR("Exporting for iOS"), export_project_only ? 2 : 5, true); String team_id = p_preset->get("application/app_store_team_id"); ERR_FAIL_COND_V_MSG(team_id.length() == 0, ERR_CANT_OPEN, "App Store Team ID not specified - cannot configure the project."); + String src_pkg_name; if (p_debug) { src_pkg_name = p_preset->get("custom_template/debug"); } else { @@ -1522,10 +1541,6 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres } } - if (!DirAccess::exists(dest_dir)) { - return ERR_FILE_BAD_PATH; - } - { Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); if (da.is_valid()) { @@ -1533,18 +1548,19 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres // remove leftovers from last export so they don't interfere // in case some files are no longer needed - if (da->change_dir(dest_dir + binary_name + ".xcodeproj") == OK) { + if (da->change_dir(binary_dir + ".xcodeproj") == OK) { da->erase_contents_recursive(); } - if (da->change_dir(dest_dir + binary_name) == OK) { + if (da->change_dir(binary_dir) == OK) { da->erase_contents_recursive(); } da->change_dir(current_dir); - if (!da->dir_exists(dest_dir + binary_name)) { - Error err = da->make_dir(dest_dir + binary_name); - if (err) { + if (!da->dir_exists(binary_dir)) { + Error err = da->make_dir(binary_dir); + if (err != OK) { + add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Failed to create the directory: \"%s\""), binary_dir)); return err; } } @@ -1554,10 +1570,11 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres if (ep.step("Making .pck", 0)) { return ERR_SKIP; } - String pack_path = dest_dir + binary_name + ".pck"; + String pack_path = binary_dir + ".pck"; Vector<SharedObject> libraries; Error err = save_pack(p_preset, p_debug, pack_path, &libraries); if (err) { + // Message is supplied by the subroutine method. return err; } @@ -1606,7 +1623,10 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres Vector<IOSExportAsset> assets; Ref<DirAccess> tmp_app_path = DirAccess::create_for_path(dest_dir); - ERR_FAIL_COND_V(tmp_app_path.is_null(), ERR_CANT_CREATE); + if (tmp_app_path.is_null()) { + add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Templates"), vformat(TTR("Could not create and open the directory: \"%s\""), dest_dir)); + return ERR_CANT_CREATE; + } print_line("Unzipping..."); Ref<FileAccess> io_fa; @@ -1617,8 +1637,14 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres return ERR_CANT_OPEN; } - err = _export_ios_plugins(p_preset, config_data, dest_dir + binary_name, assets, p_debug); - ERR_FAIL_COND_V(err, err); + err = _export_ios_plugins(p_preset, config_data, binary_dir, assets, p_debug); + if (err != OK) { + // TODO: Improve error reporting by using `add_message` throughout all methods called via `_export_ios_plugins`. + // For now a generic top level message would be fine, but we're ought to use proper reporting here instead of + // just fail macros and non-descriptive error return values. + add_message(EXPORT_MESSAGE_ERROR, TTR("iOS Plugins"), vformat(TTR("Failed to export iOS plugins with code %d. Please check the output log."), err)); + return err; + } //export rest of the files int ret = unzGoToFirstFile(src_pkg_zip); @@ -1683,8 +1709,8 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres print_line("Creating " + dir_name); Error dir_err = tmp_app_path->make_dir_recursive(dir_name); if (dir_err) { - ERR_PRINT("Can't create '" + dir_name + "'."); unzClose(src_pkg_zip); + add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Could not create a directory at path \"%s\"."), dir_name)); return ERR_CANT_CREATE; } } @@ -1693,8 +1719,8 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres { Ref<FileAccess> f = FileAccess::open(file, FileAccess::WRITE); if (f.is_null()) { - ERR_PRINT("Can't write '" + file + "'."); unzClose(src_pkg_zip); + add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Could not write to a file at path \"%s\"."), file)); return ERR_CANT_CREATE; }; f->store_buffer(data.ptr(), data.size()); @@ -1715,7 +1741,7 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres unzClose(src_pkg_zip); if (!found_library) { - ERR_PRINT("Requested template library '" + library_to_use + "' not found. It might be missing from your template archive."); + add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Requested template library '%s' not found. It might be missing from your template archive."), library_to_use)); return ERR_FILE_NOT_FOUND; } @@ -1727,7 +1753,7 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres Vector<String> translations = GLOBAL_GET("internationalization/locale/translations"); if (translations.size() > 0) { { - String fname = dest_dir + binary_name + "/en.lproj"; + String fname = binary_dir + "/en.lproj"; tmp_app_path->make_dir_recursive(fname); Ref<FileAccess> f = FileAccess::open(fname + "/InfoPlist.strings", FileAccess::WRITE); f->store_line("/* Localized versions of Info.plist keys */"); @@ -1747,7 +1773,7 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres } for (const String &lang : languages) { - String fname = dest_dir + binary_name + "/" + lang + ".lproj"; + String fname = binary_dir + "/" + lang + ".lproj"; tmp_app_path->make_dir_recursive(fname); Ref<FileAccess> f = FileAccess::open(fname + "/InfoPlist.strings", FileAccess::WRITE); f->store_line("/* Localized versions of Info.plist keys */"); @@ -1776,34 +1802,37 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres String dest_lib_file_path = dest_dir + static_lib_path.get_file(); Error lib_copy_err = tmp_app_path->copy(static_lib_path, dest_lib_file_path); if (lib_copy_err != OK) { - ERR_PRINT("Can't copy '" + static_lib_path + "'."); + add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Could not copy a file at path \"%s\" to \"%s\"."), static_lib_path, dest_lib_file_path)); return lib_copy_err; } } } - String iconset_dir = dest_dir + binary_name + "/Images.xcassets/AppIcon.appiconset/"; + String iconset_dir = binary_dir + "/Images.xcassets/AppIcon.appiconset/"; err = OK; if (!tmp_app_path->dir_exists(iconset_dir)) { err = tmp_app_path->make_dir_recursive(iconset_dir); } - if (err) { + if (err != OK) { + add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Could not create a directory at path \"%s\"."), iconset_dir)); return err; } err = _export_icons(p_preset, iconset_dir); - if (err) { + if (err != OK) { + // Message is supplied by the subroutine method. return err; } { bool use_storyboard = p_preset->get("storyboard/use_launch_screen_storyboard"); - String launch_image_path = dest_dir + binary_name + "/Images.xcassets/LaunchImage.launchimage/"; - String splash_image_path = dest_dir + binary_name + "/Images.xcassets/SplashImage.imageset/"; + String launch_image_path = binary_dir + "/Images.xcassets/LaunchImage.launchimage/"; + String splash_image_path = binary_dir + "/Images.xcassets/SplashImage.imageset/"; Ref<DirAccess> launch_screen_da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); if (launch_screen_da.is_null()) { + add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Could not access the filesystem.")); return ERR_CANT_CREATE; } @@ -1816,10 +1845,11 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres } err = _export_loading_screen_file(p_preset, splash_image_path); + add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Failed to create a file at path \"%s\" with code %d."), splash_image_path, err)); } else { print_line("Using Launch Images"); - const String launch_screen_path = dest_dir + binary_name + "/Launch Screen.storyboard"; + const String launch_screen_path = binary_dir + "/Launch Screen.storyboard"; launch_screen_da->remove(launch_screen_path); @@ -1829,21 +1859,22 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres } err = _export_loading_screen_images(p_preset, launch_image_path); + add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Failed to create a file at path \"%s\" with code %d."), launch_image_path, err)); } } - if (err) { + if (err != OK) { return err; } print_line("Exporting additional assets"); - _export_additional_assets(dest_dir + binary_name, libraries, assets); + _export_additional_assets(binary_dir, libraries, assets); _add_assets_to_project(p_preset, project_file_data, assets); - String project_file_name = dest_dir + binary_name + ".xcodeproj/project.pbxproj"; + String project_file_name = binary_dir + ".xcodeproj/project.pbxproj"; { Ref<FileAccess> f = FileAccess::open(project_file_name, FileAccess::WRITE); if (f.is_null()) { - ERR_PRINT("Can't write '" + project_file_name + "'."); + add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Could not write to a file at path \"%s\"."), project_file_name)); return ERR_CANT_CREATE; }; f->store_buffer(project_file_data.ptr(), project_file_data.size()); @@ -1854,7 +1885,7 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres if (ep.step("Code-signing dylibs", 2)) { return ERR_SKIP; } - Ref<DirAccess> dylibs_dir = DirAccess::open(dest_dir + binary_name + "/dylibs"); + Ref<DirAccess> dylibs_dir = DirAccess::open(binary_dir + "/dylibs"); ERR_FAIL_COND_V(dylibs_dir.is_null(), ERR_CANT_OPEN); CodesignData codesign_data(p_preset, p_debug); err = _walk_dir_recursive(dylibs_dir, _codesign, &codesign_data); @@ -1871,10 +1902,11 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres if (ep.step("Making .xcarchive", 3)) { return ERR_SKIP; } + String archive_path = p_path.get_basename() + ".xcarchive"; List<String> archive_args; archive_args.push_back("-project"); - archive_args.push_back(dest_dir + binary_name + ".xcodeproj"); + archive_args.push_back(binary_dir + ".xcodeproj"); archive_args.push_back("-scheme"); archive_args.push_back(binary_name); archive_args.push_back("-sdk"); @@ -1895,31 +1927,42 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres archive_args.push_back("-allowProvisioningUpdates"); archive_args.push_back("-archivePath"); archive_args.push_back(archive_path); + String archive_str; err = OS::get_singleton()->execute("xcodebuild", archive_args, &archive_str, nullptr, true); - ERR_FAIL_COND_V(err, err); + if (err != OK) { + add_message(EXPORT_MESSAGE_ERROR, TTR("Xcode Build"), vformat(TTR("Failed to run xcodebuild with code %d"), err)); + return err; + } + print_line("xcodebuild (.xcarchive):\n" + archive_str); if (!archive_str.contains("** ARCHIVE SUCCEEDED **")) { add_message(EXPORT_MESSAGE_ERROR, TTR("Xcode Build"), TTR("Xcode project build failed, see editor log for details.")); return FAILED; } - if (!p_skip_ipa) { + if (!p_oneclick) { if (ep.step("Making .ipa", 4)) { return ERR_SKIP; } + List<String> export_args; export_args.push_back("-exportArchive"); export_args.push_back("-archivePath"); export_args.push_back(archive_path); export_args.push_back("-exportOptionsPlist"); - export_args.push_back(dest_dir + binary_name + "/export_options.plist"); + export_args.push_back(binary_dir + "/export_options.plist"); export_args.push_back("-allowProvisioningUpdates"); export_args.push_back("-exportPath"); export_args.push_back(dest_dir); + String export_str; err = OS::get_singleton()->execute("xcodebuild", export_args, &export_str, nullptr, true); - ERR_FAIL_COND_V(err, err); + if (err != OK) { + add_message(EXPORT_MESSAGE_ERROR, TTR("Xcode Build"), vformat(TTR("Failed to run xcodebuild with code %d"), err)); + return err; + } + print_line("xcodebuild (.ipa):\n" + export_str); if (!export_str.contains("** EXPORT SUCCEEDED **")) { add_message(EXPORT_MESSAGE_ERROR, TTR("Xcode Build"), TTR(".ipa export failed, see editor log for details.")); @@ -2074,15 +2117,21 @@ bool EditorExportPlatformIOS::is_package_name_valid(const String &p_package, Str bool EditorExportPlatformIOS::_check_xcode_install() { static bool xcode_found = false; if (!xcode_found) { - String xcode_path; - List<String> args; - args.push_back("-p"); - int ec = 0; - Error err = OS::get_singleton()->execute("xcode-select", args, &xcode_path, &ec, true); - if (err != OK || ec != 0) { - return false; + Vector<String> mdfind_paths; + List<String> mdfind_args; + mdfind_args.push_back("kMDItemCFBundleIdentifier=com.apple.dt.Xcode"); + + String output; + Error err = OS::get_singleton()->execute("mdfind", mdfind_args, &output); + if (err == OK) { + mdfind_paths = output.split("\n"); + } + for (const String &found_path : mdfind_paths) { + xcode_found = !found_path.is_empty() && DirAccess::dir_exists_absolute(found_path.strip_edges()); + if (xcode_found) { + break; + } } - xcode_found = DirAccess::dir_exists_absolute(xcode_path.strip_edges()); } return xcode_found; } @@ -2112,12 +2161,9 @@ void EditorExportPlatformIOS::_check_for_changes_poll_thread(void *ud) { // Check for devices updates. Vector<Device> ldevices; - // Enum real devices. + // Enum real devices (via ios_deploy, pre Xcode 15). String idepl = EDITOR_GET("export/ios/ios_deploy"); - if (idepl.is_empty()) { - idepl = "ios-deploy"; - } - { + if (!idepl.is_empty()) { String devices; List<String> args; args.push_back("-c"); @@ -2143,8 +2189,9 @@ void EditorExportPlatformIOS::_check_for_changes_poll_thread(void *ud) { Dictionary device_info = device_event["Device"]; Device nd; nd.id = device_info["DeviceIdentifier"]; - nd.name = device_info["DeviceName"].operator String() + " (connected through " + device_event["Interface"].operator String() + ")"; + nd.name = device_info["DeviceName"].operator String() + " (ios_deploy, " + ((device_event["Interface"] == "WIFI") ? "network" : "wired") + ")"; nd.wifi = device_event["Interface"] == "WIFI"; + nd.use_ios_deploy = true; nd.simulator = false; ldevices.push_back(nd); } @@ -2153,39 +2200,78 @@ void EditorExportPlatformIOS::_check_for_changes_poll_thread(void *ud) { } } - // Enum simulators + // Enum simulators. if (_check_xcode_install() && (FileAccess::exists("/usr/bin/xcrun") || FileAccess::exists("/bin/xcrun"))) { - String devices; - List<String> args; - args.push_back("simctl"); - args.push_back("list"); - args.push_back("devices"); - args.push_back("-j"); - - int ec = 0; - Error err = OS::get_singleton()->execute("xcrun", args, &devices, &ec, true); - if (err == OK && ec == 0) { - Ref<JSON> json; - json.instantiate(); - err = json->parse(devices); - if (err == OK) { - Dictionary data = json->get_data(); - Dictionary devices = data["devices"]; - for (const Variant *key = devices.next(nullptr); key; key = devices.next(key)) { - Array os_devices = devices[*key]; - for (int i = 0; i < os_devices.size(); i++) { - Dictionary device_info = os_devices[i]; - if (device_info["isAvailable"].operator bool() && device_info["state"] == "Booted") { + { + String devices; + List<String> args; + args.push_back("devicectl"); + args.push_back("list"); + args.push_back("devices"); + args.push_back("-j"); + args.push_back("-"); + args.push_back("-q"); + int ec = 0; + Error err = OS::get_singleton()->execute("xcrun", args, &devices, &ec, true); + if (err == OK && ec == 0) { + Ref<JSON> json; + json.instantiate(); + err = json->parse(devices); + if (err == OK) { + const Dictionary &data = json->get_data(); + const Dictionary &result = data["result"]; + const Array &devices = result["devices"]; + for (int i = 0; i < devices.size(); i++) { + const Dictionary &device_info = devices[i]; + const Dictionary &conn_props = device_info["connectionProperties"]; + const Dictionary &dev_props = device_info["deviceProperties"]; + if (conn_props["pairingState"] == "paired" && dev_props["developerModeStatus"] == "enabled") { Device nd; - nd.id = device_info["udid"]; - nd.name = device_info["name"].operator String() + " (simulator)"; - nd.simulator = true; + nd.id = device_info["identifier"]; + nd.name = dev_props["name"].operator String() + " (devicectl, " + ((conn_props["transportType"] == "localNetwork") ? "network" : "wired") + ")"; + nd.wifi = conn_props["transportType"] == "localNetwork"; + nd.simulator = false; ldevices.push_back(nd); } } } } } + + // Enum simulators. + { + String devices; + List<String> args; + args.push_back("simctl"); + args.push_back("list"); + args.push_back("devices"); + args.push_back("-j"); + + int ec = 0; + Error err = OS::get_singleton()->execute("xcrun", args, &devices, &ec, true); + if (err == OK && ec == 0) { + Ref<JSON> json; + json.instantiate(); + err = json->parse(devices); + if (err == OK) { + const Dictionary &data = json->get_data(); + const Dictionary &devices = data["devices"]; + for (const Variant *key = devices.next(nullptr); key; key = devices.next(key)) { + const Array &os_devices = devices[*key]; + for (int i = 0; i < os_devices.size(); i++) { + const Dictionary &device_info = os_devices[i]; + if (device_info["isAvailable"].operator bool() && device_info["state"] == "Booted") { + Device nd; + nd.id = device_info["udid"]; + nd.name = device_info["name"].operator String() + " (simulator)"; + nd.simulator = true; + ldevices.push_back(nd); + } + } + } + } + } + } } // Update device list. @@ -2346,6 +2432,7 @@ Error EditorExportPlatformIOS::run(const Ref<EditorExportPreset> &p_preset, int List<String> args; args.push_back("simctl"); args.push_back("launch"); + args.push_back("--terminate-running-process"); args.push_back(dev.id); args.push_back(p_preset->get("application/bundle_identifier")); for (const String &E : cmd_args_list) { @@ -2364,8 +2451,8 @@ Error EditorExportPlatformIOS::run(const Ref<EditorExportPreset> &p_preset, int add_message(EXPORT_MESSAGE_ERROR, TTR("Run"), TTR("Running failed, see editor log for details.")); } } - } else { - // Deploy and run on real device. + } else if (dev.use_ios_deploy) { + // Deploy and run on real device (via ios-deploy). if (ep.step("Installing and running on device...", 4)) { CLEANUP_AND_RETURN(ERR_SKIP); } else { @@ -2403,6 +2490,62 @@ Error EditorExportPlatformIOS::run(const Ref<EditorExportPreset> &p_preset, int CLEANUP_AND_RETURN(ERR_UNCONFIGURED); } } + } else { + // Deploy and run on real device. + if (ep.step("Installing to device...", 3)) { + CLEANUP_AND_RETURN(ERR_SKIP); + } else { + List<String> args; + args.push_back("devicectl"); + args.push_back("device"); + args.push_back("install"); + args.push_back("app"); + args.push_back("-d"); + args.push_back(dev.id); + args.push_back(EditorPaths::get_singleton()->get_cache_dir().path_join(id).path_join("export.xcarchive/Products/Applications/export.app")); + + String log; + int ec; + err = OS::get_singleton()->execute("xcrun", args, &log, &ec, true); + if (err != OK) { + add_message(EXPORT_MESSAGE_WARNING, TTR("Run"), TTR("Could not start device executable.")); + CLEANUP_AND_RETURN(err); + } + if (ec != 0) { + print_line("device install:\n" + log); + add_message(EXPORT_MESSAGE_ERROR, TTR("Run"), TTR("Installation failed, see editor log for details.")); + CLEANUP_AND_RETURN(ERR_UNCONFIGURED); + } + } + + if (ep.step("Running on device...", 4)) { + CLEANUP_AND_RETURN(ERR_SKIP); + } else { + List<String> args; + args.push_back("devicectl"); + args.push_back("device"); + args.push_back("process"); + args.push_back("launch"); + args.push_back("--terminate-existing"); + args.push_back("-d"); + args.push_back(dev.id); + args.push_back(p_preset->get("application/bundle_identifier")); + for (const String &E : cmd_args_list) { + args.push_back(E); + } + + String log; + int ec; + err = OS::get_singleton()->execute("xcrun", args, &log, &ec, true); + if (err != OK) { + add_message(EXPORT_MESSAGE_WARNING, TTR("Run"), TTR("Could not start devicectl executable.")); + CLEANUP_AND_RETURN(err); + } + if (ec != 0) { + print_line("devicectl launch:\n" + log); + add_message(EXPORT_MESSAGE_ERROR, TTR("Run"), TTR("Running failed, see editor log for details.")); + } + } } CLEANUP_AND_RETURN(OK); diff --git a/platform/ios/export/export_plugin.h b/platform/ios/export/export_plugin.h index 951017ddae..fd2243bcda 100644 --- a/platform/ios/export/export_plugin.h +++ b/platform/ios/export/export_plugin.h @@ -70,6 +70,7 @@ class EditorExportPlatformIOS : public EditorExportPlatform { String name; bool simulator = false; bool wifi = false; + bool use_ios_deploy = false; }; Vector<Device> devices; @@ -139,7 +140,7 @@ class EditorExportPlatformIOS : public EditorExportPlatform { Error _export_additional_assets(const String &p_out_dir, const Vector<SharedObject> &p_libraries, Vector<IOSExportAsset> &r_exported_assets); Error _export_ios_plugins(const Ref<EditorExportPreset> &p_preset, IOSConfigData &p_config_data, const String &dest_dir, Vector<IOSExportAsset> &r_exported_assets, bool p_debug); - Error _export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags, bool p_simulator, bool p_skip_ipa); + Error _export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags, bool p_simulator, bool p_oneclick); bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const; diff --git a/platform/linuxbsd/export/export_plugin.cpp b/platform/linuxbsd/export/export_plugin.cpp index 9d1e058b76..64efcffae3 100644 --- a/platform/linuxbsd/export/export_plugin.cpp +++ b/platform/linuxbsd/export/export_plugin.cpp @@ -79,6 +79,7 @@ Error EditorExportPlatformLinuxBSD::export_project(const Ref<EditorExportPreset> Ref<DirAccess> tmp_app_dir = DirAccess::create_for_path(tmp_dir_path); if (export_as_zip) { if (tmp_app_dir.is_null()) { + add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Templates"), vformat(TTR("Could not create and open the directory: \"%s\""), tmp_dir_path)); return ERR_CANT_CREATE; } if (DirAccess::exists(tmp_dir_path)) { @@ -93,19 +94,18 @@ Error EditorExportPlatformLinuxBSD::export_project(const Ref<EditorExportPreset> // Export project. Error err = EditorExportPlatformPC::export_project(p_preset, p_debug, path, p_flags); if (err != OK) { + // Message is supplied by the subroutine method. return err; } // Save console wrapper. - if (err == OK) { - int con_scr = p_preset->get("debug/export_console_wrapper"); - if ((con_scr == 1 && p_debug) || (con_scr == 2)) { - String scr_path = path.get_basename() + ".sh"; - err = _export_debug_script(p_preset, pkg_name, path.get_file(), scr_path); - FileAccess::set_unix_permissions(scr_path, 0755); - if (err != OK) { - add_message(EXPORT_MESSAGE_ERROR, TTR("Debug Console Export"), TTR("Could not create console wrapper.")); - } + int con_scr = p_preset->get("debug/export_console_wrapper"); + if ((con_scr == 1 && p_debug) || (con_scr == 2)) { + String scr_path = path.get_basename() + ".sh"; + err = _export_debug_script(p_preset, pkg_name, path.get_file(), scr_path); + FileAccess::set_unix_permissions(scr_path, 0755); + if (err != OK) { + add_message(EXPORT_MESSAGE_ERROR, TTR("Debug Console Export"), TTR("Could not create console wrapper.")); } } diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp index 3bafdfb53d..e1d842422c 100644 --- a/platform/linuxbsd/x11/display_server_x11.cpp +++ b/platform/linuxbsd/x11/display_server_x11.cpp @@ -3875,7 +3875,7 @@ void DisplayServerX11::_xim_preedit_draw_callback(::XIM xim, ::XPointer client_d ds->im_selection = Point2i(); } - OS_Unix::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE); + OS_Unix::get_singleton()->get_main_loop()->call_deferred(SNAME("notification"), MainLoop::NOTIFICATION_OS_IME_UPDATE); } } diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm index 2a6f17cb5d..407a315827 100644 --- a/platform/macos/display_server_macos.mm +++ b/platform/macos/display_server_macos.mm @@ -725,7 +725,8 @@ void DisplayServerMacOS::update_mouse_pos(DisplayServerMacOS::WindowData &p_wd, } void DisplayServerMacOS::pop_last_key_event() { - if (key_event_pos > 0) { + // Does not pop last key event when it is an IME key event. + if (key_event_pos > 0 && key_event_buffer[key_event_pos - 1].raw) { key_event_pos--; } } diff --git a/platform/macos/export/export_plugin.cpp b/platform/macos/export/export_plugin.cpp index 24cb76b4ab..6f9db1427b 100644 --- a/platform/macos/export/export_plugin.cpp +++ b/platform/macos/export/export_plugin.cpp @@ -1266,10 +1266,16 @@ Error EditorExportPlatformMacOS::_export_debug_script(const Ref<EditorExportPres Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); - String src_pkg_name; + const String base_dir = p_path.get_base_dir(); + + if (!DirAccess::exists(base_dir)) { + add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Target folder does not exist or is inaccessible: \"%s\""), base_dir)); + return ERR_FILE_BAD_PATH; + } EditorProgress ep("export", TTR("Exporting for macOS"), 3, true); + String src_pkg_name; if (p_debug) { src_pkg_name = p_preset->get("custom_template/debug"); } else { @@ -1280,16 +1286,11 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p String err; src_pkg_name = find_export_template("macos.zip", &err); if (src_pkg_name.is_empty()) { - add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Templates"), TTR("Export template not found.")); + add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Templates"), TTR("Export template not found.") + "\n" + err); return ERR_FILE_NOT_FOUND; } } - if (!DirAccess::exists(p_path.get_base_dir())) { - add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Templates"), TTR("The given export path doesn't exist.")); - return ERR_FILE_BAD_PATH; - } - Ref<FileAccess> io_fa; zlib_filefunc_def io = zipio_create_io(&io_fa); @@ -2026,9 +2027,9 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p bool EditorExportPlatformMacOS::has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug) const { String err; - // Look for export templates (custom templates). - bool dvalid = false; - bool rvalid = false; + // Look for export templates (official templates first, then custom). + bool dvalid = exists_export_template("macos.zip", &err); + bool rvalid = dvalid; // Both in the same ZIP. if (p_preset->get("custom_template/debug") != "") { dvalid = FileAccess::exists(p_preset->get("custom_template/debug")); @@ -2043,12 +2044,6 @@ bool EditorExportPlatformMacOS::has_valid_export_configuration(const Ref<EditorE } } - // Look for export templates (official templates, check only is custom templates are not set). - if (!dvalid || !rvalid) { - dvalid = exists_export_template("macos.zip", &err); - rvalid = dvalid; // Both in the same ZIP. - } - bool valid = dvalid || rvalid; r_missing_templates = !valid; diff --git a/platform/macos/gl_manager_macos_angle.mm b/platform/macos/gl_manager_macos_angle.mm index ec0ca3e1f3..ac2f20ee7e 100644 --- a/platform/macos/gl_manager_macos_angle.mm +++ b/platform/macos/gl_manager_macos_angle.mm @@ -48,7 +48,7 @@ EGLenum GLManagerANGLE_MacOS::_get_platform_extension_enum() const { Vector<EGLAttrib> GLManagerANGLE_MacOS::_get_platform_display_attributes() const { Vector<EGLAttrib> ret; ret.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE); - ret.push_back(EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE); + ret.push_back(EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE); ret.push_back(EGL_NONE); return ret; diff --git a/platform/web/detect.py b/platform/web/detect.py index b0044a80ff..579eaaff03 100644 --- a/platform/web/detect.py +++ b/platform/web/detect.py @@ -186,9 +186,18 @@ def configure(env: "Environment"): env["LIBPREFIXES"] = ["$LIBPREFIX"] env["LIBSUFFIXES"] = ["$LIBSUFFIX"] + # Get version info for checks below. + cc_version = get_compiler_version(env) + cc_semver = (cc_version["major"], cc_version["minor"], cc_version["patch"]) + env.Prepend(CPPPATH=["#platform/web"]) env.Append(CPPDEFINES=["WEB_ENABLED", "UNIX_ENABLED"]) + if cc_semver >= (3, 1, 25): + env.Append(LINKFLAGS=["-s", "STACK_SIZE=5MB"]) + else: + env.Append(LINKFLAGS=["-s", "TOTAL_STACK=5MB"]) + if env["opengl3"]: env.AppendUnique(CPPDEFINES=["GLES3_ENABLED"]) # This setting just makes WebGL 2 APIs available, it does NOT disable WebGL 1. @@ -203,13 +212,10 @@ def configure(env: "Environment"): env.Append(CPPDEFINES=["PTHREAD_NO_RENAME"]) env.Append(CCFLAGS=["-s", "USE_PTHREADS=1"]) env.Append(LINKFLAGS=["-s", "USE_PTHREADS=1"]) + env.Append(LINKFLAGS=["-s", "DEFAULT_PTHREAD_STACK_SIZE=2MB"]) env.Append(LINKFLAGS=["-s", "PTHREAD_POOL_SIZE=8"]) env.Append(LINKFLAGS=["-s", "WASM_MEM_MAX=2048MB"]) - # Get version info for checks below. - cc_version = get_compiler_version(env) - cc_semver = (cc_version["major"], cc_version["minor"], cc_version["patch"]) - if env["lto"] != "none": # Workaround https://github.com/emscripten-core/emscripten/issues/19781. if cc_semver >= (3, 1, 42) and cc_semver < (3, 1, 46): diff --git a/platform/web/export/export_plugin.cpp b/platform/web/export/export_plugin.cpp index a62ccdc2aa..a70812cf5b 100644 --- a/platform/web/export/export_plugin.cpp +++ b/platform/web/export/export_plugin.cpp @@ -259,6 +259,7 @@ Error EditorExportPlatformWeb::_build_pwa(const Ref<EditorExportPreset> &p_prese _replace_strings(replaces, sw); Error err = _write_or_error(sw.ptr(), sw.size(), dir.path_join(name + ".service.worker.js")); if (err != OK) { + // Message is supplied by the subroutine method. return err; } @@ -291,16 +292,19 @@ Error EditorExportPlatformWeb::_build_pwa(const Ref<EditorExportPreset> &p_prese const String icon144_path = p_preset->get("progressive_web_app/icon_144x144"); err = _add_manifest_icon(p_path, icon144_path, 144, icons_arr); if (err != OK) { + // Message is supplied by the subroutine method. return err; } const String icon180_path = p_preset->get("progressive_web_app/icon_180x180"); err = _add_manifest_icon(p_path, icon180_path, 180, icons_arr); if (err != OK) { + // Message is supplied by the subroutine method. return err; } const String icon512_path = p_preset->get("progressive_web_app/icon_512x512"); err = _add_manifest_icon(p_path, icon512_path, 512, icons_arr); if (err != OK) { + // Message is supplied by the subroutine method. return err; } manifest["icons"] = icons_arr; @@ -308,6 +312,7 @@ Error EditorExportPlatformWeb::_build_pwa(const Ref<EditorExportPreset> &p_prese CharString cs = Variant(manifest).to_json_string().utf8(); err = _write_or_error((const uint8_t *)cs.get_data(), cs.length(), dir.path_join(name + ".manifest.json")); if (err != OK) { + // Message is supplied by the subroutine method. return err; } @@ -439,6 +444,11 @@ Error EditorExportPlatformWeb::export_project(const Ref<EditorExportPreset> &p_p const String base_path = p_path.get_basename(); const String base_name = p_path.get_file().get_basename(); + if (!DirAccess::exists(base_dir)) { + add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Target folder does not exist or is inaccessible: \"%s\""), base_dir)); + return ERR_FILE_BAD_PATH; + } + // Find the correct template String template_path = p_debug ? custom_debug : custom_release; template_path = template_path.strip_edges(); @@ -447,10 +457,6 @@ Error EditorExportPlatformWeb::export_project(const Ref<EditorExportPreset> &p_p template_path = find_export_template(_get_template_name(extensions, p_debug)); } - if (!DirAccess::exists(base_dir)) { - return ERR_FILE_BAD_PATH; - } - if (!template_path.is_empty() && !FileAccess::exists(template_path)) { add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Templates"), vformat(TTR("Template file not found: \"%s\"."), template_path)); return ERR_FILE_NOT_FOUND; @@ -480,6 +486,7 @@ Error EditorExportPlatformWeb::export_project(const Ref<EditorExportPreset> &p_p // Extract templates. error = _extract_template(template_path, base_dir, base_name, pwa); if (error) { + // Message is supplied by the subroutine method. return error; } @@ -510,6 +517,7 @@ Error EditorExportPlatformWeb::export_project(const Ref<EditorExportPreset> &p_p _fix_html(html, p_preset, base_name, p_debug, p_flags, shared_objects, file_sizes); Error err = _write_or_error(html.ptr(), html.size(), p_path); if (err != OK) { + // Message is supplied by the subroutine method. return err; } html.resize(0); @@ -543,6 +551,7 @@ Error EditorExportPlatformWeb::export_project(const Ref<EditorExportPreset> &p_p if (pwa) { err = _build_pwa(p_preset, p_path, shared_objects); if (err != OK) { + // Message is supplied by the subroutine method. return err; } } diff --git a/platform/windows/detect.py b/platform/windows/detect.py index bdacdbb9ba..dbdb13697b 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -481,7 +481,7 @@ def configure_msvc(env, vcvars_msvc_config): env["BUILDERS"]["ProgramOriginal"] = env["BUILDERS"]["Program"] env["BUILDERS"]["Program"] = methods.precious_program - env.Append(LINKFLAGS=["/NATVIS:platform\windows\godot.natvis"]) + env.Append(LINKFLAGS=["/NATVIS:platform\\windows\\godot.natvis"]) env.AppendUnique(LINKFLAGS=["/STACK:" + str(STACK_SIZE)]) diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index e8d81405f0..c801ca96e7 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -2958,9 +2958,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } } break; case WM_MOUSEACTIVATE: { - if (windows[window_id].no_focus) { - return MA_NOACTIVATEANDEAT; // Do not activate, and discard mouse messages. - } else if (windows[window_id].is_popup) { + if (windows[window_id].no_focus || windows[window_id].is_popup) { return MA_NOACTIVATE; // Do not activate, but process mouse messages. } } break; diff --git a/platform/windows/export/export_plugin.cpp b/platform/windows/export/export_plugin.cpp index 4185c36d77..418f38c127 100644 --- a/platform/windows/export/export_plugin.cpp +++ b/platform/windows/export/export_plugin.cpp @@ -226,6 +226,7 @@ Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset> Ref<DirAccess> tmp_app_dir = DirAccess::create_for_path(tmp_dir_path); if (export_as_zip) { if (tmp_app_dir.is_null()) { + add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Templates"), vformat(TTR("Could not create and open the directory: \"%s\""), tmp_dir_path)); return ERR_CANT_CREATE; } if (DirAccess::exists(tmp_dir_path)) { @@ -242,8 +243,14 @@ Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset> if (embedded) { pck_path = pck_path.get_basename() + ".tmp"; } + Error err = EditorExportPlatformPC::export_project(p_preset, p_debug, pck_path, p_flags); - if (p_preset->get("codesign/enable") && err == OK) { + if (err != OK) { + // Message is supplied by the subroutine method. + return err; + } + + if (p_preset->get("codesign/enable")) { _code_sign(p_preset, pck_path); String wrapper_path = p_path.get_basename() + ".console.exe"; if (FileAccess::exists(wrapper_path)) { @@ -251,7 +258,7 @@ Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset> } } - if (embedded && err == OK) { + if (embedded) { Ref<DirAccess> tmp_dir = DirAccess::create_for_path(p_path.get_base_dir()); err = tmp_dir->rename(pck_path, p_path); if (err != OK) { diff --git a/scene/2d/navigation_agent_2d.cpp b/scene/2d/navigation_agent_2d.cpp index 1b3b0bcef0..a45d9f2fa8 100644 --- a/scene/2d/navigation_agent_2d.cpp +++ b/scene/2d/navigation_agent_2d.cpp @@ -294,6 +294,13 @@ NavigationAgent2D::NavigationAgent2D() { NavigationServer2D::get_singleton()->agent_set_time_horizon_obstacles(agent, time_horizon_obstacles); NavigationServer2D::get_singleton()->agent_set_radius(agent, radius); NavigationServer2D::get_singleton()->agent_set_max_speed(agent, max_speed); + NavigationServer2D::get_singleton()->agent_set_avoidance_layers(agent, avoidance_layers); + NavigationServer2D::get_singleton()->agent_set_avoidance_mask(agent, avoidance_mask); + NavigationServer2D::get_singleton()->agent_set_avoidance_priority(agent, avoidance_priority); + NavigationServer2D::get_singleton()->agent_set_avoidance_enabled(agent, avoidance_enabled); + if (avoidance_enabled) { + NavigationServer2D::get_singleton()->agent_set_avoidance_callback(agent, callable_mp(this, &NavigationAgent2D::_avoidance_done)); + } // Preallocate query and result objects to improve performance. navigation_query = Ref<NavigationPathQueryParameters2D>(); @@ -302,11 +309,6 @@ NavigationAgent2D::NavigationAgent2D() { navigation_result = Ref<NavigationPathQueryResult2D>(); navigation_result.instantiate(); - set_avoidance_layers(avoidance_layers); - set_avoidance_mask(avoidance_mask); - set_avoidance_priority(avoidance_priority); - set_avoidance_enabled(avoidance_enabled); - #ifdef DEBUG_ENABLED NavigationServer2D::get_singleton()->connect(SNAME("navigation_debug_changed"), callable_mp(this, &NavigationAgent2D::_navigation_debug_changed)); #endif // DEBUG_ENABLED diff --git a/scene/2d/navigation_link_2d.cpp b/scene/2d/navigation_link_2d.cpp index 95798b6856..04ba550888 100644 --- a/scene/2d/navigation_link_2d.cpp +++ b/scene/2d/navigation_link_2d.cpp @@ -36,6 +36,8 @@ #include "servers/navigation_server_3d.h" void NavigationLink2D::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_rid"), &NavigationLink2D::get_rid); + ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &NavigationLink2D::set_enabled); ClassDB::bind_method(D_METHOD("is_enabled"), &NavigationLink2D::is_enabled); @@ -175,6 +177,10 @@ bool NavigationLink2D::_edit_is_selected_on_click(const Point2 &p_point, double } #endif // TOOLS_ENABLED +RID NavigationLink2D::get_rid() const { + return link; +} + void NavigationLink2D::set_enabled(bool p_enabled) { if (enabled == p_enabled) { return; @@ -343,7 +349,13 @@ PackedStringArray NavigationLink2D::get_configuration_warnings() const { NavigationLink2D::NavigationLink2D() { link = NavigationServer2D::get_singleton()->link_create(); + NavigationServer2D::get_singleton()->link_set_owner_id(link, get_instance_id()); + NavigationServer2D::get_singleton()->link_set_enter_cost(link, enter_cost); + NavigationServer2D::get_singleton()->link_set_travel_cost(link, travel_cost); + NavigationServer2D::get_singleton()->link_set_navigation_layers(link, navigation_layers); + NavigationServer2D::get_singleton()->link_set_bidirectional(link, bidirectional); + NavigationServer2D::get_singleton()->link_set_enabled(link, enabled); set_notify_transform(true); set_hide_clip_children(true); diff --git a/scene/2d/navigation_link_2d.h b/scene/2d/navigation_link_2d.h index 4259740c90..2929691c04 100644 --- a/scene/2d/navigation_link_2d.h +++ b/scene/2d/navigation_link_2d.h @@ -61,6 +61,7 @@ public: virtual Rect2 _edit_get_rect() const override; virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const override; #endif + RID get_rid() const; void set_enabled(bool p_enabled); bool is_enabled() const { return enabled; } diff --git a/scene/2d/navigation_obstacle_2d.cpp b/scene/2d/navigation_obstacle_2d.cpp index d993b8a400..60fb64a8e2 100644 --- a/scene/2d/navigation_obstacle_2d.cpp +++ b/scene/2d/navigation_obstacle_2d.cpp @@ -148,10 +148,10 @@ void NavigationObstacle2D::_notification(int p_what) { NavigationObstacle2D::NavigationObstacle2D() { obstacle = NavigationServer2D::get_singleton()->obstacle_create(); - set_radius(radius); - set_vertices(vertices); - set_avoidance_layers(avoidance_layers); - set_avoidance_enabled(avoidance_enabled); + NavigationServer2D::get_singleton()->obstacle_set_radius(obstacle, radius); + NavigationServer2D::get_singleton()->obstacle_set_vertices(obstacle, vertices); + NavigationServer2D::get_singleton()->obstacle_set_avoidance_layers(obstacle, avoidance_layers); + NavigationServer2D::get_singleton()->obstacle_set_avoidance_enabled(obstacle, avoidance_enabled); } NavigationObstacle2D::~NavigationObstacle2D() { diff --git a/scene/2d/navigation_region_2d.cpp b/scene/2d/navigation_region_2d.cpp index 78733f04e4..8e4b6bfa19 100644 --- a/scene/2d/navigation_region_2d.cpp +++ b/scene/2d/navigation_region_2d.cpp @@ -35,6 +35,10 @@ #include "scene/resources/world_2d.h" #include "servers/navigation_server_2d.h" +RID NavigationRegion2D::get_rid() const { + return region; +} + void NavigationRegion2D::set_enabled(bool p_enabled) { if (enabled == p_enabled) { return; @@ -136,7 +140,7 @@ real_t NavigationRegion2D::get_travel_cost() const { } RID NavigationRegion2D::get_region_rid() const { - return region; + return get_rid(); } #ifdef TOOLS_ENABLED @@ -165,6 +169,7 @@ void NavigationRegion2D::_notification(int p_what) { case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { set_physics_process_internal(false); + _region_update_transform(); } break; case NOTIFICATION_DRAW: { @@ -279,6 +284,8 @@ PackedStringArray NavigationRegion2D::get_configuration_warnings() const { } void NavigationRegion2D::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_rid"), &NavigationRegion2D::get_rid); + ClassDB::bind_method(D_METHOD("set_navigation_polygon", "navigation_polygon"), &NavigationRegion2D::set_navigation_polygon); ClassDB::bind_method(D_METHOD("get_navigation_polygon"), &NavigationRegion2D::get_navigation_polygon); @@ -356,6 +363,9 @@ NavigationRegion2D::NavigationRegion2D() { NavigationServer2D::get_singleton()->region_set_owner_id(region, get_instance_id()); NavigationServer2D::get_singleton()->region_set_enter_cost(region, get_enter_cost()); NavigationServer2D::get_singleton()->region_set_travel_cost(region, get_travel_cost()); + NavigationServer2D::get_singleton()->region_set_navigation_layers(region, navigation_layers); + NavigationServer2D::get_singleton()->region_set_use_edge_connections(region, use_edge_connections); + NavigationServer2D::get_singleton()->region_set_enabled(region, enabled); #ifdef DEBUG_ENABLED NavigationServer2D::get_singleton()->connect(SNAME("map_changed"), callable_mp(this, &NavigationRegion2D::_navigation_map_changed)); diff --git a/scene/2d/navigation_region_2d.h b/scene/2d/navigation_region_2d.h index 36e889877a..3b880dd00d 100644 --- a/scene/2d/navigation_region_2d.h +++ b/scene/2d/navigation_region_2d.h @@ -76,6 +76,7 @@ public: virtual Rect2 _edit_get_rect() const override; virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const override; #endif + RID get_rid() const; void set_enabled(bool p_enabled); bool is_enabled() const; diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 6381526f58..04e362c426 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -447,9 +447,11 @@ void TileMapLayer::_rendering_update() { for (KeyValue<Vector2i, CellData> &kv : tile_map) { CellData &cell_data = kv.value; for (const RID &occluder : cell_data.occluders) { - Transform2D xform(0, tile_map_node->map_to_local(kv.key)); - rs->canvas_light_occluder_attach_to_canvas(occluder, tile_map_node->get_canvas()); - rs->canvas_light_occluder_set_transform(occluder, tile_map_node->get_global_transform() * xform); + if (occluder.is_valid()) { + Transform2D xform(0, tile_map_node->map_to_local(kv.key)); + rs->canvas_light_occluder_attach_to_canvas(occluder, tile_map_node->get_canvas()); + rs->canvas_light_occluder_set_transform(occluder, tile_map_node->get_global_transform() * xform); + } } } } @@ -565,6 +567,15 @@ void TileMapLayer::_rendering_occluders_update_cell(CellData &r_cell_data) { const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); RenderingServer *rs = RenderingServer::get_singleton(); + // Free unused occluders then resize the occluders array. + for (uint32_t i = tile_set->get_occlusion_layers_count(); i < r_cell_data.occluders.size(); i++) { + RID occluder_id = r_cell_data.occluders[i]; + if (occluder_id.is_valid()) { + rs->free(occluder_id); + } + } + r_cell_data.occluders.resize(tile_set->get_occlusion_layers_count()); + TileSetSource *source; if (tile_set->has_source(r_cell_data.cell.source_id)) { source = *tile_set->get_source(r_cell_data.cell.source_id); @@ -580,18 +591,30 @@ void TileMapLayer::_rendering_occluders_update_cell(CellData &r_cell_data) { tile_data = atlas_source->get_tile_data(r_cell_data.cell.get_atlas_coords(), r_cell_data.cell.alternative_tile); } - // Update/create occluders. - for (int i = 0; i < tile_set->get_occlusion_layers_count(); i++) { - Transform2D xform; - xform.set_origin(tile_map_node->map_to_local(r_cell_data.coords)); - if (tile_data->get_occluder(i).is_valid()) { - RID occluder_id = rs->canvas_light_occluder_create(); - rs->canvas_light_occluder_set_enabled(occluder_id, node_visible); - rs->canvas_light_occluder_set_transform(occluder_id, tile_map_node->get_global_transform() * xform); - rs->canvas_light_occluder_set_polygon(occluder_id, tile_map_node->get_transformed_polygon(Ref<Resource>(tile_data->get_occluder(i)), r_cell_data.cell.alternative_tile)->get_rid()); - rs->canvas_light_occluder_attach_to_canvas(occluder_id, tile_map_node->get_canvas()); - rs->canvas_light_occluder_set_light_mask(occluder_id, tile_set->get_occlusion_layer_light_mask(i)); - r_cell_data.occluders.push_back(occluder_id); + // Create, update or clear occluders. + for (uint32_t occlusion_layer_index = 0; occlusion_layer_index < r_cell_data.occluders.size(); occlusion_layer_index++) { + Ref<OccluderPolygon2D> occluder_polygon = tile_data->get_occluder(occlusion_layer_index); + + RID &occluder = r_cell_data.occluders[occlusion_layer_index]; + + if (occluder_polygon.is_valid()) { + // Create or update occluder. + Transform2D xform; + xform.set_origin(tile_map_node->map_to_local(r_cell_data.coords)); + if (!occluder.is_valid()) { + occluder = rs->canvas_light_occluder_create(); + } + rs->canvas_light_occluder_set_enabled(occluder, node_visible); + rs->canvas_light_occluder_set_transform(occluder, tile_map_node->get_global_transform() * xform); + rs->canvas_light_occluder_set_polygon(occluder, tile_map_node->get_transformed_polygon(Ref<Resource>(tile_data->get_occluder(occlusion_layer_index)), r_cell_data.cell.alternative_tile)->get_rid()); + rs->canvas_light_occluder_attach_to_canvas(occluder, tile_map_node->get_canvas()); + rs->canvas_light_occluder_set_light_mask(occluder, tile_set->get_occlusion_layer_light_mask(occlusion_layer_index)); + } else { + // Clear occluder. + if (occluder.is_valid()) { + rs->free(occluder); + occluder = RID(); + } } } @@ -778,7 +801,7 @@ void TileMapLayer::_physics_update_cell(CellData &r_cell_data) { } // Free unused bodies then resize the bodies array. - for (unsigned int i = tile_set->get_physics_layers_count(); i < r_cell_data.bodies.size(); i++) { + for (uint32_t i = tile_set->get_physics_layers_count(); i < r_cell_data.bodies.size(); i++) { RID body = r_cell_data.bodies[i]; if (body.is_valid()) { bodies_coords.erase(body); @@ -787,7 +810,7 @@ void TileMapLayer::_physics_update_cell(CellData &r_cell_data) { } r_cell_data.bodies.resize(tile_set->get_physics_layers_count()); - for (int tile_set_physics_layer = 0; tile_set_physics_layer < tile_set->get_physics_layers_count(); tile_set_physics_layer++) { + for (uint32_t tile_set_physics_layer = 0; tile_set_physics_layer < (uint32_t)tile_set->get_physics_layers_count(); tile_set_physics_layer++) { Ref<PhysicsMaterial> physics_material = tile_set->get_physics_layer_physics_material(tile_set_physics_layer); uint32_t physics_layer = tile_set->get_physics_layer_collision_layer(tile_set_physics_layer); uint32_t physics_mask = tile_set->get_physics_layer_collision_mask(tile_set_physics_layer); @@ -998,7 +1021,7 @@ void TileMapLayer::_navigation_update() { void TileMapLayer::_navigation_clear_cell(CellData &r_cell_data) { NavigationServer2D *ns = NavigationServer2D::get_singleton(); // Clear navigation shapes. - for (unsigned int i = 0; i < r_cell_data.navigation_regions.size(); i++) { + for (uint32_t i = 0; i < r_cell_data.navigation_regions.size(); i++) { const RID ®ion = r_cell_data.navigation_regions[i]; if (region.is_valid()) { ns->region_set_map(region, RID()); @@ -1031,7 +1054,7 @@ void TileMapLayer::_navigation_update_cell(CellData &r_cell_data) { } // Free unused regions then resize the regions array. - for (unsigned int i = tile_set->get_navigation_layers_count(); i < r_cell_data.navigation_regions.size(); i++) { + for (uint32_t i = tile_set->get_navigation_layers_count(); i < r_cell_data.navigation_regions.size(); i++) { RID ®ion = r_cell_data.navigation_regions[i]; if (region.is_valid()) { ns->region_set_map(region, RID()); @@ -1042,7 +1065,7 @@ void TileMapLayer::_navigation_update_cell(CellData &r_cell_data) { r_cell_data.navigation_regions.resize(tile_set->get_navigation_layers_count()); // Create, update or clear regions. - for (unsigned int navigation_layer_index = 0; navigation_layer_index < r_cell_data.navigation_regions.size(); navigation_layer_index++) { + for (uint32_t navigation_layer_index = 0; navigation_layer_index < r_cell_data.navigation_regions.size(); navigation_layer_index++) { Ref<NavigationPolygon> navigation_polygon; navigation_polygon = tile_data->get_navigation_polygon(navigation_layer_index); navigation_polygon = tile_map_node->get_transformed_polygon(Ref<Resource>(navigation_polygon), c.alternative_tile); @@ -1191,7 +1214,7 @@ void TileMapLayer::_scenes_update() { const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); // Check if we should cleanup everything. - bool forced_cleanup = in_destructor || !enabled || !tile_map_node->is_inside_tree() || !tile_set.is_valid() || !tile_map_node->is_visible_in_tree(); + bool forced_cleanup = in_destructor || !enabled || !tile_map_node->is_inside_tree() || !tile_set.is_valid(); if (forced_cleanup) { // Clean everything. @@ -3250,7 +3273,7 @@ void TileMap::add_layer(int p_to_pos) { new_layer.instantiate(); new_layer->set_tile_map(this); layers.insert(p_to_pos, new_layer); - for (unsigned int i = 0; i < layers.size(); i++) { + for (uint32_t i = 0; i < layers.size(); i++) { layers[i]->set_layer_index_in_tile_map_node(i); } queue_internal_update(); @@ -3269,7 +3292,7 @@ void TileMap::move_layer(int p_layer, int p_to_pos) { Ref<TileMapLayer> layer = layers[p_layer]; layers.insert(p_to_pos, layer); layers.remove_at(p_to_pos < p_layer ? p_layer + 1 : p_layer); - for (unsigned int i = 0; i < layers.size(); i++) { + for (uint32_t i = 0; i < layers.size(); i++) { layers[i]->set_layer_index_in_tile_map_node(i); } queue_internal_update(); @@ -3289,7 +3312,7 @@ void TileMap::remove_layer(int p_layer) { // Clear before removing the layer. layers.remove_at(p_layer); - for (unsigned int i = 0; i < layers.size(); i++) { + for (uint32_t i = 0; i < layers.size(); i++) { layers[i]->set_layer_index_in_tile_map_node(i); } queue_internal_update(); @@ -3526,7 +3549,7 @@ Vector2i TileMap::get_coords_for_body_rid(RID p_physics_body) { } int TileMap::get_layer_for_body_rid(RID p_physics_body) { - for (unsigned int i = 0; i < layers.size(); i++) { + for (uint32_t i = 0; i < layers.size(); i++) { if (layers[i]->has_body_rid(p_physics_body)) { return i; } @@ -3576,7 +3599,7 @@ Rect2 TileMap::_edit_get_rect() const { bool changed = false; Rect2 rect = layers[0]->get_rect(changed); any_changed |= changed; - for (unsigned int i = 1; i < layers.size(); i++) { + for (uint32_t i = 1; i < layers.size(); i++) { rect = rect.merge(layers[i]->get_rect(changed)); any_changed |= changed; } @@ -3755,7 +3778,7 @@ void TileMap::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(m_type, property_name, PROPERTY_HINT_NONE, m_hint, (get(property_name) == property_get_revert(property_name)) ? PROPERTY_USAGE_EDITOR : PROPERTY_USAGE_DEFAULT)); \ } - for (unsigned int i = 0; i < layers.size(); i++) { + for (uint32_t i = 0; i < layers.size(); i++) { MAKE_LAYER_PROPERTY(Variant::STRING, "name", ""); MAKE_LAYER_PROPERTY(Variant::BOOL, "enabled", ""); MAKE_LAYER_PROPERTY(Variant::COLOR, "modulate", ""); diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h index 1136e4190d..9a847a7ebb 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -104,7 +104,7 @@ struct CellData { // Rendering. Ref<RenderingQuadrant> rendering_quadrant; SelfList<CellData> rendering_quadrant_list_element; - List<RID> occluders; + LocalVector<RID> occluders; // Physics. LocalVector<RID> bodies; diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp index 4ba039becd..76933cd956 100644 --- a/scene/3d/lightmap_gi.cpp +++ b/scene/3d/lightmap_gi.cpp @@ -1523,7 +1523,7 @@ PackedStringArray LightmapGI::get_configuration_warnings() const { PackedStringArray warnings = Node::get_configuration_warnings(); if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") { - warnings.push_back(RTR("LightmapGI nodes are not supported when using the GL Compatibility backend yet. Support will be added in a future release.")); + warnings.push_back(RTR("Lightmap cannot be baked when using the GL Compatibility backend yet. Support will be added in a future release.")); return warnings; } diff --git a/scene/3d/navigation_agent_3d.cpp b/scene/3d/navigation_agent_3d.cpp index b311495a7f..7a4d47438b 100644 --- a/scene/3d/navigation_agent_3d.cpp +++ b/scene/3d/navigation_agent_3d.cpp @@ -327,6 +327,14 @@ NavigationAgent3D::NavigationAgent3D() { NavigationServer3D::get_singleton()->agent_set_radius(agent, radius); NavigationServer3D::get_singleton()->agent_set_height(agent, height); NavigationServer3D::get_singleton()->agent_set_max_speed(agent, max_speed); + NavigationServer3D::get_singleton()->agent_set_avoidance_layers(agent, avoidance_layers); + NavigationServer3D::get_singleton()->agent_set_avoidance_mask(agent, avoidance_mask); + NavigationServer3D::get_singleton()->agent_set_avoidance_priority(agent, avoidance_priority); + NavigationServer3D::get_singleton()->agent_set_use_3d_avoidance(agent, use_3d_avoidance); + NavigationServer3D::get_singleton()->agent_set_avoidance_enabled(agent, avoidance_enabled); + if (avoidance_enabled) { + NavigationServer3D::get_singleton()->agent_set_avoidance_callback(agent, callable_mp(this, &NavigationAgent3D::_avoidance_done)); + } // Preallocate query and result objects to improve performance. navigation_query = Ref<NavigationPathQueryParameters3D>(); @@ -335,12 +343,6 @@ NavigationAgent3D::NavigationAgent3D() { navigation_result = Ref<NavigationPathQueryResult3D>(); navigation_result.instantiate(); - set_avoidance_layers(avoidance_layers); - set_avoidance_mask(avoidance_mask); - set_avoidance_priority(avoidance_priority); - set_use_3d_avoidance(use_3d_avoidance); - set_avoidance_enabled(avoidance_enabled); - #ifdef DEBUG_ENABLED NavigationServer3D::get_singleton()->connect(SNAME("navigation_debug_changed"), callable_mp(this, &NavigationAgent3D::_navigation_debug_changed)); #endif // DEBUG_ENABLED diff --git a/scene/3d/navigation_link_3d.cpp b/scene/3d/navigation_link_3d.cpp index 70416ca93b..dc776ebea2 100644 --- a/scene/3d/navigation_link_3d.cpp +++ b/scene/3d/navigation_link_3d.cpp @@ -147,6 +147,8 @@ void NavigationLink3D::_update_debug_mesh() { #endif // DEBUG_ENABLED void NavigationLink3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_rid"), &NavigationLink3D::get_rid); + ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &NavigationLink3D::set_enabled); ClassDB::bind_method(D_METHOD("is_enabled"), &NavigationLink3D::is_enabled); @@ -263,7 +265,13 @@ void NavigationLink3D::_notification(int p_what) { NavigationLink3D::NavigationLink3D() { link = NavigationServer3D::get_singleton()->link_create(); + NavigationServer3D::get_singleton()->link_set_owner_id(link, get_instance_id()); + NavigationServer3D::get_singleton()->link_set_enter_cost(link, enter_cost); + NavigationServer3D::get_singleton()->link_set_travel_cost(link, travel_cost); + NavigationServer3D::get_singleton()->link_set_navigation_layers(link, navigation_layers); + NavigationServer3D::get_singleton()->link_set_bidirectional(link, bidirectional); + NavigationServer3D::get_singleton()->link_set_enabled(link, enabled); set_notify_transform(true); } @@ -284,6 +292,10 @@ NavigationLink3D::~NavigationLink3D() { #endif // DEBUG_ENABLED } +RID NavigationLink3D::get_rid() const { + return link; +} + void NavigationLink3D::set_enabled(bool p_enabled) { if (enabled == p_enabled) { return; diff --git a/scene/3d/navigation_link_3d.h b/scene/3d/navigation_link_3d.h index ec92fb9dd9..1867082811 100644 --- a/scene/3d/navigation_link_3d.h +++ b/scene/3d/navigation_link_3d.h @@ -67,6 +67,8 @@ public: NavigationLink3D(); ~NavigationLink3D(); + RID get_rid() const; + void set_enabled(bool p_enabled); bool is_enabled() const { return enabled; } diff --git a/scene/3d/navigation_obstacle_3d.cpp b/scene/3d/navigation_obstacle_3d.cpp index 95881b1d5a..98cd5efef2 100644 --- a/scene/3d/navigation_obstacle_3d.cpp +++ b/scene/3d/navigation_obstacle_3d.cpp @@ -167,13 +167,11 @@ NavigationObstacle3D::NavigationObstacle3D() { obstacle = NavigationServer3D::get_singleton()->obstacle_create(); NavigationServer3D::get_singleton()->obstacle_set_height(obstacle, height); - - set_radius(radius); - set_height(height); - set_vertices(vertices); - set_avoidance_layers(avoidance_layers); - set_avoidance_enabled(avoidance_enabled); - set_use_3d_avoidance(use_3d_avoidance); + NavigationServer3D::get_singleton()->obstacle_set_radius(obstacle, radius); + NavigationServer3D::get_singleton()->obstacle_set_vertices(obstacle, vertices); + NavigationServer3D::get_singleton()->obstacle_set_avoidance_layers(obstacle, avoidance_layers); + NavigationServer3D::get_singleton()->obstacle_set_use_3d_avoidance(obstacle, use_3d_avoidance); + NavigationServer3D::get_singleton()->obstacle_set_avoidance_enabled(obstacle, avoidance_enabled); #ifdef DEBUG_ENABLED NavigationServer3D::get_singleton()->connect("avoidance_debug_changed", callable_mp(this, &NavigationObstacle3D::_update_fake_agent_radius_debug)); diff --git a/scene/3d/navigation_region_3d.cpp b/scene/3d/navigation_region_3d.cpp index b376a4945e..94c0a2279a 100644 --- a/scene/3d/navigation_region_3d.cpp +++ b/scene/3d/navigation_region_3d.cpp @@ -33,6 +33,10 @@ #include "scene/resources/navigation_mesh_source_geometry_data_3d.h" #include "servers/navigation_server_3d.h" +RID NavigationRegion3D::get_rid() const { + return region; +} + void NavigationRegion3D::set_enabled(bool p_enabled) { if (enabled == p_enabled) { return; @@ -154,7 +158,7 @@ real_t NavigationRegion3D::get_travel_cost() const { } RID NavigationRegion3D::get_region_rid() const { - return region; + return get_rid(); } void NavigationRegion3D::_notification(int p_what) { @@ -275,6 +279,8 @@ PackedStringArray NavigationRegion3D::get_configuration_warnings() const { } void NavigationRegion3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_rid"), &NavigationRegion3D::get_rid); + ClassDB::bind_method(D_METHOD("set_navigation_mesh", "navigation_mesh"), &NavigationRegion3D::set_navigation_mesh); ClassDB::bind_method(D_METHOD("get_navigation_mesh"), &NavigationRegion3D::get_navigation_mesh); @@ -410,6 +416,9 @@ NavigationRegion3D::NavigationRegion3D() { NavigationServer3D::get_singleton()->region_set_owner_id(region, get_instance_id()); NavigationServer3D::get_singleton()->region_set_enter_cost(region, get_enter_cost()); NavigationServer3D::get_singleton()->region_set_travel_cost(region, get_travel_cost()); + NavigationServer3D::get_singleton()->region_set_navigation_layers(region, navigation_layers); + NavigationServer3D::get_singleton()->region_set_use_edge_connections(region, use_edge_connections); + NavigationServer3D::get_singleton()->region_set_enabled(region, enabled); #ifdef DEBUG_ENABLED NavigationServer3D::get_singleton()->connect(SNAME("map_changed"), callable_mp(this, &NavigationRegion3D::_navigation_map_changed)); diff --git a/scene/3d/navigation_region_3d.h b/scene/3d/navigation_region_3d.h index 02fe5524b2..fe9ee178ff 100644 --- a/scene/3d/navigation_region_3d.h +++ b/scene/3d/navigation_region_3d.h @@ -73,6 +73,8 @@ protected: #endif // DISABLE_DEPRECATED public: + RID get_rid() const; + void set_enabled(bool p_enabled); bool is_enabled() const; diff --git a/scene/3d/path_3d.cpp b/scene/3d/path_3d.cpp index 6aea063096..e38375d339 100644 --- a/scene/3d/path_3d.cpp +++ b/scene/3d/path_3d.cpp @@ -93,24 +93,63 @@ void Path3D::_update_debug_mesh() { return; } - Vector<Vector3> vertex_array; + real_t interval = 0.1; + const real_t length = curve->get_baked_length(); - for (int i = 1; i < curve->get_point_count(); i++) { - Vector3 line_end = curve->get_point_position(i); - Vector3 line_start = curve->get_point_position(i - 1); - vertex_array.push_back(line_start); - vertex_array.push_back(line_end); + if (length <= CMP_EPSILON) { + RS::get_singleton()->instance_set_visible(debug_instance, false); + return; } - Array mesh_array; - mesh_array.resize(Mesh::ARRAY_MAX); - mesh_array[Mesh::ARRAY_VERTEX] = vertex_array; + const int sample_count = int(length / interval) + 2; + interval = length / (sample_count - 1); + + Vector<Vector3> ribbon; + ribbon.resize(sample_count); + Vector3 *ribbon_ptr = ribbon.ptrw(); + + Vector<Vector3> bones; + bones.resize(sample_count * 4); + Vector3 *bones_ptr = bones.ptrw(); + + for (int i = 0; i < sample_count; i++) { + const Transform3D r = curve->sample_baked_with_rotation(i * interval, true, true); + + const Vector3 p1 = r.origin; + const Vector3 side = r.basis.get_column(0); + const Vector3 up = r.basis.get_column(1); + const Vector3 forward = r.basis.get_column(2); + + // Path3D as a ribbon. + ribbon_ptr[i] = p1; + + // Fish Bone. + const Vector3 p_left = p1 + (side + forward - up * 0.3) * 0.06; + const Vector3 p_right = p1 + (-side + forward - up * 0.3) * 0.06; + + const int bone_idx = i * 4; + + bones_ptr[bone_idx] = p1; + bones_ptr[bone_idx + 1] = p_left; + bones_ptr[bone_idx + 2] = p1; + bones_ptr[bone_idx + 3] = p_right; + } + + Array ribbon_array; + ribbon_array.resize(Mesh::ARRAY_MAX); + ribbon_array[Mesh::ARRAY_VERTEX] = ribbon; + + Array bone_array; + bone_array.resize(Mesh::ARRAY_MAX); + bone_array[Mesh::ARRAY_VERTEX] = bones; debug_mesh->clear_surfaces(); - debug_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, mesh_array); + debug_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINE_STRIP, ribbon_array); + debug_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, bone_array); RS::get_singleton()->instance_set_base(debug_instance, debug_mesh->get_rid()); RS::get_singleton()->mesh_surface_set_material(debug_mesh->get_rid(), 0, st->get_debug_paths_material()->get_rid()); + RS::get_singleton()->mesh_surface_set_material(debug_mesh->get_rid(), 1, st->get_debug_paths_material()->get_rid()); if (is_inside_tree()) { RS::get_singleton()->instance_set_scenario(debug_instance, get_world_3d()->get_scenario()); RS::get_singleton()->instance_set_transform(debug_instance, get_global_transform()); diff --git a/scene/animation/animation_mixer.cpp b/scene/animation/animation_mixer.cpp index bba3dc6d7d..c6d69cf622 100644 --- a/scene/animation/animation_mixer.cpp +++ b/scene/animation/animation_mixer.cpp @@ -1985,6 +1985,7 @@ void AnimationMixer::reset() { } void AnimationMixer::restore(const Ref<AnimatedValuesBackup> &p_backup) { + ERR_FAIL_COND(p_backup.is_null()); track_cache = p_backup->get_data(); _blend_apply(); track_cache = HashMap<NodePath, AnimationMixer::TrackCache *>(); diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index 6e7aec379b..02b74c9188 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -233,7 +233,7 @@ void AnimationPlayer::_process_playback_data(PlaybackData &cd, double p_delta, f pi.delta = delta; pi.seeked = p_seeked; } - pi.is_external_seeking = false; + pi.is_external_seeking = true; // AnimationPlayer doesn't have internal seeking. pi.looped_flag = looped_flag; pi.weight = p_blend; make_animation_instance(cd.from->name, pi); diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp index d6a6af3f08..8193bbf3f1 100644 --- a/scene/animation/tween.cpp +++ b/scene/animation/tween.cpp @@ -549,9 +549,12 @@ void PropertyTweener::start() { return; } - if (do_continue && Math::is_zero_approx(delay)) { - initial_val = target_instance->get_indexed(property); - do_continue = false; + if (do_continue) { + if (Math::is_zero_approx(delay)) { + initial_val = target_instance->get_indexed(property); + } else { + do_continue_delayed = true; + } } if (relative) { @@ -576,10 +579,10 @@ bool PropertyTweener::step(double &r_delta) { if (elapsed_time < delay) { r_delta = 0; return true; - } else if (do_continue && !Math::is_zero_approx(delay)) { + } else if (do_continue_delayed && !Math::is_zero_approx(delay)) { initial_val = target_instance->get_indexed(property); delta_val = Animation::subtract_variant(final_val, initial_val); - do_continue = false; + do_continue_delayed = false; } double time = MIN(elapsed_time - delay, duration); diff --git a/scene/animation/tween.h b/scene/animation/tween.h index 10c7a272ef..053b4fac46 100644 --- a/scene/animation/tween.h +++ b/scene/animation/tween.h @@ -225,6 +225,7 @@ private: double delay = 0; bool do_continue = true; + bool do_continue_delayed = false; bool relative = false; }; diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index dc96f8a594..41f4de5b3b 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -1771,7 +1771,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; @@ -3121,6 +3121,8 @@ void RichTextLabel::_add_item(Item *p_item, bool p_enter, bool p_ensure_newline) current_char_ofs += t->text.length(); } else if (p_item->type == ITEM_IMAGE) { current_char_ofs++; + } else if (p_item->type == ITEM_NEWLINE) { + current_char_ofs++; } if (p_enter) { diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp index aa9400847f..0f461f4865 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -381,22 +381,29 @@ void TabContainer::move_tab_from_tab_container(TabContainer *p_from, int p_from_ // Get the tab properties before they get erased by the child removal. String tab_title = p_from->get_tab_title(p_from_index); Ref<Texture2D> tab_icon = p_from->get_tab_icon(p_from_index); + Ref<Texture2D> tab_button_icon = p_from->get_tab_button_icon(p_from_index); bool tab_disabled = p_from->is_tab_disabled(p_from_index); + bool tab_hidden = p_from->is_tab_hidden(p_from_index); Variant tab_metadata = p_from->get_tab_metadata(p_from_index); + int tab_icon_max_width = p_from->get_tab_bar()->get_tab_icon_max_width(p_from_index); Control *moving_tabc = p_from->get_tab_control(p_from_index); p_from->remove_child(moving_tabc); add_child(moving_tabc, true); - set_tab_title(get_tab_count() - 1, tab_title); - set_tab_icon(get_tab_count() - 1, tab_icon); - set_tab_disabled(get_tab_count() - 1, tab_disabled); - set_tab_metadata(get_tab_count() - 1, tab_metadata); - if (p_to_index < 0 || p_to_index > get_tab_count() - 1) { p_to_index = get_tab_count() - 1; } move_child(moving_tabc, get_tab_control(p_to_index)->get_index(false)); + + set_tab_title(p_to_index, tab_title); + set_tab_icon(p_to_index, tab_icon); + set_tab_button_icon(p_to_index, tab_button_icon); + set_tab_disabled(p_to_index, tab_disabled); + set_tab_hidden(p_to_index, tab_hidden); + set_tab_metadata(p_to_index, tab_metadata); + get_tab_bar()->set_tab_icon_max_width(p_to_index, tab_icon_max_width); + if (!is_tab_disabled(p_to_index)) { set_current_tab(p_to_index); } diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 86e726d9da..5817f70343 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -770,7 +770,14 @@ void TextEdit::_notification(int p_what) { Dictionary color_map = _get_line_syntax_highlighting(minimap_line); Color line_background_color = text.get_line_background_color(minimap_line); - line_background_color.a *= 0.6; + + if (line_background_color != theme_cache.background_color) { + // Make non-default background colors more visible, such as error markers. + line_background_color.a = 1.0; + } else { + line_background_color.a *= 0.6; + } + Color current_color = theme_cache.font_color; if (!editable) { current_color = theme_cache.font_readonly_color; diff --git a/scene/gui/video_stream_player.cpp b/scene/gui/video_stream_player.cpp index ac09844128..41a210e180 100644 --- a/scene/gui/video_stream_player.cpp +++ b/scene/gui/video_stream_player.cpp @@ -237,6 +237,12 @@ bool VideoStreamPlayer::has_loop() 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; @@ -248,6 +254,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(); diff --git a/scene/gui/view_panner.cpp b/scene/gui/view_panner.cpp index fc03f2d887..c61fa1d9b8 100644 --- a/scene/gui/view_panner.cpp +++ b/scene/gui/view_panner.cpp @@ -125,6 +125,17 @@ bool ViewPanner::gui_input(const Ref<InputEvent> &p_event, Rect2 p_canvas_rect) Ref<InputEventPanGesture> pan_gesture = p_event; if (pan_gesture.is_valid()) { + if (pan_gesture->is_ctrl_pressed()) { + // Zoom gesture. + float pan_zoom_factor = 1.02f; + float zoom_direction = pan_gesture->get_delta().x - pan_gesture->get_delta().y; + if (zoom_direction == 0.f) { + return true; + } + float zoom = zoom_direction < 0 ? 1.0 / pan_zoom_factor : pan_zoom_factor; + zoom_callback.call(zoom, pan_gesture->get_position(), p_event); + return true; + } pan_callback.call(-pan_gesture->get_delta() * scroll_speed, p_event); } diff --git a/scene/main/node.cpp b/scene/main/node.cpp index e730f47607..820cb7571f 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -202,6 +202,11 @@ void Node::_notification(int p_notification) { _clean_up_owner(); } + while (!data.owned.is_empty()) { + Node *n = data.owned.back()->get(); + n->_clean_up_owner(); // This will change data.owned. So it's impossible to loop over the list in the usual manner. + } + if (data.parent) { data.parent->remove_child(this); } @@ -1415,6 +1420,14 @@ void Node::add_child(Node *p_child, bool p_force_readable_name, InternalMode p_i ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, `add_child()` failed. Consider using `add_child.call_deferred(child)` instead."); _validate_child_name(p_child, p_force_readable_name); + +#ifdef DEBUG_ENABLED + if (p_child->data.owner && !p_child->data.owner->is_ancestor_of(p_child)) { + // Owner of p_child should be ancestor of p_child. + WARN_PRINT(vformat("Adding '%s' as child to '%s' will make owner '%s' inconsistent. Consider unsetting the owner beforehand.", p_child->get_name(), get_name(), p_child->data.owner->get_name())); + } +#endif // DEBUG_ENABLED + _add_child_nocheck(p_child, p_child->data.name, p_internal); } @@ -2515,44 +2528,6 @@ Node *Node::_duplicate(int p_flags, HashMap<const Node *, Node *> *r_duplimap) c } } - for (List<const Node *>::Element *N = node_tree.front(); N; N = N->next()) { - Node *current_node = node->get_node(get_path_to(N->get())); - ERR_CONTINUE(!current_node); - - if (p_flags & DUPLICATE_SCRIPTS) { - bool is_valid = false; - Variant scr = N->get()->get(script_property_name, &is_valid); - if (is_valid) { - current_node->set(script_property_name, scr); - } - } - - List<PropertyInfo> plist; - N->get()->get_property_list(&plist); - - for (const PropertyInfo &E : plist) { - if (!(E.usage & PROPERTY_USAGE_STORAGE)) { - continue; - } - String name = E.name; - if (name == script_property_name) { - continue; - } - - Variant value = N->get()->get(name).duplicate(true); - - if (E.usage & PROPERTY_USAGE_ALWAYS_DUPLICATE) { - Resource *res = Object::cast_to<Resource>(value); - if (res) { // Duplicate only if it's a resource - current_node->set(name, res->duplicate()); - } - - } else { - current_node->set(name, value); - } - } - } - if (get_name() != String()) { node->set_name(get_name()); } @@ -2618,6 +2593,62 @@ Node *Node::_duplicate(int p_flags, HashMap<const Node *, Node *> *r_duplimap) c } } + for (List<const Node *>::Element *N = node_tree.front(); N; N = N->next()) { + Node *current_node = node->get_node(get_path_to(N->get())); + ERR_CONTINUE(!current_node); + + if (p_flags & DUPLICATE_SCRIPTS) { + bool is_valid = false; + Variant scr = N->get()->get(script_property_name, &is_valid); + if (is_valid) { + current_node->set(script_property_name, scr); + } + } + + List<PropertyInfo> plist; + N->get()->get_property_list(&plist); + + for (const PropertyInfo &E : plist) { + if (!(E.usage & PROPERTY_USAGE_STORAGE)) { + continue; + } + String name = E.name; + if (name == script_property_name) { + continue; + } + + Variant value = N->get()->get(name).duplicate(true); + + if (E.usage & PROPERTY_USAGE_ALWAYS_DUPLICATE) { + Resource *res = Object::cast_to<Resource>(value); + if (res) { // Duplicate only if it's a resource + current_node->set(name, res->duplicate()); + } + + } else { + // If property points to a node which is owned by a node we are duplicating, update its path. + if (value.get_type() == Variant::OBJECT) { + Node *property_node = Object::cast_to<Node>(value); + if (property_node && is_ancestor_of(property_node)) { + value = current_node->get_node_or_null(get_path_to(property_node)); + } + } else if (value.get_type() == Variant::ARRAY) { + Array arr = value; + if (arr.get_typed_builtin() == Variant::OBJECT) { + for (int i = 0; i < arr.size(); i++) { + Node *property_node = Object::cast_to<Node>(arr[i]); + if (property_node && is_ancestor_of(property_node)) { + arr[i] = current_node->get_node_or_null(get_path_to(property_node)); + } + } + value = arr; + } + } + current_node->set(name, value); + } + } + } + return node; } diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index e9ea09a3b1..8cb7614dfd 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -2451,6 +2451,14 @@ void Viewport::_gui_update_mouse_over() { return; } + if (gui.sending_mouse_enter_exit_notifications) { + // If notifications are already being sent, delay call to next frame. + if (get_tree() && !get_tree()->is_connected(SNAME("process_frame"), callable_mp(this, &Viewport::_gui_update_mouse_over))) { + get_tree()->connect(SNAME("process_frame"), callable_mp(this, &Viewport::_gui_update_mouse_over), CONNECT_ONE_SHOT); + } + return; + } + // Rebuild the mouse over hierarchy. LocalVector<Control *> new_mouse_over_hierarchy; LocalVector<Control *> needs_enter; @@ -2507,6 +2515,8 @@ void Viewport::_gui_update_mouse_over() { return; } + gui.sending_mouse_enter_exit_notifications = true; + // Send Mouse Exit Self notification. if (gui.mouse_over && !needs_exit.is_empty() && needs_exit[0] == (int)gui.mouse_over_hierarchy.size() - 1) { gui.mouse_over->notification(Control::NOTIFICATION_MOUSE_EXIT_SELF); @@ -2528,6 +2538,8 @@ void Viewport::_gui_update_mouse_over() { for (int i = needs_enter.size() - 1; i >= 0; i--) { needs_enter[i]->notification(Control::NOTIFICATION_MOUSE_ENTER); } + + gui.sending_mouse_enter_exit_notifications = false; } Window *Viewport::get_base_window() const { @@ -3200,10 +3212,12 @@ void Viewport::_update_mouse_over(Vector2 p_pos) { gui.mouse_over = over; gui.mouse_over_hierarchy.reserve(gui.mouse_over_hierarchy.size() + over_ancestors.size()); + gui.sending_mouse_enter_exit_notifications = true; + // Send Mouse Enter notifications to parents first. for (int i = over_ancestors.size() - 1; i >= 0; i--) { - over_ancestors[i]->notification(Control::NOTIFICATION_MOUSE_ENTER); gui.mouse_over_hierarchy.push_back(over_ancestors[i]); + over_ancestors[i]->notification(Control::NOTIFICATION_MOUSE_ENTER); } // Send Mouse Enter Self notification. @@ -3211,6 +3225,8 @@ void Viewport::_update_mouse_over(Vector2 p_pos) { gui.mouse_over->notification(Control::NOTIFICATION_MOUSE_ENTER_SELF); } + gui.sending_mouse_enter_exit_notifications = false; + notify_embedded_viewports = true; } } @@ -3252,6 +3268,12 @@ void Viewport::_mouse_leave_viewport() { } void Viewport::_drop_mouse_over(Control *p_until_control) { + if (gui.sending_mouse_enter_exit_notifications) { + // If notifications are already being sent, defer call. + callable_mp(this, &Viewport::_drop_mouse_over).call_deferred(p_until_control); + return; + } + _gui_cancel_tooltip(); SubViewportContainer *c = Object::cast_to<SubViewportContainer>(gui.mouse_over); if (c) { @@ -3263,6 +3285,8 @@ void Viewport::_drop_mouse_over(Control *p_until_control) { v->_mouse_leave_viewport(); } } + + gui.sending_mouse_enter_exit_notifications = true; if (gui.mouse_over && gui.mouse_over->is_inside_tree()) { gui.mouse_over->notification(Control::NOTIFICATION_MOUSE_EXIT_SELF); } @@ -3276,6 +3300,7 @@ void Viewport::_drop_mouse_over(Control *p_until_control) { } } gui.mouse_over_hierarchy.resize(notification_until); + gui.sending_mouse_enter_exit_notifications = false; } void Viewport::push_input(const Ref<InputEvent> &p_event, bool p_local_coords) { diff --git a/scene/main/viewport.h b/scene/main/viewport.h index 82a9bfc438..6efa98ece8 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -362,6 +362,7 @@ private: Control *key_focus = nullptr; Control *mouse_over = nullptr; LocalVector<Control *> mouse_over_hierarchy; + bool sending_mouse_enter_exit_notifications = false; Window *subwindow_over = nullptr; // mouse_over and subwindow_over are mutually exclusive. At all times at least one of them is nullptr. Window *windowmanager_window_over = nullptr; // Only used in root Viewport. Control *drag_mouse_over = nullptr; diff --git a/scene/main/window.cpp b/scene/main/window.cpp index 823b0c6f5b..36d7d079b2 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -284,7 +284,13 @@ void Window::set_title(const String &p_title) { embedder->_sub_window_update(this); } else if (window_id != DisplayServer::INVALID_WINDOW_ID) { DisplayServer::get_singleton()->window_set_title(tr_title, window_id); - _update_window_size(); + if (keep_title_visible) { + Size2i title_size = DisplayServer::get_singleton()->window_get_title_size(tr_title, window_id); + Size2i size_limit = get_clamped_minimum_size(); + if (title_size.x > size_limit.x || title_size.y > size_limit.y) { + _update_window_size(); + } + } } } @@ -961,6 +967,10 @@ Size2i Window::_clamp_window_size(const Size2i &p_size) { void Window::_update_window_size() { Size2i size_limit = get_clamped_minimum_size(); + if (!embedder && window_id != DisplayServer::INVALID_WINDOW_ID && keep_title_visible) { + Size2i title_size = DisplayServer::get_singleton()->window_get_title_size(tr_title, window_id); + size_limit = size_limit.max(title_size); + } size = size.max(size_limit); @@ -992,12 +1002,6 @@ void Window::_update_window_size() { } DisplayServer::get_singleton()->window_set_max_size(max_size_used, window_id); - - if (keep_title_visible) { - Size2i title_size = DisplayServer::get_singleton()->window_get_title_size(tr_title, window_id); - size_limit = size_limit.max(title_size); - } - DisplayServer::get_singleton()->window_set_min_size(size_limit, window_id); DisplayServer::get_singleton()->window_set_size(size, window_id); } @@ -1297,7 +1301,13 @@ void Window::_notification(int p_what) { if (!embedder && window_id != DisplayServer::INVALID_WINDOW_ID) { DisplayServer::get_singleton()->window_set_title(tr_title, window_id); - _update_window_size(); + if (keep_title_visible) { + Size2i title_size = DisplayServer::get_singleton()->window_get_title_size(tr_title, window_id); + Size2i size_limit = get_clamped_minimum_size(); + if (title_size.x > size_limit.x || title_size.y > size_limit.y) { + _update_window_size(); + } + } } } break; diff --git a/scene/property_utils.cpp b/scene/property_utils.cpp index 063e91df67..090c81aefe 100644 --- a/scene/property_utils.cpp +++ b/scene/property_utils.cpp @@ -74,10 +74,31 @@ Variant PropertyUtils::get_property_default_value(const Object *p_object, const const SceneState::PackState &ia = states_stack[i]; bool found = false; Variant value_in_ancestor = ia.state->get_property_value(ia.node, p_property, found); + const Vector<String> &deferred_properties = ia.state->get_node_deferred_nodepath_properties(ia.node); if (found) { if (r_is_valid) { *r_is_valid = true; } + // Replace properties stored as NodePaths with actual Nodes. + // Otherwise, the property value would be considered as overridden. + if (deferred_properties.has(p_property)) { + if (value_in_ancestor.get_type() == Variant::ARRAY) { + Array paths = value_in_ancestor; + + bool valid = false; + Array array = node->get(p_property, &valid); + ERR_CONTINUE(!valid); + array = array.duplicate(); + + array.resize(paths.size()); + for (int j = 0; j < array.size(); j++) { + array.set(j, node->get_node_or_null(paths[j])); + } + value_in_ancestor = array; + } else { + value_in_ancestor = node->get_node_or_null(value_in_ancestor); + } + } return value_in_ancestor; } // Save script for later diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 3727c788fd..2e6759063b 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -293,6 +293,8 @@ static Ref<ResourceFormatSaverShaderInclude> resource_saver_shader_include; static Ref<ResourceFormatLoaderShaderInclude> resource_loader_shader_include; void register_scene_types() { + OS::get_singleton()->benchmark_begin_measure("Scene", "Register Types"); + SceneStringNames::create(); OS::get_singleton()->yield(); // may take time to init @@ -1182,9 +1184,13 @@ void register_scene_types() { } SceneDebugger::initialize(); + + OS::get_singleton()->benchmark_end_measure("Scene", "Register Types"); } void unregister_scene_types() { + OS::get_singleton()->benchmark_begin_measure("Scene", "Unregister Types"); + SceneDebugger::deinitialize(); ResourceLoader::remove_resource_format_loader(resource_loader_texture_layered); @@ -1227,10 +1233,16 @@ void unregister_scene_types() { CanvasItemMaterial::finish_shaders(); ColorPicker::finish_shaders(); SceneStringNames::free(); + + OS::get_singleton()->benchmark_end_measure("Scene", "Unregister Types"); } void register_scene_singletons() { + OS::get_singleton()->benchmark_begin_measure("Scene", "Register Singletons"); + GDREGISTER_CLASS(ThemeDB); Engine::get_singleton()->add_singleton(Engine::Singleton("ThemeDB", ThemeDB::get_singleton())); + + OS::get_singleton()->benchmark_end_measure("Scene", "Register Singletons"); } diff --git a/scene/resources/camera_attributes.cpp b/scene/resources/camera_attributes.cpp index 323241200c..af5df165b3 100644 --- a/scene/resources/camera_attributes.cpp +++ b/scene/resources/camera_attributes.cpp @@ -286,10 +286,10 @@ void CameraAttributesPractical::_bind_methods() { ADD_GROUP("DOF Blur", "dof_blur_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dof_blur_far_enabled"), "set_dof_blur_far_enabled", "is_dof_blur_far_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_far_distance", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp,suffix:m"), "set_dof_blur_far_distance", "get_dof_blur_far_distance"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_far_transition", PROPERTY_HINT_RANGE, "-1,8192,0.01,exp"), "set_dof_blur_far_transition", "get_dof_blur_far_transition"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_far_transition", PROPERTY_HINT_RANGE, "-1,8192,0.01"), "set_dof_blur_far_transition", "get_dof_blur_far_transition"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dof_blur_near_enabled"), "set_dof_blur_near_enabled", "is_dof_blur_near_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_near_distance", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp,suffix:m"), "set_dof_blur_near_distance", "get_dof_blur_near_distance"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_near_transition", PROPERTY_HINT_RANGE, "-1,8192,0.01,exp"), "set_dof_blur_near_transition", "get_dof_blur_near_transition"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_near_transition", PROPERTY_HINT_RANGE, "-1,8192,0.01"), "set_dof_blur_near_transition", "get_dof_blur_near_transition"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_amount", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_dof_blur_amount", "get_dof_blur_amount"); ADD_GROUP("Auto Exposure", "auto_exposure_"); diff --git a/scene/resources/importer_mesh.cpp b/scene/resources/importer_mesh.cpp index 1f4171c072..66d758080e 100644 --- a/scene/resources/importer_mesh.cpp +++ b/scene/resources/importer_mesh.cpp @@ -969,7 +969,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; } diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index 515ae67c59..1b74063ff4 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -3167,7 +3167,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" }, diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp index 13791d8c2b..8ad9eec25f 100644 --- a/scene/resources/primitive_meshes.cpp +++ b/scene/resources/primitive_meshes.cpp @@ -552,7 +552,7 @@ void CapsuleMesh::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater,suffix:m"), "set_radius", "get_radius"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater,suffix:m"), "set_height", "get_height"); ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_segments", PROPERTY_HINT_RANGE, "1,100,1,or_greater"), "set_radial_segments", "get_radial_segments"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "1,100,1,or_greater"), "set_rings", "get_rings"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_rings", "get_rings"); ADD_LINKED_PROPERTY("radius", "height"); ADD_LINKED_PROPERTY("height", "radius"); @@ -594,7 +594,8 @@ int CapsuleMesh::get_radial_segments() const { } void CapsuleMesh::set_rings(const int p_rings) { - rings = p_rings > 1 ? p_rings : 1; + ERR_FAIL_COND(p_rings < 0); + rings = p_rings; _request_update(); } @@ -1161,7 +1162,7 @@ void CylinderMesh::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bottom_radius", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater,suffix:m"), "set_bottom_radius", "get_bottom_radius"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater,suffix:m"), "set_height", "get_height"); ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_segments", PROPERTY_HINT_RANGE, "1,100,1,or_greater"), "set_radial_segments", "get_radial_segments"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "1,100,1,or_greater"), "set_rings", "get_rings"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_rings", "get_rings"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cap_top"), "set_cap_top", "is_cap_top"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cap_bottom"), "set_cap_bottom", "is_cap_bottom"); } @@ -1206,7 +1207,8 @@ int CylinderMesh::get_radial_segments() const { } void CylinderMesh::set_rings(const int p_rings) { - rings = p_rings > 0 ? p_rings : 0; + ERR_FAIL_COND(p_rings < 0); + rings = p_rings; _request_update(); } @@ -1919,7 +1921,8 @@ int SphereMesh::get_radial_segments() const { } void SphereMesh::set_rings(const int p_rings) { - rings = p_rings > 1 ? p_rings : 1; + ERR_FAIL_COND(p_rings < 1); + rings = p_rings; _request_update(); } diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index 7b4080517f..422cd4fa2c 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -1839,20 +1839,25 @@ Vector<Vector<Ref<Texture2D>>> TileSet::generate_terrains_icons(Size2i p_size) { // Generate the icons. for (int terrain_set = 0; terrain_set < get_terrain_sets_count(); terrain_set++) { for (int terrain = 0; terrain < get_terrains_count(terrain_set); terrain++) { - Ref<Image> image; - image.instantiate(); + Ref<Image> dst_image; + dst_image.instantiate(); if (counts[terrain_set][terrain].count > 0) { // Get the best tile. - Ref<Texture2D> texture = counts[terrain_set][terrain].texture; + Ref<Texture2D> src_texture = counts[terrain_set][terrain].texture; + ERR_FAIL_COND_V(src_texture.is_null(), output); + Ref<Image> src_image = src_texture->get_image(); + ERR_FAIL_COND_V(src_image.is_null(), output); Rect2i region = counts[terrain_set][terrain].region; - image->initialize_data(region.size.x, region.size.y, false, Image::FORMAT_RGBA8); - image->blit_rect(texture->get_image(), region, Point2i()); - image->resize(p_size.x, p_size.y, Image::INTERPOLATE_NEAREST); + + dst_image->initialize_data(region.size.x, region.size.y, false, src_image->get_format()); + dst_image->blit_rect(src_image, region, Point2i()); + dst_image->convert(Image::FORMAT_RGBA8); + dst_image->resize(p_size.x, p_size.y, Image::INTERPOLATE_NEAREST); } else { - image->initialize_data(1, 1, false, Image::FORMAT_RGBA8); - image->set_pixel(0, 0, get_terrain_color(terrain_set, terrain)); + dst_image->initialize_data(1, 1, false, Image::FORMAT_RGBA8); + dst_image->set_pixel(0, 0, get_terrain_color(terrain_set, terrain)); } - Ref<ImageTexture> icon = ImageTexture::create_from_image(image); + Ref<ImageTexture> icon = ImageTexture::create_from_image(dst_image); icon->set_size_override(p_size); output.write[terrain_set].write[terrain] = icon; } diff --git a/scene/resources/video_stream.cpp b/scene/resources/video_stream.cpp index dc8545426f..3b152d11ce 100644 --- a/scene/resources/video_stream.cpp +++ b/scene/resources/video_stream.cpp @@ -172,6 +172,7 @@ Ref<VideoStreamPlayback> VideoStream::instantiate_playback() { void VideoStream::set_file(const String &p_file) { file = p_file; + emit_changed(); } String VideoStream::get_file() { diff --git a/servers/navigation_server_2d.cpp b/servers/navigation_server_2d.cpp index 3804b45e1a..d37bbcfff0 100644 --- a/servers/navigation_server_2d.cpp +++ b/servers/navigation_server_2d.cpp @@ -59,6 +59,8 @@ void NavigationServer2D::_bind_methods() { ClassDB::bind_method(D_METHOD("map_force_update", "map"), &NavigationServer2D::map_force_update); + ClassDB::bind_method(D_METHOD("map_get_random_point", "map", "navigation_layers", "uniformly"), &NavigationServer2D::map_get_random_point); + ClassDB::bind_method(D_METHOD("query_path", "parameters", "result"), &NavigationServer2D::query_path); ClassDB::bind_method(D_METHOD("region_create"), &NavigationServer2D::region_create); @@ -82,6 +84,7 @@ void NavigationServer2D::_bind_methods() { ClassDB::bind_method(D_METHOD("region_get_connections_count", "region"), &NavigationServer2D::region_get_connections_count); ClassDB::bind_method(D_METHOD("region_get_connection_pathway_start", "region", "connection"), &NavigationServer2D::region_get_connection_pathway_start); ClassDB::bind_method(D_METHOD("region_get_connection_pathway_end", "region", "connection"), &NavigationServer2D::region_get_connection_pathway_end); + ClassDB::bind_method(D_METHOD("region_get_random_point", "region", "navigation_layers", "uniformly"), &NavigationServer2D::region_get_random_point); ClassDB::bind_method(D_METHOD("link_create"), &NavigationServer2D::link_create); ClassDB::bind_method(D_METHOD("link_set_map", "link", "map"), &NavigationServer2D::link_set_map); diff --git a/servers/navigation_server_2d.h b/servers/navigation_server_2d.h index 0afd794c26..66bfface3e 100644 --- a/servers/navigation_server_2d.h +++ b/servers/navigation_server_2d.h @@ -103,6 +103,8 @@ public: virtual void map_force_update(RID p_map) = 0; + virtual Vector2 map_get_random_point(RID p_map, uint32_t p_navigation_layers, bool p_uniformly) const = 0; + /// Creates a new region. virtual RID region_create() = 0; @@ -145,6 +147,8 @@ public: virtual Vector2 region_get_connection_pathway_start(RID p_region, int p_connection_id) const = 0; virtual Vector2 region_get_connection_pathway_end(RID p_region, int p_connection_id) const = 0; + virtual Vector2 region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const = 0; + /// Creates a new link between positions in the nav map. virtual RID link_create() = 0; diff --git a/servers/navigation_server_2d_dummy.h b/servers/navigation_server_2d_dummy.h index d64a9454aa..d380ca5242 100644 --- a/servers/navigation_server_2d_dummy.h +++ b/servers/navigation_server_2d_dummy.h @@ -58,6 +58,7 @@ public: TypedArray<RID> map_get_agents(RID p_map) const override { return TypedArray<RID>(); } TypedArray<RID> map_get_obstacles(RID p_map) const override { return TypedArray<RID>(); } void map_force_update(RID p_map) override {} + Vector2 map_get_random_point(RID p_map, uint32_t p_naviation_layers, bool p_uniformly) const override { return Vector2(); }; RID region_create() override { return RID(); } void region_set_enabled(RID p_region, bool p_enabled) override {} @@ -80,6 +81,7 @@ public: int region_get_connections_count(RID p_region) const override { return 0; } Vector2 region_get_connection_pathway_start(RID p_region, int p_connection_id) const override { return Vector2(); } Vector2 region_get_connection_pathway_end(RID p_region, int p_connection_id) const override { return Vector2(); } + Vector2 region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const override { return Vector2(); }; RID link_create() override { return RID(); } void link_set_map(RID p_link, RID p_map) override {} diff --git a/servers/navigation_server_3d.cpp b/servers/navigation_server_3d.cpp index eccacd3ea8..874809a6d7 100644 --- a/servers/navigation_server_3d.cpp +++ b/servers/navigation_server_3d.cpp @@ -64,6 +64,8 @@ void NavigationServer3D::_bind_methods() { ClassDB::bind_method(D_METHOD("map_force_update", "map"), &NavigationServer3D::map_force_update); + ClassDB::bind_method(D_METHOD("map_get_random_point", "map", "navigation_layers", "uniformly"), &NavigationServer3D::map_get_random_point); + ClassDB::bind_method(D_METHOD("query_path", "parameters", "result"), &NavigationServer3D::query_path); ClassDB::bind_method(D_METHOD("region_create"), &NavigationServer3D::region_create); @@ -90,6 +92,7 @@ void NavigationServer3D::_bind_methods() { ClassDB::bind_method(D_METHOD("region_get_connections_count", "region"), &NavigationServer3D::region_get_connections_count); ClassDB::bind_method(D_METHOD("region_get_connection_pathway_start", "region", "connection"), &NavigationServer3D::region_get_connection_pathway_start); ClassDB::bind_method(D_METHOD("region_get_connection_pathway_end", "region", "connection"), &NavigationServer3D::region_get_connection_pathway_end); + ClassDB::bind_method(D_METHOD("region_get_random_point", "region", "navigation_layers", "uniformly"), &NavigationServer3D::region_get_random_point); ClassDB::bind_method(D_METHOD("link_create"), &NavigationServer3D::link_create); ClassDB::bind_method(D_METHOD("link_set_map", "link", "map"), &NavigationServer3D::link_set_map); diff --git a/servers/navigation_server_3d.h b/servers/navigation_server_3d.h index 53d33d0058..96587afefa 100644 --- a/servers/navigation_server_3d.h +++ b/servers/navigation_server_3d.h @@ -114,6 +114,8 @@ public: virtual void map_force_update(RID p_map) = 0; + virtual Vector3 map_get_random_point(RID p_map, uint32_t p_navigation_layers, bool p_uniformly) const = 0; + /// Creates a new region. virtual RID region_create() = 0; @@ -161,6 +163,8 @@ public: virtual Vector3 region_get_connection_pathway_start(RID p_region, int p_connection_id) const = 0; virtual Vector3 region_get_connection_pathway_end(RID p_region, int p_connection_id) const = 0; + virtual Vector3 region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const = 0; + /// Creates a new link between positions in the nav map. virtual RID link_create() = 0; diff --git a/servers/navigation_server_3d_dummy.h b/servers/navigation_server_3d_dummy.h index 5258022328..7bd3eec937 100644 --- a/servers/navigation_server_3d_dummy.h +++ b/servers/navigation_server_3d_dummy.h @@ -58,6 +58,7 @@ public: Vector3 map_get_closest_point(RID p_map, const Vector3 &p_point) const override { return Vector3(); } Vector3 map_get_closest_point_normal(RID p_map, const Vector3 &p_point) const override { return Vector3(); } RID map_get_closest_point_owner(RID p_map, const Vector3 &p_point) const override { return RID(); } + Vector3 map_get_random_point(RID p_map, uint32_t p_navigation_layers, bool p_uniformly) const override { return Vector3(); } TypedArray<RID> map_get_links(RID p_map) const override { return TypedArray<RID>(); } TypedArray<RID> map_get_regions(RID p_map) const override { return TypedArray<RID>(); } TypedArray<RID> map_get_agents(RID p_map) const override { return TypedArray<RID>(); } @@ -87,6 +88,7 @@ public: int region_get_connections_count(RID p_region) const override { return 0; } Vector3 region_get_connection_pathway_start(RID p_region, int p_connection_id) const override { return Vector3(); } Vector3 region_get_connection_pathway_end(RID p_region, int p_connection_id) const override { return Vector3(); } + Vector3 region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const override { return Vector3(); } RID link_create() override { return RID(); } void link_set_map(RID p_link, RID p_map) override {} RID link_get_map(RID p_link) const override { return RID(); } diff --git a/servers/register_server_types.cpp b/servers/register_server_types.cpp index 50e14a1f37..2beab44f91 100644 --- a/servers/register_server_types.cpp +++ b/servers/register_server_types.cpp @@ -117,6 +117,8 @@ static MovieWriterMJPEG *writer_mjpeg = nullptr; static MovieWriterPNGWAV *writer_pngwav = nullptr; void register_server_types() { + OS::get_singleton()->benchmark_begin_measure("Servers", "Register Extensions"); + shader_types = memnew(ShaderTypes); GDREGISTER_CLASS(TextServerManager); @@ -293,16 +295,24 @@ void register_server_types() { writer_pngwav = memnew(MovieWriterPNGWAV); MovieWriter::add_writer(writer_pngwav); + + OS::get_singleton()->benchmark_end_measure("Servers", "Register Extensions"); } void unregister_server_types() { + OS::get_singleton()->benchmark_begin_measure("Servers", "Unregister Extensions"); + ServersDebugger::deinitialize(); memdelete(shader_types); memdelete(writer_mjpeg); memdelete(writer_pngwav); + + OS::get_singleton()->benchmark_end_measure("Servers", "Unregister Extensions"); } void register_server_singletons() { + OS::get_singleton()->benchmark_begin_measure("Servers", "Register Singletons"); + Engine::get_singleton()->add_singleton(Engine::Singleton("DisplayServer", DisplayServer::get_singleton(), "DisplayServer")); Engine::get_singleton()->add_singleton(Engine::Singleton("RenderingServer", RenderingServer::get_singleton(), "RenderingServer")); Engine::get_singleton()->add_singleton(Engine::Singleton("AudioServer", AudioServer::get_singleton(), "AudioServer")); @@ -312,4 +322,6 @@ void register_server_singletons() { Engine::get_singleton()->add_singleton(Engine::Singleton("NavigationServer3D", NavigationServer3D::get_singleton(), "NavigationServer3D")); Engine::get_singleton()->add_singleton(Engine::Singleton("XRServer", XRServer::get_singleton(), "XRServer")); Engine::get_singleton()->add_singleton(Engine::Singleton("CameraServer", CameraServer::get_singleton(), "CameraServer")); + + OS::get_singleton()->benchmark_end_measure("Servers", "Register Singletons"); } diff --git a/servers/rendering/renderer_rd/effects/copy_effects.cpp b/servers/rendering/renderer_rd/effects/copy_effects.cpp index 87033cb559..c9f0008998 100644 --- a/servers/rendering/renderer_rd/effects/copy_effects.cpp +++ b/servers/rendering/renderer_rd/effects/copy_effects.cpp @@ -71,7 +71,9 @@ CopyEffects::CopyEffects(bool p_prefer_raster_effects) { for (int i = 0; i < BLUR_MODE_MAX; i++) { blur_raster.pipelines[i].clear(); } + } + { Vector<String> copy_modes; copy_modes.push_back("\n#define MODE_GAUSSIAN_BLUR\n"); copy_modes.push_back("\n#define MODE_GAUSSIAN_BLUR\n#define DST_IMAGE_8BIT\n"); @@ -310,12 +312,12 @@ CopyEffects::~CopyEffects() { filter.raster_shader.version_free(filter.shader_version); roughness.raster_shader.version_free(roughness.shader_version); } else { - copy.shader.version_free(copy.shader_version); cubemap_downsampler.compute_shader.version_free(cubemap_downsampler.shader_version); filter.compute_shader.version_free(filter.shader_version); roughness.compute_shader.version_free(roughness.shader_version); } + copy.shader.version_free(copy.shader_version); specular_merge.shader.version_free(specular_merge.shader_version); RD::get_singleton()->free(filter.coefficient_buffer); @@ -335,8 +337,6 @@ CopyEffects::~CopyEffects() { } void CopyEffects::copy_to_rect(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y, bool p_force_luminance, bool p_all_source, bool p_8_bit_dst, bool p_alpha_to_one) { - ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use the compute version of the copy_to_rect shader with the mobile renderer."); - UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton(); ERR_FAIL_NULL(uniform_set_cache); MaterialStorage *material_storage = MaterialStorage::get_singleton(); @@ -386,8 +386,6 @@ void CopyEffects::copy_to_rect(RID p_source_rd_texture, RID p_dest_texture, cons } void CopyEffects::copy_cubemap_to_panorama(RID p_source_cube, RID p_dest_panorama, const Size2i &p_panorama_size, float p_lod, bool p_is_array) { - ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use the compute version of the copy_cubemap_to_panorama shader with the mobile renderer."); - UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton(); ERR_FAIL_NULL(uniform_set_cache); MaterialStorage *material_storage = MaterialStorage::get_singleton(); @@ -423,8 +421,6 @@ void CopyEffects::copy_cubemap_to_panorama(RID p_source_cube, RID p_dest_panoram } void CopyEffects::copy_depth_to_rect(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y) { - ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use the compute version of the copy_depth_to_rect shader with the mobile renderer."); - UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton(); ERR_FAIL_NULL(uniform_set_cache); MaterialStorage *material_storage = MaterialStorage::get_singleton(); @@ -462,8 +458,6 @@ void CopyEffects::copy_depth_to_rect(RID p_source_rd_texture, RID p_dest_texture } void CopyEffects::copy_depth_to_rect_and_linearize(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y, float p_z_near, float p_z_far) { - ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use the compute version of the copy_depth_to_rect_and_linearize shader with the mobile renderer."); - UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton(); ERR_FAIL_NULL(uniform_set_cache); MaterialStorage *material_storage = MaterialStorage::get_singleton(); diff --git a/servers/rendering/renderer_rd/effects/ss_effects.cpp b/servers/rendering/renderer_rd/effects/ss_effects.cpp index d123f24865..628edc0127 100644 --- a/servers/rendering/renderer_rd/effects/ss_effects.cpp +++ b/servers/rendering/renderer_rd/effects/ss_effects.cpp @@ -950,7 +950,7 @@ void SSEffects::screen_space_indirect_lighting(Ref<RenderSceneBuffersRD> p_rende RD::get_singleton()->draw_command_end_label(); // SSIL - RD::get_singleton()->compute_list_end(RD::BARRIER_MASK_NO_BARRIER); + RD::get_singleton()->compute_list_end(RD::BARRIER_MASK_TRANSFER); // Zeroing importance_map_load_counter depends on us. int zero[1] = { 0 }; RD::get_singleton()->buffer_update(ssil.importance_map_load_counter, 0, sizeof(uint32_t), &zero, 0); //no barrier @@ -1332,7 +1332,7 @@ void SSEffects::generate_ssao(Ref<RenderSceneBuffersRD> p_render_buffers, SSAORe RD::get_singleton()->draw_command_end_label(); // Interleave } RD::get_singleton()->draw_command_end_label(); //SSAO - RD::get_singleton()->compute_list_end(RD::BARRIER_MASK_NO_BARRIER); //wait for upcoming transfer + RD::get_singleton()->compute_list_end(RD::BARRIER_MASK_TRANSFER); // Zeroing importance_map_load_counter depends on us. int zero[1] = { 0 }; RD::get_singleton()->buffer_update(ssao.importance_map_load_counter, 0, sizeof(uint32_t), &zero, 0); //no barrier diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp index 9e10f9599e..702f4f1a09 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -1672,6 +1672,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co RID color_framebuffer; RID color_only_framebuffer; RID depth_framebuffer; + RendererRD::MaterialStorage::Samplers samplers; PassMode depth_pass_mode = PASS_MODE_DEPTH; uint32_t color_pass_flags = 0; @@ -1698,6 +1699,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co } reverse_cull = true; // for some reason our views are inverted + samplers = RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default(); } else { screen_size = rb->get_internal_size(); @@ -1729,6 +1731,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co color_framebuffer = rb_data->get_color_pass_fb(color_pass_flags); color_only_framebuffer = rb_data->get_color_only_fb(); + samplers = rb->get_samplers(); } p_render_data->scene_data->emissive_exposure_normalization = -1.0; @@ -1740,11 +1743,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co _setup_environment(p_render_data, is_reflection_probe, screen_size, !is_reflection_probe, p_default_bg_color, false); // May have changed due to the above (light buffer enlarged, as an example). - if (is_reflection_probe) { - _update_render_base_uniform_set(RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default(), BASE_UNIFORM_SET_CACHE_DEFAULT); - } else { - _update_render_base_uniform_set(rb->get_samplers(), BASE_UNIFORM_SET_CACHE_VIEWPORT); - } + _update_render_base_uniform_set(); _fill_render_list(RENDER_LIST_OPAQUE, p_render_data, PASS_MODE_COLOR, using_sdfgi, using_sdfgi || using_voxelgi, using_motion_pass); render_list[RENDER_LIST_OPAQUE].sort_by_key(); @@ -1928,7 +1927,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co RD::get_singleton()->draw_command_begin_label("Render Depth Pre-Pass"); - RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_OPAQUE, nullptr, RID()); + RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_OPAQUE, nullptr, RID(), samplers); bool finish_depth = using_ssao || using_ssil || using_sdfgi || using_voxelgi; RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].element_info.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, depth_pass_mode, 0, rb_data.is_null(), p_render_data->directional_light_soft_shadows, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count); @@ -1975,17 +1974,13 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co p_render_data->scene_data->opaque_prepass_threshold = 0.0f; // Shadow pass can change the base uniform set samplers. - if (is_reflection_probe) { - _update_render_base_uniform_set(RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default(), BASE_UNIFORM_SET_CACHE_DEFAULT); - } else { - _update_render_base_uniform_set(rb->get_samplers(), BASE_UNIFORM_SET_CACHE_VIEWPORT); - } + _update_render_base_uniform_set(); _setup_environment(p_render_data, is_reflection_probe, screen_size, !is_reflection_probe, p_default_bg_color, true, using_motion_pass); RENDER_TIMESTAMP("Render Opaque Pass"); - RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_OPAQUE, p_render_data, radiance_texture, true); + RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_OPAQUE, p_render_data, radiance_texture, samplers, true); bool can_continue_color = !scene_state.used_screen_texture && !using_ssr && !using_sss; bool can_continue_depth = !(scene_state.used_depth_texture || scene_state.used_normal_texture) && !using_ssr && !using_sss; @@ -2032,7 +2027,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co RENDER_TIMESTAMP("Render Motion Pass"); - rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_MOTION, p_render_data, radiance_texture, true); + rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_MOTION, p_render_data, radiance_texture, samplers, true); RenderListParameters render_list_params(render_list[RENDER_LIST_MOTION].elements.ptr(), render_list[RENDER_LIST_MOTION].element_info.ptr(), render_list[RENDER_LIST_MOTION].elements.size(), reverse_cull, PASS_MODE_COLOR, color_pass_flags, rb_data.is_null(), p_render_data->directional_light_soft_shadows, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count); _render_list_with_threads(&render_list_params, color_framebuffer, RD::INITIAL_ACTION_CONTINUE, final_color_action, RD::INITIAL_ACTION_CONTINUE, final_depth_action); @@ -2175,7 +2170,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co RD::get_singleton()->draw_command_begin_label("Render 3D Transparent Pass"); - rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_ALPHA, p_render_data, radiance_texture, true); + rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_ALPHA, p_render_data, radiance_texture, samplers, true); _setup_environment(p_render_data, is_reflection_probe, screen_size, !is_reflection_probe, p_default_bg_color, false); @@ -2504,7 +2499,7 @@ void RenderForwardClustered::_render_shadow_pass(RID p_light, RID p_shadow_atlas void RenderForwardClustered::_render_shadow_begin() { scene_state.shadow_passes.clear(); RD::get_singleton()->draw_command_begin_label("Shadow Setup"); - _update_render_base_uniform_set(RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default(), BASE_UNIFORM_SET_CACHE_DEFAULT); + _update_render_base_uniform_set(); render_list[RENDER_LIST_SECONDARY].clear(); scene_state.instance_data[RENDER_LIST_SECONDARY].clear(); @@ -2587,7 +2582,7 @@ void RenderForwardClustered::_render_shadow_process() { for (uint32_t i = 0; i < scene_state.shadow_passes.size(); i++) { //render passes need to be configured after instance buffer is done, since they need the latest version SceneState::ShadowPass &shadow_pass = scene_state.shadow_passes[i]; - shadow_pass.rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, RID(), false, i); + shadow_pass.rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, RID(), RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default(), false, i); } RD::get_singleton()->draw_command_end_label(); @@ -2628,7 +2623,7 @@ void RenderForwardClustered::_render_particle_collider_heightfield(RID p_fb, con render_data.cluster_max_elements = 32; render_data.instances = &p_instances; - _update_render_base_uniform_set(RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default(), BASE_UNIFORM_SET_CACHE_DEFAULT); + _update_render_base_uniform_set(); _setup_environment(&render_data, true, Vector2(1, 1), true, Color(), false, false, false); @@ -2638,7 +2633,7 @@ void RenderForwardClustered::_render_particle_collider_heightfield(RID p_fb, con render_list[RENDER_LIST_SECONDARY].sort_by_key(); _fill_instance_data(RENDER_LIST_SECONDARY); - RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, RID()); + RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, RID(), RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default()); RENDER_TIMESTAMP("Render Collider Heightfield"); @@ -2674,7 +2669,7 @@ void RenderForwardClustered::_render_material(const Transform3D &p_cam_transform scene_shader.enable_advanced_shader_group(); - _update_render_base_uniform_set(RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default(), BASE_UNIFORM_SET_CACHE_DEFAULT); + _update_render_base_uniform_set(); _setup_environment(&render_data, true, Vector2(1, 1), false, Color()); @@ -2683,7 +2678,7 @@ void RenderForwardClustered::_render_material(const Transform3D &p_cam_transform render_list[RENDER_LIST_SECONDARY].sort_by_key(); _fill_instance_data(RENDER_LIST_SECONDARY); - RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, RID()); + RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, RID(), RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default()); RENDER_TIMESTAMP("Render 3D Material"); @@ -2725,7 +2720,7 @@ void RenderForwardClustered::_render_uv2(const PagedArray<RenderGeometryInstance scene_shader.enable_advanced_shader_group(); - _update_render_base_uniform_set(RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default(), BASE_UNIFORM_SET_CACHE_DEFAULT); + _update_render_base_uniform_set(); _setup_environment(&render_data, true, Vector2(1, 1), false, Color()); @@ -2734,7 +2729,7 @@ void RenderForwardClustered::_render_uv2(const PagedArray<RenderGeometryInstance render_list[RENDER_LIST_SECONDARY].sort_by_key(); _fill_instance_data(RENDER_LIST_SECONDARY); - RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, RID()); + RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, RID(), RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default()); RENDER_TIMESTAMP("Render 3D Material"); @@ -2794,7 +2789,7 @@ void RenderForwardClustered::_render_sdfgi(Ref<RenderSceneBuffersRD> p_render_bu render_data.cluster_max_elements = 32; render_data.instances = &p_instances; - _update_render_base_uniform_set(RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default(), BASE_UNIFORM_SET_CACHE_DEFAULT); + _update_render_base_uniform_set(); PassMode pass_mode = PASS_MODE_SDF; _fill_render_list(RENDER_LIST_SECONDARY, &render_data, pass_mode); @@ -2852,7 +2847,7 @@ void RenderForwardClustered::_render_sdfgi(Ref<RenderSceneBuffersRD> p_render_bu scene_data.emissive_exposure_normalization = p_exposure_normalization; _setup_environment(&render_data, true, Vector2(1, 1), false, Color()); - RID rp_uniform_set = _setup_sdfgi_render_pass_uniform_set(p_albedo_texture, p_emission_texture, p_emission_aniso_texture, p_geom_facing_texture); + RID rp_uniform_set = _setup_sdfgi_render_pass_uniform_set(p_albedo_texture, p_emission_texture, p_emission_aniso_texture, p_geom_facing_texture, RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default()); HashMap<Size2i, RID>::Iterator E = sdfgi_framebuffer_size_cache.find(fb_size); if (!E) { @@ -2868,23 +2863,21 @@ void RenderForwardClustered::_render_sdfgi(Ref<RenderSceneBuffersRD> p_render_bu } void RenderForwardClustered::base_uniforms_changed() { - for (int i = 0; i < BASE_UNIFORM_SET_CACHE_MAX; i++) { - if (!render_base_uniform_set_cache[i].is_null() && RD::get_singleton()->uniform_set_is_valid(render_base_uniform_set_cache[i])) { - RD::get_singleton()->free(render_base_uniform_set_cache[i]); - } - render_base_uniform_set_cache[i] = RID(); + if (!render_base_uniform_set.is_null() && RD::get_singleton()->uniform_set_is_valid(render_base_uniform_set)) { + RD::get_singleton()->free(render_base_uniform_set); } + render_base_uniform_set = RID(); } -void RenderForwardClustered::_update_render_base_uniform_set(const RendererRD::MaterialStorage::Samplers &p_samplers, BaseUniformSetCache p_cache_index) { +void RenderForwardClustered::_update_render_base_uniform_set() { RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton(); - if (render_base_uniform_set_cache[p_cache_index].is_null() || !RD::get_singleton()->uniform_set_is_valid(render_base_uniform_set_cache[p_cache_index]) || (lightmap_texture_array_version_cache[p_cache_index] != light_storage->lightmap_array_get_version())) { - if (render_base_uniform_set_cache[p_cache_index].is_valid() && RD::get_singleton()->uniform_set_is_valid(render_base_uniform_set_cache[p_cache_index])) { - RD::get_singleton()->free(render_base_uniform_set_cache[p_cache_index]); + if (render_base_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(render_base_uniform_set) || (lightmap_texture_array_version != light_storage->lightmap_array_get_version())) { + if (render_base_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(render_base_uniform_set)) { + RD::get_singleton()->free(render_base_uniform_set); } - lightmap_texture_array_version_cache[p_cache_index] = light_storage->lightmap_array_get_version(); + lightmap_texture_array_version = light_storage->lightmap_array_get_version(); Vector<RD::Uniform> uniforms; @@ -2899,73 +2892,13 @@ void RenderForwardClustered::_update_render_base_uniform_set(const RendererRD::M { RD::Uniform u; u.binding = 3; - u.uniform_type = RD::UNIFORM_TYPE_SAMPLER; - RID sampler; - switch (decals_get_filter()) { - case RS::DECAL_FILTER_NEAREST: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - case RS::DECAL_FILTER_LINEAR: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - case RS::DECAL_FILTER_NEAREST_MIPMAPS: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - case RS::DECAL_FILTER_LINEAR_MIPMAPS: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - case RS::DECAL_FILTER_NEAREST_MIPMAPS_ANISOTROPIC: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - case RS::DECAL_FILTER_LINEAR_MIPMAPS_ANISOTROPIC: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - } - - u.append_id(sampler); - uniforms.push_back(u); - } - - { - RD::Uniform u; - u.binding = 4; - u.uniform_type = RD::UNIFORM_TYPE_SAMPLER; - RID sampler; - switch (light_projectors_get_filter()) { - case RS::LIGHT_PROJECTOR_FILTER_NEAREST: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - case RS::LIGHT_PROJECTOR_FILTER_LINEAR: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - case RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - case RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - case RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS_ANISOTROPIC: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - case RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS_ANISOTROPIC: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - } - - u.append_id(sampler); - uniforms.push_back(u); - } - - { - RD::Uniform u; - u.binding = 5; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.append_id(RendererRD::LightStorage::get_singleton()->get_omni_light_buffer()); uniforms.push_back(u); } { RD::Uniform u; - u.binding = 6; + u.binding = 4; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.append_id(RendererRD::LightStorage::get_singleton()->get_spot_light_buffer()); uniforms.push_back(u); @@ -2973,35 +2906,35 @@ void RenderForwardClustered::_update_render_base_uniform_set(const RendererRD::M { RD::Uniform u; - u.binding = 7; + u.binding = 5; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.append_id(RendererRD::LightStorage::get_singleton()->get_reflection_probe_buffer()); uniforms.push_back(u); } { RD::Uniform u; - u.binding = 8; + u.binding = 6; u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; u.append_id(RendererRD::LightStorage::get_singleton()->get_directional_light_buffer()); uniforms.push_back(u); } { RD::Uniform u; - u.binding = 9; + u.binding = 7; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.append_id(scene_state.lightmap_buffer); uniforms.push_back(u); } { RD::Uniform u; - u.binding = 10; + u.binding = 8; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.append_id(scene_state.lightmap_capture_buffer); uniforms.push_back(u); } { RD::Uniform u; - u.binding = 11; + u.binding = 9; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID decal_atlas = RendererRD::TextureStorage::get_singleton()->decal_atlas_get_texture(); u.append_id(decal_atlas); @@ -3009,7 +2942,7 @@ void RenderForwardClustered::_update_render_base_uniform_set(const RendererRD::M } { RD::Uniform u; - u.binding = 12; + u.binding = 10; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID decal_atlas = RendererRD::TextureStorage::get_singleton()->decal_atlas_get_texture_srgb(); u.append_id(decal_atlas); @@ -3017,7 +2950,7 @@ void RenderForwardClustered::_update_render_base_uniform_set(const RendererRD::M } { RD::Uniform u; - u.binding = 13; + u.binding = 11; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.append_id(RendererRD::TextureStorage::get_singleton()->get_decal_buffer()); uniforms.push_back(u); @@ -3026,7 +2959,7 @@ void RenderForwardClustered::_update_render_base_uniform_set(const RendererRD::M { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; - u.binding = 14; + u.binding = 12; u.append_id(RendererRD::MaterialStorage::get_singleton()->global_shader_uniforms_get_storage_buffer()); uniforms.push_back(u); } @@ -3034,19 +2967,18 @@ void RenderForwardClustered::_update_render_base_uniform_set(const RendererRD::M { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; - u.binding = 15; + u.binding = 13; u.append_id(sdfgi_get_ubo()); uniforms.push_back(u); } - uniforms.append_array(p_samplers.get_uniforms(SAMPLERS_BINDING_FIRST_INDEX)); + uniforms.append_array(RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default().get_uniforms(14)); - render_base_uniform_set_cache[p_cache_index] = RD::get_singleton()->uniform_set_create(uniforms, scene_shader.default_shader_rd, SCENE_UNIFORM_SET); + render_base_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, scene_shader.default_shader_rd, SCENE_UNIFORM_SET); } - render_base_uniform_set = render_base_uniform_set_cache[p_cache_index]; } -RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_render_list, const RenderDataRD *p_render_data, RID p_radiance_texture, bool p_use_directional_shadow_atlas, int p_index) { +RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_render_list, const RenderDataRD *p_render_data, RID p_radiance_texture, const RendererRD::MaterialStorage::Samplers &p_samplers, bool p_use_directional_shadow_atlas, int p_index) { RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton(); @@ -3194,6 +3126,68 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend { RD::Uniform u; u.binding = 10; + u.uniform_type = RD::UNIFORM_TYPE_SAMPLER; + RID sampler; + switch (decals_get_filter()) { + case RS::DECAL_FILTER_NEAREST: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::DECAL_FILTER_LINEAR: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::DECAL_FILTER_NEAREST_MIPMAPS: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::DECAL_FILTER_LINEAR_MIPMAPS: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::DECAL_FILTER_NEAREST_MIPMAPS_ANISOTROPIC: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::DECAL_FILTER_LINEAR_MIPMAPS_ANISOTROPIC: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + } + + u.append_id(sampler); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.binding = 11; + u.uniform_type = RD::UNIFORM_TYPE_SAMPLER; + RID sampler; + switch (light_projectors_get_filter()) { + case RS::LIGHT_PROJECTOR_FILTER_NEAREST: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::LIGHT_PROJECTOR_FILTER_LINEAR: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS_ANISOTROPIC: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS_ANISOTROPIC: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + } + + u.append_id(sampler); + uniforms.push_back(u); + } + + uniforms.append_array(p_samplers.get_uniforms(12)); + + { + RD::Uniform u; + u.binding = 24; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID texture; if (rb.is_valid() && rb->has_texture(RB_SCOPE_BUFFERS, RB_TEX_BACK_DEPTH)) { @@ -3206,7 +3200,7 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend } { RD::Uniform u; - u.binding = 11; + u.binding = 25; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID bbt = rb_data.is_valid() ? rb->get_back_buffer_texture() : RID(); RID texture = bbt.is_valid() ? bbt : texture_storage->texture_rd_get_default(is_multiview ? RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_BLACK : RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK); @@ -3216,7 +3210,7 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend { RD::Uniform u; - u.binding = 12; + u.binding = 26; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID texture = rb_data.is_valid() && rb_data->has_normal_roughness() ? rb_data->get_normal_roughness() : texture_storage->texture_rd_get_default(is_multiview ? RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_NORMAL : RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_NORMAL); u.append_id(texture); @@ -3225,7 +3219,7 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend { RD::Uniform u; - u.binding = 13; + u.binding = 27; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID aot = rb.is_valid() && rb->has_texture(RB_SCOPE_SSAO, RB_FINAL) ? rb->get_texture(RB_SCOPE_SSAO, RB_FINAL) : RID(); RID texture = aot.is_valid() ? aot : texture_storage->texture_rd_get_default(is_multiview ? RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_BLACK : RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK); @@ -3235,7 +3229,7 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend { RD::Uniform u; - u.binding = 14; + u.binding = 28; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID texture = rb_data.is_valid() && rb->has_texture(RB_SCOPE_GI, RB_TEX_AMBIENT) ? rb->get_texture(RB_SCOPE_GI, RB_TEX_AMBIENT) : texture_storage->texture_rd_get_default(is_multiview ? RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_BLACK : RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK); u.append_id(texture); @@ -3244,7 +3238,7 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend { RD::Uniform u; - u.binding = 15; + u.binding = 29; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID texture = rb_data.is_valid() && rb->has_texture(RB_SCOPE_GI, RB_TEX_REFLECTION) ? rb->get_texture(RB_SCOPE_GI, RB_TEX_REFLECTION) : texture_storage->texture_rd_get_default(is_multiview ? RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_BLACK : RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK); u.append_id(texture); @@ -3252,7 +3246,7 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend } { RD::Uniform u; - u.binding = 16; + u.binding = 30; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID t; if (rb.is_valid() && rb->has_custom_data(RB_SCOPE_SDFGI)) { @@ -3267,7 +3261,7 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend } { RD::Uniform u; - u.binding = 17; + u.binding = 31; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID t; if (rb.is_valid() && rb->has_custom_data(RB_SCOPE_SDFGI)) { @@ -3282,7 +3276,7 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend } { RD::Uniform u; - u.binding = 18; + u.binding = 32; u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; RID voxel_gi; if (rb.is_valid() && rb->has_custom_data(RB_SCOPE_GI)) { @@ -3294,7 +3288,7 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend } { RD::Uniform u; - u.binding = 19; + u.binding = 33; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID vfog; if (rb_data.is_valid() && rb->has_custom_data(RB_SCOPE_FOG)) { @@ -3311,7 +3305,7 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend } { RD::Uniform u; - u.binding = 20; + u.binding = 34; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID ssil = rb.is_valid() && rb->has_texture(RB_SCOPE_SSIL, RB_FINAL) ? rb->get_texture(RB_SCOPE_SSIL, RB_FINAL) : RID(); RID texture = ssil.is_valid() ? ssil : texture_storage->texture_rd_get_default(is_multiview ? RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_BLACK : RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK); @@ -3322,7 +3316,7 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend return UniformSetCacheRD::get_singleton()->get_cache_vec(scene_shader.default_shader_rd, RENDER_PASS_UNIFORM_SET, uniforms); } -RID RenderForwardClustered::_setup_sdfgi_render_pass_uniform_set(RID p_albedo_texture, RID p_emission_texture, RID p_emission_aniso_texture, RID p_geom_facing_texture) { +RID RenderForwardClustered::_setup_sdfgi_render_pass_uniform_set(RID p_albedo_texture, RID p_emission_texture, RID p_emission_aniso_texture, RID p_geom_facing_texture, const RendererRD::MaterialStorage::Samplers &p_samplers) { RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); Vector<RD::Uniform> uniforms; @@ -3428,33 +3422,95 @@ RID RenderForwardClustered::_setup_sdfgi_render_pass_uniform_set(RID p_albedo_te uniforms.push_back(u); } + { + RD::Uniform u; + u.binding = 10; + u.uniform_type = RD::UNIFORM_TYPE_SAMPLER; + RID sampler; + switch (decals_get_filter()) { + case RS::DECAL_FILTER_NEAREST: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::DECAL_FILTER_LINEAR: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::DECAL_FILTER_NEAREST_MIPMAPS: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::DECAL_FILTER_LINEAR_MIPMAPS: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::DECAL_FILTER_NEAREST_MIPMAPS_ANISOTROPIC: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::DECAL_FILTER_LINEAR_MIPMAPS_ANISOTROPIC: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + } + + u.append_id(sampler); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.binding = 11; + u.uniform_type = RD::UNIFORM_TYPE_SAMPLER; + RID sampler; + switch (light_projectors_get_filter()) { + case RS::LIGHT_PROJECTOR_FILTER_NEAREST: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::LIGHT_PROJECTOR_FILTER_LINEAR: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS_ANISOTROPIC: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS_ANISOTROPIC: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + } + + u.append_id(sampler); + uniforms.push_back(u); + } + + uniforms.append_array(p_samplers.get_uniforms(12)); + // actual sdfgi stuff { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 10; + u.binding = 24; u.append_id(p_albedo_texture); uniforms.push_back(u); } { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 11; + u.binding = 25; u.append_id(p_emission_texture); uniforms.push_back(u); } { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 12; + u.binding = 26; u.append_id(p_emission_aniso_texture); uniforms.push_back(u); } { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 13; + u.binding = 27; u.append_id(p_geom_facing_texture); uniforms.push_back(u); } @@ -4148,7 +4204,6 @@ RenderForwardClustered::RenderForwardClustered() { } { defines += "\n#define MATERIAL_UNIFORM_SET " + itos(MATERIAL_UNIFORM_SET) + "\n"; - defines += "\n#define SAMPLERS_BINDING_FIRST_INDEX " + itos(SAMPLERS_BINDING_FIRST_INDEX) + "\n"; } #ifdef REAL_T_IS_DOUBLE { diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h index bedf119210..8119e6ff4d 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h @@ -64,8 +64,6 @@ class RenderForwardClustered : public RendererSceneRenderRD { MATERIAL_UNIFORM_SET = 3, }; - const int SAMPLERS_BINDING_FIRST_INDEX = 16; - enum { SPEC_CONSTANT_SOFT_SHADOW_SAMPLES = 6, SPEC_CONSTANT_PENUMBRA_SHADOW_SAMPLES = 7, @@ -158,22 +156,13 @@ class RenderForwardClustered : public RendererSceneRenderRD { virtual void setup_render_buffer_data(Ref<RenderSceneBuffersRD> p_render_buffers) override; - enum BaseUniformSetCache { - BASE_UNIFORM_SET_CACHE_VIEWPORT, - BASE_UNIFORM_SET_CACHE_DEFAULT, - BASE_UNIFORM_SET_CACHE_MAX - }; - RID render_base_uniform_set; - // One for custom samplers, one for default samplers. - // Need to switch between them as default is needed for probes, shadows, materials, etc. - RID render_base_uniform_set_cache[BASE_UNIFORM_SET_CACHE_MAX]; - uint64_t lightmap_texture_array_version_cache[BASE_UNIFORM_SET_CACHE_MAX] = { 0xFFFFFFFF, 0xFFFFFFFF }; + uint64_t lightmap_texture_array_version = 0xFFFFFFFF; - void _update_render_base_uniform_set(const RendererRD::MaterialStorage::Samplers &p_samplers, BaseUniformSetCache p_cache_index); - RID _setup_sdfgi_render_pass_uniform_set(RID p_albedo_texture, RID p_emission_texture, RID p_emission_aniso_texture, RID p_geom_facing_texture); - RID _setup_render_pass_uniform_set(RenderListType p_render_list, const RenderDataRD *p_render_data, RID p_radiance_texture, bool p_use_directional_shadow_atlas = false, int p_index = 0); + void _update_render_base_uniform_set(); + RID _setup_sdfgi_render_pass_uniform_set(RID p_albedo_texture, RID p_emission_texture, RID p_emission_aniso_texture, RID p_geom_facing_texture, const RendererRD::MaterialStorage::Samplers &p_samplers); + RID _setup_render_pass_uniform_set(RenderListType p_render_list, const RenderDataRD *p_render_data, RID p_radiance_texture, const RendererRD::MaterialStorage::Samplers &p_samplers, bool p_use_directional_shadow_atlas = false, int p_index = 0); enum PassMode { PASS_MODE_COLOR, diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp index b1413a52e8..5ff3c54385 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp @@ -349,7 +349,7 @@ bool RenderForwardMobile::_render_buffers_can_be_storage() { return false; } -RID RenderForwardMobile::_setup_render_pass_uniform_set(RenderListType p_render_list, const RenderDataRD *p_render_data, RID p_radiance_texture, bool p_use_directional_shadow_atlas, int p_index) { +RID RenderForwardMobile::_setup_render_pass_uniform_set(RenderListType p_render_list, const RenderDataRD *p_render_data, RID p_radiance_texture, const RendererRD::MaterialStorage::Samplers &p_samplers, bool p_use_directional_shadow_atlas, int p_index) { RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton(); RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); @@ -523,6 +523,68 @@ RID RenderForwardMobile::_setup_render_pass_uniform_set(RenderListType p_render_ uniforms.push_back(u); } + { + RD::Uniform u; + u.binding = 11; + u.uniform_type = RD::UNIFORM_TYPE_SAMPLER; + RID sampler; + switch (decals_get_filter()) { + case RS::DECAL_FILTER_NEAREST: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::DECAL_FILTER_LINEAR: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::DECAL_FILTER_NEAREST_MIPMAPS: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::DECAL_FILTER_LINEAR_MIPMAPS: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::DECAL_FILTER_NEAREST_MIPMAPS_ANISOTROPIC: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::DECAL_FILTER_LINEAR_MIPMAPS_ANISOTROPIC: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + } + + u.append_id(sampler); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.binding = 12; + u.uniform_type = RD::UNIFORM_TYPE_SAMPLER; + RID sampler; + switch (light_projectors_get_filter()) { + case RS::LIGHT_PROJECTOR_FILTER_NEAREST: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::LIGHT_PROJECTOR_FILTER_LINEAR: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS_ANISOTROPIC: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + case RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS_ANISOTROPIC: { + sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + } break; + } + + u.append_id(sampler); + uniforms.push_back(u); + } + + uniforms.append_array(p_samplers.get_uniforms(13)); + if (p_index >= (int)render_pass_uniform_sets.size()) { render_pass_uniform_sets.resize(p_index + 1); } @@ -679,6 +741,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color bool reverse_cull = p_render_data->scene_data->cam_transform.basis.determinant() < 0; bool using_subpass_transparent = true; bool using_subpass_post_process = true; + RendererRD::MaterialStorage::Samplers samplers; bool using_shadows = true; @@ -725,6 +788,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color reverse_cull = true; using_subpass_transparent = true; // we ignore our screen/depth texture here using_subpass_post_process = false; // not applicable at all for reflection probes. + samplers = RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default(); } else if (rb_data.is_valid()) { // setup rendering to render buffer screen_size = p_render_data->render_buffers->get_internal_size(); @@ -753,6 +817,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color // only opaque and sky as subpasses framebuffer = rb_data->get_color_fbs(RenderBufferDataForwardMobile::FB_CONFIG_TWO_SUBPASSES); } + samplers = rb->get_samplers(); } else { ERR_FAIL(); //bug? } @@ -765,11 +830,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color _setup_environment(p_render_data, is_reflection_probe, screen_size, !is_reflection_probe, p_default_bg_color, false); // May have changed due to the above (light buffer enlarged, as an example). - if (is_reflection_probe) { - _update_render_base_uniform_set(RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default(), BASE_UNIFORM_SET_CACHE_DEFAULT); - } else { - _update_render_base_uniform_set(rb->get_samplers(), BASE_UNIFORM_SET_CACHE_VIEWPORT); - } + _update_render_base_uniform_set(); RD::get_singleton()->draw_command_end_label(); // Render Setup @@ -908,11 +969,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color p_render_data->scene_data->directional_light_count = p_render_data->directional_light_count; // Shadow pass can change the base uniform set samplers. - if (is_reflection_probe) { - _update_render_base_uniform_set(RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default(), BASE_UNIFORM_SET_CACHE_DEFAULT); - } else { - _update_render_base_uniform_set(rb->get_samplers(), BASE_UNIFORM_SET_CACHE_VIEWPORT); - } + _update_render_base_uniform_set(); _setup_environment(p_render_data, is_reflection_probe, screen_size, !is_reflection_probe, p_default_bg_color, p_render_data->render_buffers.is_valid()); @@ -924,7 +981,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color RENDER_TIMESTAMP("Render Opaque"); } - RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_OPAQUE, p_render_data, radiance_texture, true); + RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_OPAQUE, p_render_data, radiance_texture, samplers, true); bool can_continue_color = !using_subpass_transparent && !scene_state.used_screen_texture; bool can_continue_depth = !using_subpass_transparent && !scene_state.used_depth_texture; @@ -1007,7 +1064,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color RD::get_singleton()->draw_command_begin_label("Render Transparent Subpass"); - rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_ALPHA, p_render_data, radiance_texture, true); + rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_ALPHA, p_render_data, radiance_texture, samplers, true); if (using_subpass_transparent) { RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(framebuffer); @@ -1274,7 +1331,7 @@ void RenderForwardMobile::_render_shadow_pass(RID p_light, RID p_shadow_atlas, i void RenderForwardMobile::_render_shadow_begin() { scene_state.shadow_passes.clear(); RD::get_singleton()->draw_command_begin_label("Shadow Setup"); - _update_render_base_uniform_set(RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default(), BASE_UNIFORM_SET_CACHE_DEFAULT); + _update_render_base_uniform_set(); render_list[RENDER_LIST_SECONDARY].clear(); } @@ -1354,7 +1411,7 @@ void RenderForwardMobile::_render_shadow_process() { for (uint32_t i = 0; i < scene_state.shadow_passes.size(); i++) { //render passes need to be configured after instance buffer is done, since they need the latest version SceneState::ShadowPass &shadow_pass = scene_state.shadow_passes[i]; - shadow_pass.rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, RID(), false, i); + shadow_pass.rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, RID(), RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default(), false, i); } RD::get_singleton()->draw_command_end_label(); @@ -1381,7 +1438,7 @@ void RenderForwardMobile::_render_material(const Transform3D &p_cam_transform, c RD::get_singleton()->draw_command_begin_label("Render 3D Material"); - _update_render_base_uniform_set(RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default(), BASE_UNIFORM_SET_CACHE_DEFAULT); + _update_render_base_uniform_set(); RenderSceneDataRD scene_data; scene_data.cam_projection = p_cam_projection; @@ -1405,7 +1462,7 @@ void RenderForwardMobile::_render_material(const Transform3D &p_cam_transform, c render_list[RENDER_LIST_SECONDARY].sort_by_key(); _fill_instance_data(RENDER_LIST_SECONDARY); - RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, RID()); + RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, RID(), RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default()); RENDER_TIMESTAMP("Render 3D Material"); @@ -1432,7 +1489,7 @@ void RenderForwardMobile::_render_uv2(const PagedArray<RenderGeometryInstance *> RD::get_singleton()->draw_command_begin_label("Render UV2"); - _update_render_base_uniform_set(RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default(), BASE_UNIFORM_SET_CACHE_DEFAULT); + _update_render_base_uniform_set(); RenderSceneDataRD scene_data; scene_data.dual_paraboloid_side = 0; @@ -1450,7 +1507,7 @@ void RenderForwardMobile::_render_uv2(const PagedArray<RenderGeometryInstance *> render_list[RENDER_LIST_SECONDARY].sort_by_key(); _fill_instance_data(RENDER_LIST_SECONDARY); - RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, RID()); + RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, RID(), RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default()); RENDER_TIMESTAMP("Render 3D Material"); @@ -1506,7 +1563,7 @@ void RenderForwardMobile::_render_particle_collider_heightfield(RID p_fb, const RD::get_singleton()->draw_command_begin_label("Render Collider Heightfield"); - _update_render_base_uniform_set(RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default(), BASE_UNIFORM_SET_CACHE_DEFAULT); + _update_render_base_uniform_set(); RenderSceneDataRD scene_data; scene_data.cam_projection = p_cam_projection; @@ -1531,7 +1588,7 @@ void RenderForwardMobile::_render_particle_collider_heightfield(RID p_fb, const render_list[RENDER_LIST_SECONDARY].sort_by_key(); _fill_instance_data(RENDER_LIST_SECONDARY); - RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, RID()); + RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, RID(), RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default()); RENDER_TIMESTAMP("Render Collider Heightfield"); @@ -1544,23 +1601,21 @@ void RenderForwardMobile::_render_particle_collider_heightfield(RID p_fb, const } void RenderForwardMobile::base_uniforms_changed() { - for (int i = 0; i < BASE_UNIFORM_SET_CACHE_MAX; i++) { - if (!render_base_uniform_set_cache[i].is_null() && RD::get_singleton()->uniform_set_is_valid(render_base_uniform_set_cache[i])) { - RD::get_singleton()->free(render_base_uniform_set_cache[i]); - } - render_base_uniform_set_cache[i] = RID(); + if (!render_base_uniform_set.is_null() && RD::get_singleton()->uniform_set_is_valid(render_base_uniform_set)) { + RD::get_singleton()->free(render_base_uniform_set); } + render_base_uniform_set = RID(); } -void RenderForwardMobile::_update_render_base_uniform_set(const RendererRD::MaterialStorage::Samplers &p_samplers, BaseUniformSetCache p_cache_index) { +void RenderForwardMobile::_update_render_base_uniform_set() { RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton(); - if (render_base_uniform_set_cache[p_cache_index].is_null() || !RD::get_singleton()->uniform_set_is_valid(render_base_uniform_set_cache[p_cache_index]) || (lightmap_texture_array_version_cache[p_cache_index] != light_storage->lightmap_array_get_version())) { - if (render_base_uniform_set_cache[p_cache_index].is_valid() && RD::get_singleton()->uniform_set_is_valid(render_base_uniform_set_cache[p_cache_index])) { - RD::get_singleton()->free(render_base_uniform_set_cache[p_cache_index]); + if (render_base_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(render_base_uniform_set) || (lightmap_texture_array_version != light_storage->lightmap_array_get_version())) { + if (render_base_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(render_base_uniform_set)) { + RD::get_singleton()->free(render_base_uniform_set); } - lightmap_texture_array_version_cache[p_cache_index] = light_storage->lightmap_array_get_version(); + lightmap_texture_array_version = light_storage->lightmap_array_get_version(); Vector<RD::Uniform> uniforms; @@ -1575,73 +1630,13 @@ void RenderForwardMobile::_update_render_base_uniform_set(const RendererRD::Mate { RD::Uniform u; u.binding = 3; - u.uniform_type = RD::UNIFORM_TYPE_SAMPLER; - RID sampler; - switch (decals_get_filter()) { - case RS::DECAL_FILTER_NEAREST: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - case RS::DECAL_FILTER_LINEAR: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - case RS::DECAL_FILTER_NEAREST_MIPMAPS: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - case RS::DECAL_FILTER_LINEAR_MIPMAPS: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - case RS::DECAL_FILTER_NEAREST_MIPMAPS_ANISOTROPIC: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - case RS::DECAL_FILTER_LINEAR_MIPMAPS_ANISOTROPIC: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - } - - u.append_id(sampler); - uniforms.push_back(u); - } - - { - RD::Uniform u; - u.binding = 4; - u.uniform_type = RD::UNIFORM_TYPE_SAMPLER; - RID sampler; - switch (light_projectors_get_filter()) { - case RS::LIGHT_PROJECTOR_FILTER_NEAREST: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - case RS::LIGHT_PROJECTOR_FILTER_LINEAR: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - case RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - case RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - case RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS_ANISOTROPIC: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - case RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS_ANISOTROPIC: { - sampler = p_samplers.get_sampler(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } break; - } - - u.append_id(sampler); - uniforms.push_back(u); - } - - { - RD::Uniform u; - u.binding = 5; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.append_id(RendererRD::LightStorage::get_singleton()->get_omni_light_buffer()); uniforms.push_back(u); } { RD::Uniform u; - u.binding = 6; + u.binding = 4; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.append_id(RendererRD::LightStorage::get_singleton()->get_spot_light_buffer()); uniforms.push_back(u); @@ -1649,35 +1644,35 @@ void RenderForwardMobile::_update_render_base_uniform_set(const RendererRD::Mate { RD::Uniform u; - u.binding = 7; + u.binding = 5; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.append_id(RendererRD::LightStorage::get_singleton()->get_reflection_probe_buffer()); uniforms.push_back(u); } { RD::Uniform u; - u.binding = 8; + u.binding = 6; u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; u.append_id(RendererRD::LightStorage::get_singleton()->get_directional_light_buffer()); uniforms.push_back(u); } { RD::Uniform u; - u.binding = 9; + u.binding = 7; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.append_id(scene_state.lightmap_buffer); uniforms.push_back(u); } { RD::Uniform u; - u.binding = 10; + u.binding = 8; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.append_id(scene_state.lightmap_capture_buffer); uniforms.push_back(u); } { RD::Uniform u; - u.binding = 11; + u.binding = 9; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID decal_atlas = RendererRD::TextureStorage::get_singleton()->decal_atlas_get_texture(); u.append_id(decal_atlas); @@ -1685,7 +1680,7 @@ void RenderForwardMobile::_update_render_base_uniform_set(const RendererRD::Mate } { RD::Uniform u; - u.binding = 12; + u.binding = 10; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID decal_atlas = RendererRD::TextureStorage::get_singleton()->decal_atlas_get_texture_srgb(); u.append_id(decal_atlas); @@ -1693,7 +1688,7 @@ void RenderForwardMobile::_update_render_base_uniform_set(const RendererRD::Mate } { RD::Uniform u; - u.binding = 13; + u.binding = 11; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.append_id(RendererRD::TextureStorage::get_singleton()->get_decal_buffer()); uniforms.push_back(u); @@ -1702,16 +1697,14 @@ void RenderForwardMobile::_update_render_base_uniform_set(const RendererRD::Mate { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; - u.binding = 14; + u.binding = 12; u.append_id(RendererRD::MaterialStorage::get_singleton()->global_shader_uniforms_get_storage_buffer()); uniforms.push_back(u); } + uniforms.append_array(RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default().get_uniforms(13)); - uniforms.append_array(p_samplers.get_uniforms(SAMPLERS_BINDING_FIRST_INDEX)); - - render_base_uniform_set_cache[p_cache_index] = RD::get_singleton()->uniform_set_create(uniforms, scene_shader.default_shader_rd, SCENE_UNIFORM_SET); + render_base_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, scene_shader.default_shader_rd, SCENE_UNIFORM_SET); } - render_base_uniform_set = render_base_uniform_set_cache[p_cache_index]; } RID RenderForwardMobile::_render_buffers_get_normal_texture(Ref<RenderSceneBuffersRD> p_render_buffers) { @@ -2864,7 +2857,6 @@ RenderForwardMobile::RenderForwardMobile() { } { defines += "\n#define MATERIAL_UNIFORM_SET " + itos(MATERIAL_UNIFORM_SET) + "\n"; - defines += "\n#define SAMPLERS_BINDING_FIRST_INDEX " + itos(SAMPLERS_BINDING_FIRST_INDEX) + "\n"; } #ifdef REAL_T_IS_DOUBLE { diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h index f10d3c1568..c393e87bd0 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h @@ -59,8 +59,6 @@ private: MATERIAL_UNIFORM_SET = 3, }; - const int SAMPLERS_BINDING_FIRST_INDEX = 15; - enum { SPEC_CONSTANT_USING_PROJECTOR = 0, @@ -195,22 +193,12 @@ private: /* Render Scene */ - RID _setup_render_pass_uniform_set(RenderListType p_render_list, const RenderDataRD *p_render_data, RID p_radiance_texture, bool p_use_directional_shadow_atlas = false, int p_index = 0); + RID _setup_render_pass_uniform_set(RenderListType p_render_list, const RenderDataRD *p_render_data, RID p_radiance_texture, const RendererRD::MaterialStorage::Samplers &p_samplers, bool p_use_directional_shadow_atlas = false, int p_index = 0); void _pre_opaque_render(RenderDataRD *p_render_data); - enum BaseUniformSetCache { - BASE_UNIFORM_SET_CACHE_VIEWPORT, - BASE_UNIFORM_SET_CACHE_DEFAULT, - BASE_UNIFORM_SET_CACHE_MAX - }; - - // One for custom samplers, one for default samplers. - // Need to switch between them as default is needed for probes, shadows, materials, etc. - RID render_base_uniform_set_cache[BASE_UNIFORM_SET_CACHE_MAX]; - - uint64_t lightmap_texture_array_version_cache[BASE_UNIFORM_SET_CACHE_MAX] = { 0xFFFFFFFF, 0xFFFFFFFF }; + uint64_t lightmap_texture_array_version = 0xFFFFFFFF; - void _update_render_base_uniform_set(const RendererRD::MaterialStorage::Samplers &p_samplers, BaseUniformSetCache p_cache_index); + void _update_render_base_uniform_set(); void _update_instance_data_buffer(RenderListType p_render_list); void _fill_instance_data(RenderListType p_render_list, uint32_t p_offset = 0, int32_t p_max_elements = -1, bool p_update_buffer = true); diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp index 6ec9f0768a..6cb8a71019 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp @@ -1981,8 +1981,7 @@ void RendererCanvasRenderRD::occluder_polygon_set_shape(RID p_occluder, const Ve if (oc->vertex_array.is_null()) { //create from scratch //vertices - // TODO: geometry is always of length lc * 6 * sizeof(float), so in doubles builds this will receive half the data it needs - oc->vertex_buffer = RD::get_singleton()->vertex_buffer_create(lc * 6 * sizeof(real_t), geometry); + oc->vertex_buffer = RD::get_singleton()->vertex_buffer_create(lc * 6 * sizeof(float), geometry); Vector<RID> buffer; buffer.push_back(oc->vertex_buffer); @@ -2043,7 +2042,18 @@ void RendererCanvasRenderRD::occluder_polygon_set_shape(RID p_occluder, const Ve if (oc->sdf_vertex_array.is_null()) { //create from scratch //vertices - oc->sdf_vertex_buffer = RD::get_singleton()->vertex_buffer_create(p_points.size() * 2 * sizeof(real_t), p_points.to_byte_array()); +#ifdef REAL_T_IS_DOUBLE + PackedFloat32Array float_points; + float_points.resize(p_points.size() * 2); + float *float_points_ptr = (float *)float_points.ptrw(); + for (int i = 0; i < p_points.size(); i++) { + float_points_ptr[i * 2] = p_points[i].x; + float_points_ptr[i * 2 + 1] = p_points[i].y; + } + oc->sdf_vertex_buffer = RD::get_singleton()->vertex_buffer_create(p_points.size() * 2 * sizeof(float), float_points.to_byte_array()); +#else + oc->sdf_vertex_buffer = RD::get_singleton()->vertex_buffer_create(p_points.size() * 2 * sizeof(float), p_points.to_byte_array()); +#endif oc->sdf_index_buffer = RD::get_singleton()->index_buffer_create(sdf_indices.size(), RD::INDEX_BUFFER_FORMAT_UINT32, sdf_indices.to_byte_array()); oc->sdf_index_array = RD::get_singleton()->index_array_create(oc->sdf_index_buffer, 0, sdf_indices.size()); @@ -2054,7 +2064,18 @@ void RendererCanvasRenderRD::occluder_polygon_set_shape(RID p_occluder, const Ve } else { //update existing - RD::get_singleton()->buffer_update(oc->sdf_vertex_buffer, 0, sizeof(real_t) * 2 * p_points.size(), p_points.ptr()); +#ifdef REAL_T_IS_DOUBLE + PackedFloat32Array float_points; + float_points.resize(p_points.size() * 2); + float *float_points_ptr = (float *)float_points.ptrw(); + for (int i = 0; i < p_points.size(); i++) { + float_points_ptr[i * 2] = p_points[i].x; + float_points_ptr[i * 2 + 1] = p_points[i].y; + } + RD::get_singleton()->buffer_update(oc->sdf_vertex_buffer, 0, sizeof(float) * 2 * p_points.size(), float_points.ptr()); +#else + RD::get_singleton()->buffer_update(oc->sdf_vertex_buffer, 0, sizeof(float) * 2 * p_points.size(), p_points.ptr()); +#endif RD::get_singleton()->buffer_update(oc->sdf_index_buffer, 0, sdf_indices.size() * sizeof(int32_t), sdf_indices.ptr()); } } @@ -2578,15 +2599,15 @@ RendererCanvasRenderRD::RendererCanvasRenderRD() { //pipelines Vector<RD::VertexAttribute> vf; RD::VertexAttribute vd; - vd.format = sizeof(real_t) == sizeof(float) ? RD::DATA_FORMAT_R32G32B32_SFLOAT : RD::DATA_FORMAT_R64G64B64_SFLOAT; + vd.format = RD::DATA_FORMAT_R32G32B32_SFLOAT; + vd.stride = sizeof(float) * 3; vd.location = 0; vd.offset = 0; - vd.stride = sizeof(real_t) * 3; vf.push_back(vd); shadow_render.vertex_format = RD::get_singleton()->vertex_format_create(vf); - vd.format = sizeof(real_t) == sizeof(float) ? RD::DATA_FORMAT_R32G32_SFLOAT : RD::DATA_FORMAT_R64G64_SFLOAT; - vd.stride = sizeof(real_t) * 2; + vd.format = RD::DATA_FORMAT_R32G32_SFLOAT; + vd.stride = sizeof(float) * 2; vf.write[0] = vd; shadow_render.sdf_vertex_format = RD::get_singleton()->vertex_format_create(vf); diff --git a/servers/rendering/renderer_rd/renderer_compositor_rd.cpp b/servers/rendering/renderer_rd/renderer_compositor_rd.cpp index b9bda9329e..49c4d08b86 100644 --- a/servers/rendering/renderer_rd/renderer_compositor_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_compositor_rd.cpp @@ -310,15 +310,16 @@ RendererCompositorRD::RendererCompositorRD() { uint64_t textures_per_stage = RD::get_singleton()->limit_get(RD::LIMIT_MAX_TEXTURES_PER_SHADER_STAGE); if (rendering_method == "mobile" || textures_per_stage < 48) { - scene = memnew(RendererSceneRenderImplementation::RenderForwardMobile()); if (rendering_method == "forward_plus") { WARN_PRINT_ONCE("Platform supports less than 48 textures per stage which is less than required by the Clustered renderer. Defaulting to Mobile renderer."); } + scene = memnew(RendererSceneRenderImplementation::RenderForwardMobile()); } else if (rendering_method == "forward_plus") { - // default to our high end renderer scene = memnew(RendererSceneRenderImplementation::RenderForwardClustered()); } else { - ERR_FAIL_MSG("Cannot instantiate RenderingDevice-based renderer with renderer type " + rendering_method); + // Fall back to our high end renderer. + ERR_PRINT(vformat("Cannot instantiate RenderingDevice-based renderer with renderer type '%s'. Defaulting to Forward+ renderer.", rendering_method)); + scene = memnew(RendererSceneRenderImplementation::RenderForwardClustered()); } scene->init(); diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl index 206c2fb245..604e623005 100644 --- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl @@ -802,7 +802,7 @@ vec4 volumetric_fog_process(vec2 screen_uv, float z) { fog_pos.z = pow(fog_pos.z, implementation_data.volumetric_fog_detail_spread); } - return texture(sampler3D(volumetric_fog_texture, SAMPLER_LINEAR_CLAMP), fog_pos); + return texture(sampler3D(volumetric_fog_texture, DEFAULT_SAMPLER_LINEAR_CLAMP), fog_pos); } vec4 fog_process(vec3 vertex) { @@ -816,10 +816,10 @@ vec4 fog_process(vec3 vertex) { #ifdef USE_RADIANCE_CUBEMAP_ARRAY float lod, blend; blend = modf(mip_level * MAX_ROUGHNESS_LOD, lod); - sky_fog_color = texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(cube_view, lod)).rgb; - sky_fog_color = mix(sky_fog_color, texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(cube_view, lod + 1)).rgb, blend); + sky_fog_color = texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(cube_view, lod)).rgb; + sky_fog_color = mix(sky_fog_color, texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(cube_view, lod + 1)).rgb, blend); #else - sky_fog_color = textureLod(samplerCube(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), cube_view, mip_level * MAX_ROUGHNESS_LOD).rgb; + sky_fog_color = textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), cube_view, mip_level * MAX_ROUGHNESS_LOD).rgb; #endif //USE_RADIANCE_CUBEMAP_ARRAY fog_color = mix(fog_color, sky_fog_color, scene_data_block.data.fog_aerial_perspective); } @@ -1271,11 +1271,11 @@ void fragment_shader(in SceneData scene_data) { float lod, blend; blend = modf(sqrt(roughness) * MAX_ROUGHNESS_LOD, lod); - specular_light = texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod)).rgb; - specular_light = mix(specular_light, texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod + 1)).rgb, blend); + specular_light = texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod)).rgb; + specular_light = mix(specular_light, texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod + 1)).rgb, blend); #else - specular_light = textureLod(samplerCube(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_vec, sqrt(roughness) * MAX_ROUGHNESS_LOD).rgb; + specular_light = textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_vec, sqrt(roughness) * MAX_ROUGHNESS_LOD).rgb; #endif //USE_RADIANCE_CUBEMAP_ARRAY specular_light *= scene_data.IBL_exposure_normalization; @@ -1295,9 +1295,9 @@ void fragment_shader(in SceneData scene_data) { if (scene_data.use_ambient_cubemap) { vec3 ambient_dir = scene_data.radiance_inverse_xform * normal; #ifdef USE_RADIANCE_CUBEMAP_ARRAY - vec3 cubemap_ambient = texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ambient_dir, MAX_ROUGHNESS_LOD)).rgb; + vec3 cubemap_ambient = texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ambient_dir, MAX_ROUGHNESS_LOD)).rgb; #else - vec3 cubemap_ambient = textureLod(samplerCube(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ambient_dir, MAX_ROUGHNESS_LOD).rgb; + vec3 cubemap_ambient = textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ambient_dir, MAX_ROUGHNESS_LOD).rgb; #endif //USE_RADIANCE_CUBEMAP_ARRAY cubemap_ambient *= scene_data.IBL_exposure_normalization; ambient_light = mix(ambient_light, cubemap_ambient * scene_data.ambient_light_color_energy.a, scene_data.ambient_color_sky_mix); @@ -1328,11 +1328,11 @@ void fragment_shader(in SceneData scene_data) { float lod, blend; blend = modf(roughness_lod, lod); - vec3 clearcoat_light = texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod)).rgb; - clearcoat_light = mix(clearcoat_light, texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod + 1)).rgb, blend); + vec3 clearcoat_light = texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod)).rgb; + clearcoat_light = mix(clearcoat_light, texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod + 1)).rgb, blend); #else - vec3 clearcoat_light = textureLod(samplerCube(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_vec, roughness_lod).rgb; + vec3 clearcoat_light = textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_vec, roughness_lod).rgb; #endif //USE_RADIANCE_CUBEMAP_ARRAY specular_light += clearcoat_light * horizon * horizon * Fc * scene_data.ambient_light_color_energy.a; @@ -1379,10 +1379,10 @@ void fragment_shader(in SceneData scene_data) { if (uses_sh) { uvw.z *= 4.0; //SH textures use 4 times more data - vec3 lm_light_l0 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 0.0), 0.0).rgb; - vec3 lm_light_l1n1 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 1.0), 0.0).rgb; - vec3 lm_light_l1_0 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 2.0), 0.0).rgb; - vec3 lm_light_l1p1 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 3.0), 0.0).rgb; + vec3 lm_light_l0 = textureLod(sampler2DArray(lightmap_textures[ofs], DEFAULT_SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 0.0), 0.0).rgb; + vec3 lm_light_l1n1 = textureLod(sampler2DArray(lightmap_textures[ofs], DEFAULT_SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 1.0), 0.0).rgb; + vec3 lm_light_l1_0 = textureLod(sampler2DArray(lightmap_textures[ofs], DEFAULT_SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 2.0), 0.0).rgb; + vec3 lm_light_l1p1 = textureLod(sampler2DArray(lightmap_textures[ofs], DEFAULT_SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 3.0), 0.0).rgb; vec3 n = normalize(lightmaps.data[ofs].normal_xform * normal); float en = lightmaps.data[ofs].exposure_normalization; @@ -1399,7 +1399,7 @@ void fragment_shader(in SceneData scene_data) { } } else { - ambient_light += textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw, 0.0).rgb * lightmaps.data[ofs].exposure_normalization; + ambient_light += textureLod(sampler2DArray(lightmap_textures[ofs], DEFAULT_SAMPLER_LINEAR_CLAMP), uvw, 0.0).rgb * lightmaps.data[ofs].exposure_normalization; } } #else @@ -1519,18 +1519,18 @@ void fragment_shader(in SceneData scene_data) { vec2 base_coord = screen_uv; vec2 closest_coord = base_coord; #ifdef USE_MULTIVIEW - float closest_ang = dot(normal, textureLod(sampler2DArray(normal_roughness_buffer, SAMPLER_LINEAR_CLAMP), vec3(base_coord, ViewIndex), 0.0).xyz * 2.0 - 1.0); + float closest_ang = dot(normal, textureLod(sampler2DArray(normal_roughness_buffer, DEFAULT_SAMPLER_LINEAR_CLAMP), vec3(base_coord, ViewIndex), 0.0).xyz * 2.0 - 1.0); #else // USE_MULTIVIEW - float closest_ang = dot(normal, textureLod(sampler2D(normal_roughness_buffer, SAMPLER_LINEAR_CLAMP), base_coord, 0.0).xyz * 2.0 - 1.0); + float closest_ang = dot(normal, textureLod(sampler2D(normal_roughness_buffer, DEFAULT_SAMPLER_LINEAR_CLAMP), base_coord, 0.0).xyz * 2.0 - 1.0); #endif // USE_MULTIVIEW for (int i = 0; i < 4; i++) { const vec2 neighbors[4] = vec2[](vec2(-1, 0), vec2(1, 0), vec2(0, -1), vec2(0, 1)); vec2 neighbour_coord = base_coord + neighbors[i] * scene_data.screen_pixel_size; #ifdef USE_MULTIVIEW - float neighbour_ang = dot(normal, textureLod(sampler2DArray(normal_roughness_buffer, SAMPLER_LINEAR_CLAMP), vec3(neighbour_coord, ViewIndex), 0.0).xyz * 2.0 - 1.0); + float neighbour_ang = dot(normal, textureLod(sampler2DArray(normal_roughness_buffer, DEFAULT_SAMPLER_LINEAR_CLAMP), vec3(neighbour_coord, ViewIndex), 0.0).xyz * 2.0 - 1.0); #else // USE_MULTIVIEW - float neighbour_ang = dot(normal, textureLod(sampler2D(normal_roughness_buffer, SAMPLER_LINEAR_CLAMP), neighbour_coord, 0.0).xyz * 2.0 - 1.0); + float neighbour_ang = dot(normal, textureLod(sampler2D(normal_roughness_buffer, DEFAULT_SAMPLER_LINEAR_CLAMP), neighbour_coord, 0.0).xyz * 2.0 - 1.0); #endif // USE_MULTIVIEW if (neighbour_ang > closest_ang) { closest_ang = neighbour_ang; @@ -1545,11 +1545,11 @@ void fragment_shader(in SceneData scene_data) { } #ifdef USE_MULTIVIEW - vec4 buffer_ambient = textureLod(sampler2DArray(ambient_buffer, SAMPLER_LINEAR_CLAMP), vec3(coord, ViewIndex), 0.0); - vec4 buffer_reflection = textureLod(sampler2DArray(reflection_buffer, SAMPLER_LINEAR_CLAMP), vec3(coord, ViewIndex), 0.0); + vec4 buffer_ambient = textureLod(sampler2DArray(ambient_buffer, DEFAULT_SAMPLER_LINEAR_CLAMP), vec3(coord, ViewIndex), 0.0); + vec4 buffer_reflection = textureLod(sampler2DArray(reflection_buffer, DEFAULT_SAMPLER_LINEAR_CLAMP), vec3(coord, ViewIndex), 0.0); #else // USE_MULTIVIEW - vec4 buffer_ambient = textureLod(sampler2D(ambient_buffer, SAMPLER_LINEAR_CLAMP), coord, 0.0); - vec4 buffer_reflection = textureLod(sampler2D(reflection_buffer, SAMPLER_LINEAR_CLAMP), coord, 0.0); + vec4 buffer_ambient = textureLod(sampler2D(ambient_buffer, DEFAULT_SAMPLER_LINEAR_CLAMP), coord, 0.0); + vec4 buffer_reflection = textureLod(sampler2D(reflection_buffer, DEFAULT_SAMPLER_LINEAR_CLAMP), coord, 0.0); #endif // USE_MULTIVIEW ambient_light = mix(ambient_light, buffer_ambient.rgb, buffer_ambient.a); @@ -1559,9 +1559,9 @@ void fragment_shader(in SceneData scene_data) { if (bool(implementation_data.ss_effects_flags & SCREEN_SPACE_EFFECTS_FLAGS_USE_SSAO)) { #ifdef USE_MULTIVIEW - float ssao = texture(sampler2DArray(ao_buffer, SAMPLER_LINEAR_CLAMP), vec3(screen_uv, ViewIndex)).r; + float ssao = texture(sampler2DArray(ao_buffer, DEFAULT_SAMPLER_LINEAR_CLAMP), vec3(screen_uv, ViewIndex)).r; #else - float ssao = texture(sampler2D(ao_buffer, SAMPLER_LINEAR_CLAMP), screen_uv).r; + float ssao = texture(sampler2D(ao_buffer, DEFAULT_SAMPLER_LINEAR_CLAMP), screen_uv).r; #endif ao = min(ao, ssao); ao_light_affect = mix(ao_light_affect, max(ao_light_affect, implementation_data.ssao_light_affect), implementation_data.ssao_ao_affect); @@ -1646,9 +1646,9 @@ void fragment_shader(in SceneData scene_data) { if (bool(implementation_data.ss_effects_flags & SCREEN_SPACE_EFFECTS_FLAGS_USE_SSIL)) { #ifdef USE_MULTIVIEW - vec4 ssil = textureLod(sampler2DArray(ssil_buffer, SAMPLER_LINEAR_CLAMP), vec3(screen_uv, ViewIndex), 0.0); + vec4 ssil = textureLod(sampler2DArray(ssil_buffer, DEFAULT_SAMPLER_LINEAR_CLAMP), vec3(screen_uv, ViewIndex), 0.0); #else - vec4 ssil = textureLod(sampler2D(ssil_buffer, SAMPLER_LINEAR_CLAMP), screen_uv, 0.0); + vec4 ssil = textureLod(sampler2D(ssil_buffer, DEFAULT_SAMPLER_LINEAR_CLAMP), screen_uv, 0.0); #endif // USE_MULTIVIEW ambient_light *= 1.0 - ssil.a; ambient_light += ssil.rgb * albedo.rgb; @@ -1932,7 +1932,7 @@ void fragment_shader(in SceneData scene_data) { vec4 trans_coord = directional_lights.data[i].shadow_matrix1 * trans_vertex; trans_coord /= trans_coord.w; - float shadow_z = textureLod(sampler2D(directional_shadow_atlas, SAMPLER_LINEAR_CLAMP), trans_coord.xy, 0.0).r; + float shadow_z = textureLod(sampler2D(directional_shadow_atlas, DEFAULT_SAMPLER_LINEAR_CLAMP), trans_coord.xy, 0.0).r; shadow_z *= directional_lights.data[i].shadow_z_range.x; float z = trans_coord.z * directional_lights.data[i].shadow_z_range.x; @@ -1942,7 +1942,7 @@ void fragment_shader(in SceneData scene_data) { vec4 trans_coord = directional_lights.data[i].shadow_matrix2 * trans_vertex; trans_coord /= trans_coord.w; - float shadow_z = textureLod(sampler2D(directional_shadow_atlas, SAMPLER_LINEAR_CLAMP), trans_coord.xy, 0.0).r; + float shadow_z = textureLod(sampler2D(directional_shadow_atlas, DEFAULT_SAMPLER_LINEAR_CLAMP), trans_coord.xy, 0.0).r; shadow_z *= directional_lights.data[i].shadow_z_range.y; float z = trans_coord.z * directional_lights.data[i].shadow_z_range.y; @@ -1952,7 +1952,7 @@ void fragment_shader(in SceneData scene_data) { vec4 trans_coord = directional_lights.data[i].shadow_matrix3 * trans_vertex; trans_coord /= trans_coord.w; - float shadow_z = textureLod(sampler2D(directional_shadow_atlas, SAMPLER_LINEAR_CLAMP), trans_coord.xy, 0.0).r; + float shadow_z = textureLod(sampler2D(directional_shadow_atlas, DEFAULT_SAMPLER_LINEAR_CLAMP), trans_coord.xy, 0.0).r; shadow_z *= directional_lights.data[i].shadow_z_range.z; float z = trans_coord.z * directional_lights.data[i].shadow_z_range.z; @@ -1963,7 +1963,7 @@ void fragment_shader(in SceneData scene_data) { vec4 trans_coord = directional_lights.data[i].shadow_matrix4 * trans_vertex; trans_coord /= trans_coord.w; - float shadow_z = textureLod(sampler2D(directional_shadow_atlas, SAMPLER_LINEAR_CLAMP), trans_coord.xy, 0.0).r; + float shadow_z = textureLod(sampler2D(directional_shadow_atlas, DEFAULT_SAMPLER_LINEAR_CLAMP), trans_coord.xy, 0.0).r; shadow_z *= directional_lights.data[i].shadow_z_range.w; float z = trans_coord.z * directional_lights.data[i].shadow_z_range.w; diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl index bfd87b4ea1..78d7f15267 100644 --- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl @@ -46,14 +46,8 @@ draw_call; #include "../light_data_inc.glsl" -#include "../samplers_inc.glsl" - layout(set = 0, binding = 2) uniform sampler shadow_sampler; -layout(set = 0, binding = 3) uniform sampler decal_sampler; - -layout(set = 0, binding = 4) uniform sampler light_projector_sampler; - #define INSTANCE_FLAGS_NON_UNIFORM_SCALE (1 << 4) #define INSTANCE_FLAGS_USE_GI_BUFFERS (1 << 5) #define INSTANCE_FLAGS_USE_SDFGI (1 << 6) @@ -74,22 +68,22 @@ layout(set = 0, binding = 4) uniform sampler light_projector_sampler; #define SCREEN_SPACE_EFFECTS_FLAGS_USE_SSAO 1 #define SCREEN_SPACE_EFFECTS_FLAGS_USE_SSIL 2 -layout(set = 0, binding = 5, std430) restrict readonly buffer OmniLights { +layout(set = 0, binding = 3, std430) restrict readonly buffer OmniLights { LightData data[]; } omni_lights; -layout(set = 0, binding = 6, std430) restrict readonly buffer SpotLights { +layout(set = 0, binding = 4, std430) restrict readonly buffer SpotLights { LightData data[]; } spot_lights; -layout(set = 0, binding = 7, std430) restrict readonly buffer ReflectionProbeData { +layout(set = 0, binding = 5, std430) restrict readonly buffer ReflectionProbeData { ReflectionData data[]; } reflections; -layout(set = 0, binding = 8, std140) uniform DirectionalLights { +layout(set = 0, binding = 6, std140) uniform DirectionalLights { DirectionalLightData data[MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS]; } directional_lights; @@ -103,7 +97,7 @@ struct Lightmap { float exposure_normalization; }; -layout(set = 0, binding = 9, std140) restrict readonly buffer Lightmaps { +layout(set = 0, binding = 7, std140) restrict readonly buffer Lightmaps { Lightmap data[]; } lightmaps; @@ -112,20 +106,20 @@ struct LightmapCapture { vec4 sh[9]; }; -layout(set = 0, binding = 10, std140) restrict readonly buffer LightmapCaptures { +layout(set = 0, binding = 8, std140) restrict readonly buffer LightmapCaptures { LightmapCapture data[]; } lightmap_captures; -layout(set = 0, binding = 11) uniform texture2D decal_atlas; -layout(set = 0, binding = 12) uniform texture2D decal_atlas_srgb; +layout(set = 0, binding = 9) uniform texture2D decal_atlas; +layout(set = 0, binding = 10) uniform texture2D decal_atlas_srgb; -layout(set = 0, binding = 13, std430) restrict readonly buffer Decals { +layout(set = 0, binding = 11, std430) restrict readonly buffer Decals { DecalData data[]; } decals; -layout(set = 0, binding = 14, std430) restrict readonly buffer GlobalShaderUniformData { +layout(set = 0, binding = 12, std430) restrict readonly buffer GlobalShaderUniformData { vec4 data[]; } global_shader_uniforms; @@ -139,7 +133,7 @@ struct SDFVoxelGICascadeData { float exposure_normalization; }; -layout(set = 0, binding = 15, std140) uniform SDFGI { +layout(set = 0, binding = 13, std140) uniform SDFGI { vec3 grid_size; uint max_cascades; @@ -167,6 +161,19 @@ layout(set = 0, binding = 15, std140) uniform SDFGI { } sdfgi; +layout(set = 0, binding = 14 + 0) uniform sampler DEFAULT_SAMPLER_NEAREST_CLAMP; +layout(set = 0, binding = 14 + 1) uniform sampler DEFAULT_SAMPLER_LINEAR_CLAMP; +layout(set = 0, binding = 14 + 2) uniform sampler DEFAULT_SAMPLER_NEAREST_WITH_MIPMAPS_CLAMP; +layout(set = 0, binding = 14 + 3) uniform sampler DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP; +layout(set = 0, binding = 14 + 4) uniform sampler DEFAULT_SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_CLAMP; +layout(set = 0, binding = 14 + 5) uniform sampler DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_CLAMP; +layout(set = 0, binding = 14 + 6) uniform sampler DEFAULT_SAMPLER_NEAREST_REPEAT; +layout(set = 0, binding = 14 + 7) uniform sampler DEFAULT_SAMPLER_LINEAR_REPEAT; +layout(set = 0, binding = 14 + 8) uniform sampler DEFAULT_SAMPLER_NEAREST_WITH_MIPMAPS_REPEAT; +layout(set = 0, binding = 14 + 9) uniform sampler DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_REPEAT; +layout(set = 0, binding = 14 + 10) uniform sampler DEFAULT_SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_REPEAT; +layout(set = 0, binding = 14 + 11) uniform sampler DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_REPEAT; + /* Set 1: Render Pass (changes per render pass) */ layout(set = 1, binding = 0, std140) uniform SceneDataBlock { @@ -250,12 +257,29 @@ layout(set = 1, binding = 9, std430) buffer restrict readonly ClusterBuffer { } cluster_buffer; +layout(set = 1, binding = 10) uniform sampler decal_sampler; + +layout(set = 1, binding = 11) uniform sampler light_projector_sampler; + +layout(set = 1, binding = 12 + 0) uniform sampler SAMPLER_NEAREST_CLAMP; +layout(set = 1, binding = 12 + 1) uniform sampler SAMPLER_LINEAR_CLAMP; +layout(set = 1, binding = 12 + 2) uniform sampler SAMPLER_NEAREST_WITH_MIPMAPS_CLAMP; +layout(set = 1, binding = 12 + 3) uniform sampler SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP; +layout(set = 1, binding = 12 + 4) uniform sampler SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_CLAMP; +layout(set = 1, binding = 12 + 5) uniform sampler SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_CLAMP; +layout(set = 1, binding = 12 + 6) uniform sampler SAMPLER_NEAREST_REPEAT; +layout(set = 1, binding = 12 + 7) uniform sampler SAMPLER_LINEAR_REPEAT; +layout(set = 1, binding = 12 + 8) uniform sampler SAMPLER_NEAREST_WITH_MIPMAPS_REPEAT; +layout(set = 1, binding = 12 + 9) uniform sampler SAMPLER_LINEAR_WITH_MIPMAPS_REPEAT; +layout(set = 1, binding = 12 + 10) uniform sampler SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_REPEAT; +layout(set = 1, binding = 12 + 11) uniform sampler SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_REPEAT; + #ifdef MODE_RENDER_SDF -layout(r16ui, set = 1, binding = 10) uniform restrict writeonly uimage3D albedo_volume_grid; -layout(r32ui, set = 1, binding = 11) uniform restrict writeonly uimage3D emission_grid; -layout(r32ui, set = 1, binding = 12) uniform restrict writeonly uimage3D emission_aniso_grid; -layout(r32ui, set = 1, binding = 13) uniform restrict uimage3D geom_facing_grid; +layout(r16ui, set = 1, binding = 24) uniform restrict writeonly uimage3D albedo_volume_grid; +layout(r32ui, set = 1, binding = 25) uniform restrict writeonly uimage3D emission_grid; +layout(r32ui, set = 1, binding = 26) uniform restrict writeonly uimage3D emission_aniso_grid; +layout(r32ui, set = 1, binding = 27) uniform restrict uimage3D geom_facing_grid; //still need to be present for shaders that use it, so remap them to something #define depth_buffer shadow_atlas @@ -266,24 +290,24 @@ layout(r32ui, set = 1, binding = 13) uniform restrict uimage3D geom_facing_grid; #else #ifdef USE_MULTIVIEW -layout(set = 1, binding = 10) uniform texture2DArray depth_buffer; -layout(set = 1, binding = 11) uniform texture2DArray color_buffer; -layout(set = 1, binding = 12) uniform texture2DArray normal_roughness_buffer; -layout(set = 1, binding = 13) uniform texture2DArray ao_buffer; -layout(set = 1, binding = 14) uniform texture2DArray ambient_buffer; -layout(set = 1, binding = 15) uniform texture2DArray reflection_buffer; +layout(set = 1, binding = 24) uniform texture2DArray depth_buffer; +layout(set = 1, binding = 25) uniform texture2DArray color_buffer; +layout(set = 1, binding = 26) uniform texture2DArray normal_roughness_buffer; +layout(set = 1, binding = 27) uniform texture2DArray ao_buffer; +layout(set = 1, binding = 28) uniform texture2DArray ambient_buffer; +layout(set = 1, binding = 29) uniform texture2DArray reflection_buffer; #define multiviewSampler sampler2DArray #else // USE_MULTIVIEW -layout(set = 1, binding = 10) uniform texture2D depth_buffer; -layout(set = 1, binding = 11) uniform texture2D color_buffer; -layout(set = 1, binding = 12) uniform texture2D normal_roughness_buffer; -layout(set = 1, binding = 13) uniform texture2D ao_buffer; -layout(set = 1, binding = 14) uniform texture2D ambient_buffer; -layout(set = 1, binding = 15) uniform texture2D reflection_buffer; +layout(set = 1, binding = 24) uniform texture2D depth_buffer; +layout(set = 1, binding = 25) uniform texture2D color_buffer; +layout(set = 1, binding = 26) uniform texture2D normal_roughness_buffer; +layout(set = 1, binding = 27) uniform texture2D ao_buffer; +layout(set = 1, binding = 28) uniform texture2D ambient_buffer; +layout(set = 1, binding = 29) uniform texture2D reflection_buffer; #define multiviewSampler sampler2D #endif -layout(set = 1, binding = 16) uniform texture2DArray sdfgi_lightprobe_texture; -layout(set = 1, binding = 17) uniform texture3D sdfgi_occlusion_cascades; +layout(set = 1, binding = 30) uniform texture2DArray sdfgi_lightprobe_texture; +layout(set = 1, binding = 31) uniform texture3D sdfgi_occlusion_cascades; struct VoxelGIData { mat4 xform; // 64 - 64 @@ -300,17 +324,17 @@ struct VoxelGIData { float exposure_normalization; // 4 - 112 }; -layout(set = 1, binding = 18, std140) uniform VoxelGIs { +layout(set = 1, binding = 32, std140) uniform VoxelGIs { VoxelGIData data[MAX_VOXEL_GI_INSTANCES]; } voxel_gi_instances; -layout(set = 1, binding = 19) uniform texture3D volumetric_fog_texture; +layout(set = 1, binding = 33) uniform texture3D volumetric_fog_texture; #ifdef USE_MULTIVIEW -layout(set = 1, binding = 20) uniform texture2DArray ssil_buffer; +layout(set = 1, binding = 34) uniform texture2DArray ssil_buffer; #else -layout(set = 1, binding = 20) uniform texture2D ssil_buffer; +layout(set = 1, binding = 34) uniform texture2D ssil_buffer; #endif // USE_MULTIVIEW #endif diff --git a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl index 5ed3669703..a3774f0f1c 100644 --- a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl @@ -658,10 +658,10 @@ vec4 fog_process(vec3 vertex) { #ifdef USE_RADIANCE_CUBEMAP_ARRAY float lod, blend; blend = modf(mip_level * MAX_ROUGHNESS_LOD, lod); - sky_fog_color = texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(cube_view, lod)).rgb; - sky_fog_color = mix(sky_fog_color, texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(cube_view, lod + 1)).rgb, blend); + sky_fog_color = texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(cube_view, lod)).rgb; + sky_fog_color = mix(sky_fog_color, texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(cube_view, lod + 1)).rgb, blend); #else - sky_fog_color = textureLod(samplerCube(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), cube_view, mip_level * MAX_ROUGHNESS_LOD).rgb; + sky_fog_color = textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), cube_view, mip_level * MAX_ROUGHNESS_LOD).rgb; #endif //USE_RADIANCE_CUBEMAP_ARRAY fog_color = mix(fog_color, sky_fog_color, scene_data_block.data.fog_aerial_perspective); } @@ -1051,11 +1051,11 @@ void main() { float lod, blend; blend = modf(sqrt(roughness) * MAX_ROUGHNESS_LOD, lod); - specular_light = texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod)).rgb; - specular_light = mix(specular_light, texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod + 1)).rgb, blend); + specular_light = texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod)).rgb; + specular_light = mix(specular_light, texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod + 1)).rgb, blend); #else // USE_RADIANCE_CUBEMAP_ARRAY - specular_light = textureLod(samplerCube(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_vec, sqrt(roughness) * MAX_ROUGHNESS_LOD).rgb; + specular_light = textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_vec, sqrt(roughness) * MAX_ROUGHNESS_LOD).rgb; #endif //USE_RADIANCE_CUBEMAP_ARRAY specular_light *= sc_luminance_multiplier; @@ -1076,9 +1076,9 @@ void main() { if (scene_data.use_ambient_cubemap) { vec3 ambient_dir = scene_data.radiance_inverse_xform * normal; #ifdef USE_RADIANCE_CUBEMAP_ARRAY - vec3 cubemap_ambient = texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ambient_dir, MAX_ROUGHNESS_LOD)).rgb; + vec3 cubemap_ambient = texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ambient_dir, MAX_ROUGHNESS_LOD)).rgb; #else - vec3 cubemap_ambient = textureLod(samplerCube(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ambient_dir, MAX_ROUGHNESS_LOD).rgb; + vec3 cubemap_ambient = textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ambient_dir, MAX_ROUGHNESS_LOD).rgb; #endif //USE_RADIANCE_CUBEMAP_ARRAY cubemap_ambient *= sc_luminance_multiplier; cubemap_ambient *= scene_data.IBL_exposure_normalization; @@ -1110,11 +1110,11 @@ void main() { float lod, blend; blend = modf(roughness_lod, lod); - vec3 clearcoat_light = texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod)).rgb; - clearcoat_light = mix(clearcoat_light, texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod + 1)).rgb, blend); + vec3 clearcoat_light = texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod)).rgb; + clearcoat_light = mix(clearcoat_light, texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod + 1)).rgb, blend); #else - vec3 clearcoat_light = textureLod(samplerCube(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_vec, roughness_lod).rgb; + vec3 clearcoat_light = textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_vec, roughness_lod).rgb; #endif //USE_RADIANCE_CUBEMAP_ARRAY specular_light += clearcoat_light * horizon * horizon * Fc * scene_data.ambient_light_color_energy.a; @@ -1160,10 +1160,10 @@ void main() { if (uses_sh) { uvw.z *= 4.0; //SH textures use 4 times more data - vec3 lm_light_l0 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 0.0), 0.0).rgb; - vec3 lm_light_l1n1 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 1.0), 0.0).rgb; - vec3 lm_light_l1_0 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 2.0), 0.0).rgb; - vec3 lm_light_l1p1 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 3.0), 0.0).rgb; + vec3 lm_light_l0 = textureLod(sampler2DArray(lightmap_textures[ofs], DEFAULT_SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 0.0), 0.0).rgb; + vec3 lm_light_l1n1 = textureLod(sampler2DArray(lightmap_textures[ofs], DEFAULT_SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 1.0), 0.0).rgb; + vec3 lm_light_l1_0 = textureLod(sampler2DArray(lightmap_textures[ofs], DEFAULT_SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 2.0), 0.0).rgb; + vec3 lm_light_l1p1 = textureLod(sampler2DArray(lightmap_textures[ofs], DEFAULT_SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 3.0), 0.0).rgb; vec3 n = normalize(lightmaps.data[ofs].normal_xform * normal); float exposure_normalization = lightmaps.data[ofs].exposure_normalization; @@ -1180,7 +1180,7 @@ void main() { } } else { - ambient_light += textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw, 0.0).rgb * lightmaps.data[ofs].exposure_normalization; + ambient_light += textureLod(sampler2DArray(lightmap_textures[ofs], DEFAULT_SAMPLER_LINEAR_CLAMP), uvw, 0.0).rgb * lightmaps.data[ofs].exposure_normalization; } } diff --git a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl index 3de5e76970..116b0c8fbc 100644 --- a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl @@ -27,13 +27,8 @@ draw_call; #include "../light_data_inc.glsl" -#include "../samplers_inc.glsl" - layout(set = 0, binding = 2) uniform sampler shadow_sampler; -layout(set = 0, binding = 3) uniform sampler decal_sampler; -layout(set = 0, binding = 4) uniform sampler light_projector_sampler; - #define INSTANCE_FLAGS_NON_UNIFORM_SCALE (1 << 4) #define INSTANCE_FLAGS_USE_GI_BUFFERS (1 << 5) #define INSTANCE_FLAGS_USE_SDFGI (1 << 6) @@ -50,22 +45,22 @@ layout(set = 0, binding = 4) uniform sampler light_projector_sampler; //3 bits of stride #define INSTANCE_FLAGS_PARTICLE_TRAIL_MASK 0xFF -layout(set = 0, binding = 5, std430) restrict readonly buffer OmniLights { +layout(set = 0, binding = 3, std430) restrict readonly buffer OmniLights { LightData data[]; } omni_lights; -layout(set = 0, binding = 6, std430) restrict readonly buffer SpotLights { +layout(set = 0, binding = 4, std430) restrict readonly buffer SpotLights { LightData data[]; } spot_lights; -layout(set = 0, binding = 7, std430) restrict readonly buffer ReflectionProbeData { +layout(set = 0, binding = 5, std430) restrict readonly buffer ReflectionProbeData { ReflectionData data[]; } reflections; -layout(set = 0, binding = 8, std140) uniform DirectionalLights { +layout(set = 0, binding = 6, std140) uniform DirectionalLights { DirectionalLightData data[MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS]; } directional_lights; @@ -79,7 +74,7 @@ struct Lightmap { float exposure_normalization; }; -layout(set = 0, binding = 9, std140) restrict readonly buffer Lightmaps { +layout(set = 0, binding = 7, std140) restrict readonly buffer Lightmaps { Lightmap data[]; } lightmaps; @@ -88,24 +83,37 @@ struct LightmapCapture { mediump vec4 sh[9]; }; -layout(set = 0, binding = 10, std140) restrict readonly buffer LightmapCaptures { +layout(set = 0, binding = 8, std140) restrict readonly buffer LightmapCaptures { LightmapCapture data[]; } lightmap_captures; -layout(set = 0, binding = 11) uniform mediump texture2D decal_atlas; -layout(set = 0, binding = 12) uniform mediump texture2D decal_atlas_srgb; +layout(set = 0, binding = 9) uniform mediump texture2D decal_atlas; +layout(set = 0, binding = 10) uniform mediump texture2D decal_atlas_srgb; -layout(set = 0, binding = 13, std430) restrict readonly buffer Decals { +layout(set = 0, binding = 11, std430) restrict readonly buffer Decals { DecalData data[]; } decals; -layout(set = 0, binding = 14, std430) restrict readonly buffer GlobalShaderUniformData { +layout(set = 0, binding = 12, std430) restrict readonly buffer GlobalShaderUniformData { highp vec4 data[]; } global_shader_uniforms; +layout(set = 0, binding = 13 + 0) uniform sampler DEFAULT_SAMPLER_NEAREST_CLAMP; +layout(set = 0, binding = 13 + 1) uniform sampler DEFAULT_SAMPLER_LINEAR_CLAMP; +layout(set = 0, binding = 13 + 2) uniform sampler DEFAULT_SAMPLER_NEAREST_WITH_MIPMAPS_CLAMP; +layout(set = 0, binding = 13 + 3) uniform sampler DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP; +layout(set = 0, binding = 13 + 4) uniform sampler DEFAULT_SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_CLAMP; +layout(set = 0, binding = 13 + 5) uniform sampler DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_CLAMP; +layout(set = 0, binding = 13 + 6) uniform sampler DEFAULT_SAMPLER_NEAREST_REPEAT; +layout(set = 0, binding = 13 + 7) uniform sampler DEFAULT_SAMPLER_LINEAR_REPEAT; +layout(set = 0, binding = 13 + 8) uniform sampler DEFAULT_SAMPLER_NEAREST_WITH_MIPMAPS_REPEAT; +layout(set = 0, binding = 13 + 9) uniform sampler DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_REPEAT; +layout(set = 0, binding = 13 + 10) uniform sampler DEFAULT_SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_REPEAT; +layout(set = 0, binding = 13 + 11) uniform sampler DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_REPEAT; + /* Set 1: Render Pass (changes per render pass) */ layout(set = 1, binding = 0, std140) uniform SceneDataBlock { @@ -166,6 +174,23 @@ layout(set = 1, binding = 10) uniform mediump texture2D color_buffer; #define multiviewSampler sampler2D #endif // USE_MULTIVIEW +layout(set = 1, binding = 11) uniform sampler decal_sampler; + +layout(set = 1, binding = 12) uniform sampler light_projector_sampler; + +layout(set = 1, binding = 13 + 0) uniform sampler SAMPLER_NEAREST_CLAMP; +layout(set = 1, binding = 13 + 1) uniform sampler SAMPLER_LINEAR_CLAMP; +layout(set = 1, binding = 13 + 2) uniform sampler SAMPLER_NEAREST_WITH_MIPMAPS_CLAMP; +layout(set = 1, binding = 13 + 3) uniform sampler SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP; +layout(set = 1, binding = 13 + 4) uniform sampler SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_CLAMP; +layout(set = 1, binding = 13 + 5) uniform sampler SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_CLAMP; +layout(set = 1, binding = 13 + 6) uniform sampler SAMPLER_NEAREST_REPEAT; +layout(set = 1, binding = 13 + 7) uniform sampler SAMPLER_LINEAR_REPEAT; +layout(set = 1, binding = 13 + 8) uniform sampler SAMPLER_NEAREST_WITH_MIPMAPS_REPEAT; +layout(set = 1, binding = 13 + 9) uniform sampler SAMPLER_LINEAR_WITH_MIPMAPS_REPEAT; +layout(set = 1, binding = 13 + 10) uniform sampler SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_REPEAT; +layout(set = 1, binding = 13 + 11) uniform sampler SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_REPEAT; + /* Set 2 Skeleton & Instancing (can change per item) */ layout(set = 2, binding = 0, std430) restrict readonly buffer Transforms { diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_gi_inc.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_gi_inc.glsl index 8f68030cba..1602e3ae76 100644 --- a/servers/rendering/renderer_rd/shaders/scene_forward_gi_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/scene_forward_gi_inc.glsl @@ -13,7 +13,7 @@ vec4 voxel_cone_trace(texture3D probe, vec3 cell_size, vec3 pos, vec3 direction, if (any(greaterThan(abs(uvw_pos - 0.5), vec3(0.5f + half_diameter * cell_size)))) { break; } - vec4 scolor = textureLod(sampler3D(probe, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), uvw_pos, log2(diameter)); + vec4 scolor = textureLod(sampler3D(probe, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), uvw_pos, log2(diameter)); float a = (1.0 - color.a); color += a * scolor; dist += half_diameter; @@ -35,7 +35,7 @@ vec4 voxel_cone_trace_45_degrees(texture3D probe, vec3 cell_size, vec3 pos, vec3 if (any(greaterThan(abs(uvw_pos - 0.5), vec3(0.5f + radius * cell_size)))) { break; } - vec4 scolor = textureLod(sampler3D(probe, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), uvw_pos, lod_level); + vec4 scolor = textureLod(sampler3D(probe, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), uvw_pos, lod_level); lod_level += 1.0; float a = (1.0 - color.a); @@ -176,7 +176,7 @@ void sdfgi_process(uint cascade, vec3 cascade_pos, vec3 cam_pos, vec3 cam_normal } occ_pos *= sdfgi.occlusion_renormalize; - float occlusion = dot(textureLod(sampler3D(sdfgi_occlusion_cascades, SAMPLER_LINEAR_CLAMP), occ_pos, 0.0), occ_mask); + float occlusion = dot(textureLod(sampler3D(sdfgi_occlusion_cascades, DEFAULT_SAMPLER_LINEAR_CLAMP), occ_pos, 0.0), occ_mask); weight *= max(occlusion, 0.01); } @@ -187,7 +187,7 @@ void sdfgi_process(uint cascade, vec3 cascade_pos, vec3 cam_pos, vec3 cam_normal vec3 pos_uvw = diffuse_posf; pos_uvw.xy += vec2(offset.xy) * sdfgi.lightprobe_uv_offset.xy; pos_uvw.x += float(offset.z) * sdfgi.lightprobe_uv_offset.z; - diffuse = textureLod(sampler2DArray(sdfgi_lightprobe_texture, SAMPLER_LINEAR_CLAMP), pos_uvw, 0.0).rgb; + diffuse = textureLod(sampler2DArray(sdfgi_lightprobe_texture, DEFAULT_SAMPLER_LINEAR_CLAMP), pos_uvw, 0.0).rgb; diffuse_accum += vec4(diffuse * weight * sdfgi.cascades[cascade].exposure_normalization, weight); @@ -197,10 +197,10 @@ void sdfgi_process(uint cascade, vec3 cascade_pos, vec3 cam_pos, vec3 cam_normal pos_uvw.xy += vec2(offset.xy) * sdfgi.lightprobe_uv_offset.xy; pos_uvw.x += float(offset.z) * sdfgi.lightprobe_uv_offset.z; if (roughness < 0.99) { - specular = textureLod(sampler2DArray(sdfgi_lightprobe_texture, SAMPLER_LINEAR_CLAMP), pos_uvw + vec3(0, 0, float(sdfgi.max_cascades)), 0.0).rgb; + specular = textureLod(sampler2DArray(sdfgi_lightprobe_texture, DEFAULT_SAMPLER_LINEAR_CLAMP), pos_uvw + vec3(0, 0, float(sdfgi.max_cascades)), 0.0).rgb; } if (roughness > 0.5) { - specular = mix(specular, textureLod(sampler2DArray(sdfgi_lightprobe_texture, SAMPLER_LINEAR_CLAMP), pos_uvw, 0.0).rgb, (roughness - 0.5) * 2.0); + specular = mix(specular, textureLod(sampler2DArray(sdfgi_lightprobe_texture, DEFAULT_SAMPLER_LINEAR_CLAMP), pos_uvw, 0.0).rgb, (roughness - 0.5) * 2.0); } specular_accum += specular * weight * sdfgi.cascades[cascade].exposure_normalization; diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl index 4fb577d697..880b7a77ca 100644 --- a/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl @@ -374,7 +374,7 @@ float sample_directional_soft_shadow(texture2D shadow, vec3 pssm_coord, vec2 tex for (uint i = 0; i < sc_directional_penumbra_shadow_samples; i++) { vec2 suv = pssm_coord.xy + (disk_rotation * scene_data_block.data.directional_penumbra_shadow_kernel[i].xy) * tex_scale; - float d = textureLod(sampler2D(shadow, SAMPLER_LINEAR_CLAMP), suv, 0.0).r; + float d = textureLod(sampler2D(shadow, DEFAULT_SAMPLER_LINEAR_CLAMP), suv, 0.0).r; if (d < pssm_coord.z) { blocker_average += d; blocker_count += 1.0; @@ -478,7 +478,7 @@ float light_process_omni_shadow(uint idx, vec3 vertex, vec3 normal) { pos.xy = pos.xy * 0.5 + 0.5; pos.xy = uv_rect.xy + pos.xy * uv_rect.zw; - float d = textureLod(sampler2D(shadow_atlas, SAMPLER_LINEAR_CLAMP), pos.xy, 0.0).r; + float d = textureLod(sampler2D(shadow_atlas, DEFAULT_SAMPLER_LINEAR_CLAMP), pos.xy, 0.0).r; if (d < z_norm) { blocker_average += d; blocker_count += 1.0; @@ -605,7 +605,7 @@ void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 v // splane.xy = clamp(splane.xy,clamp_rect.xy + scene_data_block.data.shadow_atlas_pixel_size,clamp_rect.xy + clamp_rect.zw - scene_data_block.data.shadow_atlas_pixel_size ); splane.w = 1.0; //needed? i think it should be 1 already - float shadow_z = textureLod(sampler2D(shadow_atlas, SAMPLER_LINEAR_CLAMP), splane.xy, 0.0).r; + float shadow_z = textureLod(sampler2D(shadow_atlas, DEFAULT_SAMPLER_LINEAR_CLAMP), splane.xy, 0.0).r; transmittance_z = (splane.z - shadow_z) / omni_lights.data[idx].inv_radius; } #endif @@ -734,7 +734,7 @@ float light_process_spot_shadow(uint idx, vec3 vertex, vec3 normal) { for (uint i = 0; i < sc_penumbra_shadow_samples; i++) { vec2 suv = shadow_uv + (disk_rotation * scene_data_block.data.penumbra_shadow_kernel[i].xy) * uv_size; suv = clamp(suv, spot_lights.data[idx].atlas_rect.xy, clamp_max); - float d = textureLod(sampler2D(shadow_atlas, SAMPLER_LINEAR_CLAMP), suv, 0.0).r; + float d = textureLod(sampler2D(shadow_atlas, DEFAULT_SAMPLER_LINEAR_CLAMP), suv, 0.0).r; if (d < splane.z) { blocker_average += d; blocker_count += 1.0; @@ -838,7 +838,7 @@ void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 v splane /= splane.w; splane.xy = splane.xy * spot_lights.data[idx].atlas_rect.zw + spot_lights.data[idx].atlas_rect.xy; - float shadow_z = textureLod(sampler2D(shadow_atlas, SAMPLER_LINEAR_CLAMP), splane.xy, 0.0).r; + float shadow_z = textureLod(sampler2D(shadow_atlas, DEFAULT_SAMPLER_LINEAR_CLAMP), splane.xy, 0.0).r; shadow_z = shadow_z * 2.0 - 1.0; float z_far = 1.0 / spot_lights.data[idx].inv_radius; @@ -932,7 +932,7 @@ void reflection_process(uint ref_index, vec3 vertex, vec3 ref_vec, vec3 normal, vec4 reflection; - reflection.rgb = textureLod(samplerCubeArray(reflection_atlas, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(local_ref_vec, reflections.data[ref_index].index), roughness * MAX_ROUGHNESS_LOD).rgb * sc_luminance_multiplier; + reflection.rgb = textureLod(samplerCubeArray(reflection_atlas, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(local_ref_vec, reflections.data[ref_index].index), roughness * MAX_ROUGHNESS_LOD).rgb * sc_luminance_multiplier; reflection.rgb *= reflections.data[ref_index].exposure_normalization; if (reflections.data[ref_index].exterior) { reflection.rgb = mix(specular_light, reflection.rgb, blend); @@ -955,7 +955,7 @@ void reflection_process(uint ref_index, vec3 vertex, vec3 ref_vec, vec3 normal, vec4 ambient_out; - ambient_out.rgb = textureLod(samplerCubeArray(reflection_atlas, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(local_amb_vec, reflections.data[ref_index].index), MAX_ROUGHNESS_LOD).rgb; + ambient_out.rgb = textureLod(samplerCubeArray(reflection_atlas, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(local_amb_vec, reflections.data[ref_index].index), MAX_ROUGHNESS_LOD).rgb; ambient_out.rgb *= reflections.data[ref_index].exposure_normalization; ambient_out.a = blend; if (reflections.data[ref_index].exterior) { diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp index 1f6d1021f4..5f4bf6c8ed 100644 --- a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp @@ -1669,6 +1669,8 @@ void LightStorage::update_reflection_probe_buffer(RenderDataRD *p_render_data, c for (uint32_t i = 0; i < reflection_count; i++) { ReflectionProbeInstance *rpi = reflection_sort[i].probe_instance; + rpi->last_pass = RSG::rasterizer->get_frame_number(); + if (using_forward_ids) { forward_id_storage->map_forward_id(FORWARD_ID_TYPE_REFLECTION_PROBE, rpi->forward_id, i, rpi->last_pass); } @@ -1717,8 +1719,6 @@ void LightStorage::update_reflection_probe_buffer(RenderDataRD *p_render_data, c // hook for subclass to do further processing. RendererSceneRenderRD::get_singleton()->setup_added_reflection_probe(transform, extents); - - rpi->last_pass = RSG::rasterizer->get_frame_number(); } if (reflection_count) { diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp index 5ff5adc59a..6a99eb4108 100644 --- a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp +++ b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp @@ -148,9 +148,6 @@ void RenderSceneBuffersRD::configure(const RenderSceneBuffersConfiguration *p_co update_samplers(); - // Notify the renderer the base uniform needs to be recreated. - RendererSceneRenderRD::get_singleton()->base_uniforms_changed(); - // cleanout any old buffers we had. cleanup(); diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp index a246420bff..de6c5f6c21 100644 --- a/servers/rendering/renderer_viewport.cpp +++ b/servers/rendering/renderer_viewport.cpp @@ -886,9 +886,7 @@ void RendererViewport::viewport_set_fsr_sharpness(RID p_viewport, float p_sharpn ERR_FAIL_NULL(viewport); viewport->fsr_sharpness = p_sharpness; - if (viewport->render_buffers.is_valid()) { - viewport->render_buffers->set_fsr_sharpness(p_sharpness); - } + _configure_3d_render_buffers(viewport); } void RendererViewport::viewport_set_texture_mipmap_bias(RID p_viewport, float p_mipmap_bias) { @@ -896,9 +894,7 @@ void RendererViewport::viewport_set_texture_mipmap_bias(RID p_viewport, float p_ ERR_FAIL_NULL(viewport); viewport->texture_mipmap_bias = p_mipmap_bias; - if (viewport->render_buffers.is_valid()) { - viewport->render_buffers->set_texture_mipmap_bias(p_mipmap_bias); - } + _configure_3d_render_buffers(viewport); } void RendererViewport::viewport_set_scaling_3d_scale(RID p_viewport, float p_scaling_3d_scale) { @@ -1268,9 +1264,7 @@ void RendererViewport::viewport_set_use_debanding(RID p_viewport, bool p_use_deb return; } viewport->use_debanding = p_use_debanding; - if (viewport->render_buffers.is_valid()) { - viewport->render_buffers->set_use_debanding(p_use_debanding); - } + _configure_3d_render_buffers(viewport); } void RendererViewport::viewport_set_use_occlusion_culling(RID p_viewport, bool p_use_occlusion_culling) { diff --git a/servers/rendering/rendering_server_default.cpp b/servers/rendering/rendering_server_default.cpp index 65a6da8ac3..77fe91e4c9 100644 --- a/servers/rendering/rendering_server_default.cpp +++ b/servers/rendering/rendering_server_default.cpp @@ -379,6 +379,7 @@ void RenderingServerDefault::sync() { } void RenderingServerDefault::draw(bool p_swap_buffers, double frame_step) { + ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "Manually triggering the draw function from the RenderingServer can only be done on the main thread. Call this function from the main thread or use call_deferred()."); if (create_thread) { command_queue.push(this, &RenderingServerDefault::_thread_draw, p_swap_buffers, frame_step); } else { diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index e84616a232..763989fdd4 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -1193,7 +1193,7 @@ Error RenderingServer::mesh_create_surface_data_from_arrays(SurfaceData *r_surfa array_len = v3.size(); } break; default: { - ERR_FAIL_V(ERR_INVALID_DATA); + ERR_FAIL_V_MSG(ERR_INVALID_DATA, "Vertex array must be a PackedVector2Array or PackedVector3Array."); } break; } ERR_FAIL_COND_V(array_len == 0, ERR_INVALID_DATA); @@ -1214,7 +1214,7 @@ Error RenderingServer::mesh_create_surface_data_from_arrays(SurfaceData *r_surfa } } break; default: { - ERR_FAIL_V(ERR_INVALID_DATA); + ERR_FAIL_V_MSG(ERR_INVALID_DATA, "Bones array must be a PackedInt32Array."); } break; } } else if (i == RS::ARRAY_INDEX) { diff --git a/tests/core/math/test_basis.h b/tests/core/math/test_basis.h index fcac9a6231..a9bc2e9b99 100644 --- a/tests/core/math/test_basis.h +++ b/tests/core/math/test_basis.h @@ -324,6 +324,100 @@ TEST_CASE("[Basis] Is conformal checks") { CHECK_FALSE_MESSAGE( Basis(Vector3(Math_SQRT12, Math_SQRT12, 0), Vector3(0, 1, 0), Vector3(0, 0, 1)).is_conformal(), "Basis with the X axis skewed 45 degrees should not be conformal."); + + CHECK_MESSAGE( + Basis(0, 0, 0, 0, 0, 0, 0, 0, 0).is_conformal(), + "Edge case: Basis with all zeroes should return true for is_conformal (because a 0 scale is uniform)."); +} + +TEST_CASE("[Basis] Is orthogonal checks") { + CHECK_MESSAGE( + Basis().is_orthogonal(), + "Identity Basis should be orthogonal."); + + CHECK_MESSAGE( + Basis::from_euler(Vector3(1.2, 3.4, 5.6)).is_orthogonal(), + "Basis with only rotation should be orthogonal."); + + CHECK_MESSAGE( + Basis::from_scale(Vector3(-1, -1, -1)).is_orthogonal(), + "Basis with only a flip should be orthogonal."); + + CHECK_MESSAGE( + Basis::from_scale(Vector3(1.2, 3.4, 5.6)).is_orthogonal(), + "Basis with only scale should be orthogonal."); + + CHECK_MESSAGE( + Basis(Vector3(3, 4, 0), Vector3(4, -3, 0), Vector3(0, 0, 5)).is_orthogonal(), + "Basis with a flip, rotation, and uniform scale should be orthogonal."); + + CHECK_FALSE_MESSAGE( + Basis(Vector3(Math_SQRT12, Math_SQRT12, 0), Vector3(0, 1, 0), Vector3(0, 0, 1)).is_orthogonal(), + "Basis with the X axis skewed 45 degrees should not be orthogonal."); + + CHECK_MESSAGE( + Basis(0, 0, 0, 0, 0, 0, 0, 0, 0).is_orthogonal(), + "Edge case: Basis with all zeroes should return true for is_orthogonal, since zero vectors are orthogonal to all vectors."); +} + +TEST_CASE("[Basis] Is orthonormal checks") { + CHECK_MESSAGE( + Basis().is_orthonormal(), + "Identity Basis should be orthonormal."); + + CHECK_MESSAGE( + Basis::from_euler(Vector3(1.2, 3.4, 5.6)).is_orthonormal(), + "Basis with only rotation should be orthonormal."); + + CHECK_MESSAGE( + Basis::from_scale(Vector3(-1, -1, -1)).is_orthonormal(), + "Basis with only a flip should be orthonormal."); + + CHECK_FALSE_MESSAGE( + Basis::from_scale(Vector3(1.2, 3.4, 5.6)).is_orthonormal(), + "Basis with only scale should not be orthonormal."); + + CHECK_FALSE_MESSAGE( + Basis(Vector3(3, 4, 0), Vector3(4, -3, 0), Vector3(0, 0, 5)).is_orthonormal(), + "Basis with a flip, rotation, and uniform scale should not be orthonormal."); + + CHECK_FALSE_MESSAGE( + Basis(Vector3(Math_SQRT12, Math_SQRT12, 0), Vector3(0, 1, 0), Vector3(0, 0, 1)).is_orthonormal(), + "Basis with the X axis skewed 45 degrees should not be orthonormal."); + + CHECK_FALSE_MESSAGE( + Basis(0, 0, 0, 0, 0, 0, 0, 0, 0).is_orthonormal(), + "Edge case: Basis with all zeroes should return false for is_orthonormal, since the vectors do not have a length of 1."); +} + +TEST_CASE("[Basis] Is rotation checks") { + CHECK_MESSAGE( + Basis().is_rotation(), + "Identity Basis should be a rotation (a rotation of zero)."); + + CHECK_MESSAGE( + Basis::from_euler(Vector3(1.2, 3.4, 5.6)).is_rotation(), + "Basis with only rotation should be a rotation."); + + CHECK_FALSE_MESSAGE( + Basis::from_scale(Vector3(-1, -1, -1)).is_rotation(), + "Basis with only a flip should not be a rotation."); + + CHECK_FALSE_MESSAGE( + Basis::from_scale(Vector3(1.2, 3.4, 5.6)).is_rotation(), + "Basis with only scale should not be a rotation."); + + CHECK_FALSE_MESSAGE( + Basis(Vector3(2, 0, 0), Vector3(0, 0.5, 0), Vector3(0, 0, 1)).is_rotation(), + "Basis with a squeeze should not be a rotation."); + + CHECK_FALSE_MESSAGE( + Basis(Vector3(Math_SQRT12, Math_SQRT12, 0), Vector3(0, 1, 0), Vector3(0, 0, 1)).is_rotation(), + "Basis with the X axis skewed 45 degrees should not be a rotation."); + + CHECK_FALSE_MESSAGE( + Basis(0, 0, 0, 0, 0, 0, 0, 0, 0).is_rotation(), + "Edge case: Basis with all zeroes should return false for is_rotation, because it is not just a rotation (has a scale of 0)."); } } // namespace TestBasis diff --git a/tests/core/string/test_string.h b/tests/core/string/test_string.h index 02349aedc0..8a11491bb2 100644 --- a/tests/core/string/test_string.h +++ b/tests/core/string/test_string.h @@ -456,30 +456,93 @@ TEST_CASE("[String] Number to string") { } TEST_CASE("[String] String to integer") { - static const char *nums[4] = { "1237461283", "- 22", "0", " - 1123412" }; - static const int num[4] = { 1237461283, -22, 0, -1123412 }; + static const char *nums[14] = { "1237461283", "- 22", "0", " - 1123412", "", "10_000_000", "-1_2_3_4", "10__000", " 1 2 34 ", "-0", "007", "--45", "---46", "-7-2" }; + static const int num[14] = { 1237461283, -22, 0, -1123412, 0, 10000000, -1234, 10000, 1234, 0, 7, 45, -46, -72 }; - for (int i = 0; i < 4; i++) { + for (int i = 0; i < 14; i++) { CHECK(String(nums[i]).to_int() == num[i]); } + CHECK(String("0b1011").to_int() == 1011); // Looks like a binary number, but to_int() handles this as a base-10 number, "b" is just ignored. + CHECK(String("0x1012").to_int() == 1012); // Looks like a hexadecimal number, but to_int() handles this as a base-10 number, "x" is just ignored. + + ERR_PRINT_OFF + CHECK(String("999999999999999999999999999999999999999999999999999999999").to_int() == INT64_MAX); // Too large, largest possible is returned. + CHECK(String("-999999999999999999999999999999999999999999999999999999999").to_int() == INT64_MIN); // Too small, smallest possible is returned. + ERR_PRINT_ON } TEST_CASE("[String] Hex to integer") { - static const char *nums[4] = { "0xFFAE", "22", "0", "AADDAD" }; - static const int64_t num[4] = { 0xFFAE, 0x22, 0, 0xAADDAD }; + static const char *nums[12] = { "0xFFAE", "22", "0", "AADDAD", "0x7FFFFFFFFFFFFFFF", "-0xf", "", "000", "000f", "0xaA", "-ff", "-" }; + static const int64_t num[12] = { 0xFFAE, 0x22, 0, 0xAADDAD, 0x7FFFFFFFFFFFFFFF, -0xf, 0, 0, 0xf, 0xaa, -0xff, 0x0 }; - for (int i = 0; i < 4; i++) { + for (int i = 0; i < 12; i++) { CHECK(String(nums[i]).hex_to_int() == num[i]); } + + // Invalid hex strings should return 0. + static const char *invalid_nums[15] = { "qwerty", "QWERTY", "0xqwerty", "0x00qwerty", "qwerty00", "0x", "0x__", "__", "x12", "+", " ff", "ff ", "f f", "+ff", "--0x78" }; + + ERR_PRINT_OFF + for (int i = 0; i < 15; i++) { + CHECK(String(invalid_nums[i]).hex_to_int() == 0); + } + + CHECK(String("0xFFFFFFFFFFFFFFFFFFFFFFF").hex_to_int() == INT64_MAX); // Too large, largest possible is returned. + CHECK(String("-0xFFFFFFFFFFFFFFFFFFFFFFF").hex_to_int() == INT64_MIN); // Too small, smallest possible is returned. + ERR_PRINT_ON +} + +TEST_CASE("[String] Bin to integer") { + static const char *nums[10] = { "", "0", "0b0", "0b1", "0b", "1", "0b1010", "-0b11", "-1010", "0b0111111111111111111111111111111111111111111111111111111111111111" }; + static const int64_t num[10] = { 0, 0, 0, 1, 0, 1, 10, -3, -10, 0x7FFFFFFFFFFFFFFF }; + + for (int i = 0; i < 10; i++) { + CHECK(String(nums[i]).bin_to_int() == num[i]); + } + + // Invalid bin strings should return 0. The long "0x11...11" is just too long for a 64 bit int. + static const char *invalid_nums[16] = { "qwerty", "QWERTY", "0bqwerty", "0b00qwerty", "qwerty00", "0x__", "0b__", "__", "b12", "+", "-", "0x12ab", " 11", "11 ", "1 1", "--0b11" }; + + for (int i = 0; i < 16; i++) { + CHECK(String(invalid_nums[i]).bin_to_int() == 0); + } + + ERR_PRINT_OFF + CHECK(String("0b111111111111111111111111111111111111111111111111111111111111111111111111111111111").bin_to_int() == INT64_MAX); // Too large, largest possible is returned. + CHECK(String("-0b111111111111111111111111111111111111111111111111111111111111111111111111111111111").bin_to_int() == INT64_MIN); // Too small, smallest possible is returned. + ERR_PRINT_ON } TEST_CASE("[String] String to float") { - static const char *nums[4] = { "-12348298412.2", "0.05", "2.0002", " -0.0001" }; - static const double num[4] = { -12348298412.2, 0.05, 2.0002, -0.0001 }; + static const char *nums[12] = { "-12348298412.2", "0.05", "2.0002", " -0.0001", "0", "000", "123", "0.0", "000.000", "000.007", "234__", "3..14" }; + static const double num[12] = { -12348298412.2, 0.05, 2.0002, -0.0001, 0.0, 0.0, 123.0, 0.0, 0.0, 0.007, 234.0, 3.0 }; - for (int i = 0; i < 4; i++) { + for (int i = 0; i < 12; i++) { CHECK(!(ABS(String(nums[i]).to_float() - num[i]) > 0.00001)); } + + // Invalid float strings should return 0. + static const char *invalid_nums[6] = { "qwerty", "qwerty123", "0xffff", "0b1010", "--3.13", "__345" }; + + for (int i = 0; i < 6; i++) { + CHECK(String(invalid_nums[i]).to_float() == 0); + } + + // Very large exponents. + CHECK(String("1e308").to_float() == 1e308); + CHECK(String("-1e308").to_float() == -1e308); + + // Exponent is so high that value is INFINITY/-INFINITY. + CHECK(String("1e309").to_float() == INFINITY); + CHECK(String("1e511").to_float() == INFINITY); + CHECK(String("-1e309").to_float() == -INFINITY); + CHECK(String("-1e511").to_float() == -INFINITY); + + // Exponent is so high that a warning message is printed. Value is INFINITY/-INFINITY. + ERR_PRINT_OFF + CHECK(String("1e512").to_float() == INFINITY); + CHECK(String("-1e512").to_float() == -INFINITY); + ERR_PRINT_ON } TEST_CASE("[String] Slicing") { diff --git a/tests/scene/test_primitives.h b/tests/scene/test_primitives.h index 9232a3020d..552f722d24 100644 --- a/tests/scene/test_primitives.h +++ b/tests/scene/test_primitives.h @@ -232,7 +232,7 @@ TEST_CASE("[SceneTree][Primitive][Cylinder] Cylinder Primitive") { CHECK(cylinder->get_bottom_radius() > 0); CHECK(cylinder->get_height() > 0); CHECK(cylinder->get_radial_segments() > 0); - CHECK(cylinder->get_rings() > 0); + CHECK(cylinder->get_rings() >= 0); } SUBCASE("[SceneTree][Primitive][Cylinder] Set properties and get them") { diff --git a/tests/scene/test_text_edit.h b/tests/scene/test_text_edit.h index 9c9ade4445..e81578a862 100644 --- a/tests/scene/test_text_edit.h +++ b/tests/scene/test_text_edit.h @@ -4118,7 +4118,7 @@ TEST_CASE("[SceneTree][TextEdit] setter getters") { CHECK_FALSE(text_edit->is_drawing_spaces()); } - SUBCASE("[TextEdit] draw minimao") { + SUBCASE("[TextEdit] draw minimap") { text_edit->set_draw_minimap(true); CHECK(text_edit->is_drawing_minimap()); text_edit->set_draw_minimap(false); diff --git a/tests/servers/test_navigation_server_3d.h b/tests/servers/test_navigation_server_3d.h index 691536da8e..5ab2975b74 100644 --- a/tests/servers/test_navigation_server_3d.h +++ b/tests/servers/test_navigation_server_3d.h @@ -429,6 +429,105 @@ TEST_SUITE("[Navigation]") { navigation_server->free(map); } + TEST_CASE("[NavigationServer3D] Server should make agents avoid dynamic obstacles when avoidance enabled") { + NavigationServer3D *navigation_server = NavigationServer3D::get_singleton(); + + RID map = navigation_server->map_create(); + RID agent_1 = navigation_server->agent_create(); + RID obstacle_1 = navigation_server->obstacle_create(); + + navigation_server->map_set_active(map, true); + + navigation_server->agent_set_map(agent_1, map); + navigation_server->agent_set_avoidance_enabled(agent_1, true); + navigation_server->agent_set_position(agent_1, Vector3(0, 0, 0)); + navigation_server->agent_set_radius(agent_1, 1); + navigation_server->agent_set_velocity(agent_1, Vector3(1, 0, 0)); + CallableMock agent_1_avoidance_callback_mock; + navigation_server->agent_set_avoidance_callback(agent_1, callable_mp(&agent_1_avoidance_callback_mock, &CallableMock::function1)); + + navigation_server->obstacle_set_map(obstacle_1, map); + navigation_server->obstacle_set_avoidance_enabled(obstacle_1, true); + navigation_server->obstacle_set_position(obstacle_1, Vector3(2.5, 0, 0.5)); + navigation_server->obstacle_set_radius(obstacle_1, 1); + + CHECK_EQ(agent_1_avoidance_callback_mock.function1_calls, 0); + navigation_server->process(0.0); // Give server some cycles to commit. + CHECK_EQ(agent_1_avoidance_callback_mock.function1_calls, 1); + Vector3 agent_1_safe_velocity = agent_1_avoidance_callback_mock.function1_latest_arg0; + CHECK_MESSAGE(agent_1_safe_velocity.x > 0, "Agent 1 should move a bit along desired velocity (+X)."); + CHECK_MESSAGE(agent_1_safe_velocity.z < 0, "Agent 1 should move a bit to the side so that it avoids obstacle."); + + navigation_server->free(obstacle_1); + navigation_server->free(agent_1); + navigation_server->free(map); + navigation_server->process(0.0); // Give server some cycles to commit. + } + + TEST_CASE("[NavigationServer3D] Server should make agents avoid static obstacles when avoidance enabled") { + NavigationServer3D *navigation_server = NavigationServer3D::get_singleton(); + + RID map = navigation_server->map_create(); + RID agent_1 = navigation_server->agent_create(); + RID agent_2 = navigation_server->agent_create(); + RID obstacle_1 = navigation_server->obstacle_create(); + + navigation_server->map_set_active(map, true); + + navigation_server->agent_set_map(agent_1, map); + navigation_server->agent_set_avoidance_enabled(agent_1, true); + navigation_server->agent_set_radius(agent_1, 1.6); // Have hit the obstacle already. + navigation_server->agent_set_velocity(agent_1, Vector3(1, 0, 0)); + CallableMock agent_1_avoidance_callback_mock; + navigation_server->agent_set_avoidance_callback(agent_1, callable_mp(&agent_1_avoidance_callback_mock, &CallableMock::function1)); + + navigation_server->agent_set_map(agent_2, map); + navigation_server->agent_set_avoidance_enabled(agent_2, true); + navigation_server->agent_set_radius(agent_2, 1.4); // Haven't hit the obstacle yet. + navigation_server->agent_set_velocity(agent_2, Vector3(1, 0, 0)); + CallableMock agent_2_avoidance_callback_mock; + navigation_server->agent_set_avoidance_callback(agent_2, callable_mp(&agent_2_avoidance_callback_mock, &CallableMock::function1)); + + navigation_server->obstacle_set_map(obstacle_1, map); + navigation_server->obstacle_set_avoidance_enabled(obstacle_1, true); + PackedVector3Array obstacle_1_vertices; + + SUBCASE("Static obstacles should work on ground level") { + navigation_server->agent_set_position(agent_1, Vector3(0, 0, 0)); + navigation_server->agent_set_position(agent_2, Vector3(0, 0, 5)); + obstacle_1_vertices.push_back(Vector3(1.5, 0, 0.5)); + obstacle_1_vertices.push_back(Vector3(1.5, 0, 4.5)); + } + + SUBCASE("Static obstacles should work when elevated") { + navigation_server->agent_set_position(agent_1, Vector3(0, 5, 0)); + navigation_server->agent_set_position(agent_2, Vector3(0, 5, 5)); + obstacle_1_vertices.push_back(Vector3(1.5, 0, 0.5)); + obstacle_1_vertices.push_back(Vector3(1.5, 0, 4.5)); + navigation_server->obstacle_set_position(obstacle_1, Vector3(0, 5, 0)); + } + + navigation_server->obstacle_set_vertices(obstacle_1, obstacle_1_vertices); + + CHECK_EQ(agent_1_avoidance_callback_mock.function1_calls, 0); + CHECK_EQ(agent_2_avoidance_callback_mock.function1_calls, 0); + navigation_server->process(0.0); // Give server some cycles to commit. + CHECK_EQ(agent_1_avoidance_callback_mock.function1_calls, 1); + CHECK_EQ(agent_2_avoidance_callback_mock.function1_calls, 1); + Vector3 agent_1_safe_velocity = agent_1_avoidance_callback_mock.function1_latest_arg0; + Vector3 agent_2_safe_velocity = agent_2_avoidance_callback_mock.function1_latest_arg0; + CHECK_MESSAGE(agent_1_safe_velocity.x > 0, "Agent 1 should move a bit along desired velocity (+X)."); + CHECK_MESSAGE(agent_1_safe_velocity.z < 0, "Agent 1 should move a bit to the side so that it avoids obstacle."); + CHECK_MESSAGE(agent_2_safe_velocity.x > 0, "Agent 2 should move a bit along desired velocity (+X)."); + CHECK_MESSAGE(agent_2_safe_velocity.z == 0, "Agent 2 should not move to the side."); + + navigation_server->free(obstacle_1); + navigation_server->free(agent_2); + navigation_server->free(agent_1); + navigation_server->free(map); + navigation_server->process(0.0); // Give server some cycles to commit. + } + #ifndef DISABLE_DEPRECATED // This test case uses only public APIs on purpose - other test cases use simplified baking. // FIXME: Remove once deprecated `region_bake_navigation_mesh()` is removed. diff --git a/thirdparty/squish/colourblock.cpp b/thirdparty/squish/colourblock.cpp index 3d87adaa77..f14c9362bd 100644 --- a/thirdparty/squish/colourblock.cpp +++ b/thirdparty/squish/colourblock.cpp @@ -24,9 +24,9 @@ -------------------------------------------------------------------------- */ #include "colourblock.h" -// -- Godot start -- +// -- GODOT start -- #include "alpha.h" -// -- Godot end -- +// -- GODOT end -- namespace squish { @@ -214,7 +214,18 @@ void DecompressColour( u8* rgba, void const* block, bool isDxt1 ) } } -// -- Godot start -- +// -- GODOT start -- +void DecompressColourBc4( u8* rgba, void const* block) +{ + DecompressAlphaDxt5(rgba,block); + for ( int i = 0; i < 16; ++i ) { + rgba[i*4] = rgba[i*4 + 3]; + rgba[i*4 + 1] = 0; + rgba[i*4 + 2] = 0; + rgba[i*4 + 3] = 255; + } +} + void DecompressColourBc5( u8* rgba, void const* block) { void const* rblock = block; diff --git a/thirdparty/squish/colourblock.h b/thirdparty/squish/colourblock.h index 3cb9b7e3b9..e1eb9e4917 100644 --- a/thirdparty/squish/colourblock.h +++ b/thirdparty/squish/colourblock.h @@ -36,6 +36,7 @@ void WriteColourBlock4( Vec3::Arg start, Vec3::Arg end, u8 const* indices, void* void DecompressColour( u8* rgba, void const* block, bool isDxt1 ); // -- GODOT start -- +void DecompressColourBc4( u8* rgba, void const* block ); void DecompressColourBc5( u8* rgba, void const* block ); // -- GODOT end -- diff --git a/thirdparty/squish/godot-changes.patch b/thirdparty/squish/godot-changes.patch index ef7bafb4b4..555fbc51d0 100644 --- a/thirdparty/squish/godot-changes.patch +++ b/thirdparty/squish/godot-changes.patch @@ -100,3 +100,112 @@ index 1d22a64ad..fd11a147d 100644 // decompress alpha separately if necessary if( ( flags & kDxt3 ) != 0 ) + +diff --git a/thirdparty/squish/colourblock.cpp b/thirdparty/squish/colourblock.cpp +index 49401358bc..f14c9362bd 100644 +--- a/thirdparty/squish/colourblock.cpp ++++ b/thirdparty/squish/colourblock.cpp +@@ -24,9 +24,9 @@ + -------------------------------------------------------------------------- */ + + #include "colourblock.h" +-// -- Godot start -- ++// -- GODOT start -- + #include "alpha.h" +-// -- Godot end -- ++// -- GODOT end -- + + namespace squish { + +diff --git a/thirdparty/squish/godot-changes.patch b/thirdparty/squish/godot-changes.patch +index ef7bafb4b4..655a8cffc2 100644 +--- a/thirdparty/squish/godot-changes.patch ++++ b/thirdparty/squish/godot-changes.patch +@@ -1,22 +1,33 @@ + diff --git a/thirdparty/squish/colourblock.cpp b/thirdparty/squish/colourblock.cpp +-index af8b98036..3d87adaa7 100644 ++index af8b980365..f14c9362bd 100644 + --- a/thirdparty/squish/colourblock.cpp + +++ b/thirdparty/squish/colourblock.cpp + @@ -24,6 +24,9 @@ + -------------------------------------------------------------------------- */ + + #include "colourblock.h" +-+// -- Godot start -- +++// -- GODOT start -- + +#include "alpha.h" +-+// -- Godot end -- +++// -- GODOT end -- + + namespace squish { + +-@@ -211,4 +214,23 @@ void DecompressColour( u8* rgba, void const* block, bool isDxt1 ) ++@@ -211,4 +214,34 @@ void DecompressColour( u8* rgba, void const* block, bool isDxt1 ) + } + } + +-+// -- Godot start -- +++// -- GODOT start -- +++void DecompressColourBc4( u8* rgba, void const* block) +++{ +++ DecompressAlphaDxt5(rgba,block); +++ for ( int i = 0; i < 16; ++i ) { +++ rgba[i*4] = rgba[i*4 + 3]; +++ rgba[i*4 + 1] = 0; +++ rgba[i*4 + 2] = 0; +++ rgba[i*4 + 3] = 255; +++ } +++} +++ + +void DecompressColourBc5( u8* rgba, void const* block) + +{ + + void const* rblock = block; +@@ -37,21 +48,22 @@ index af8b98036..3d87adaa7 100644 + + + } // namespace squish + diff --git a/thirdparty/squish/colourblock.h b/thirdparty/squish/colourblock.h +-index fee2cd7c5..3cb9b7e3b 100644 ++index fee2cd7c5d..e1eb9e4917 100644 + --- a/thirdparty/squish/colourblock.h + +++ b/thirdparty/squish/colourblock.h +-@@ -35,6 +35,9 @@ void WriteColourBlock3( Vec3::Arg start, Vec3::Arg end, u8 const* indices, void* ++@@ -35,6 +35,10 @@ void WriteColourBlock3( Vec3::Arg start, Vec3::Arg end, u8 const* indices, void* + void WriteColourBlock4( Vec3::Arg start, Vec3::Arg end, u8 const* indices, void* block ); + + void DecompressColour( u8* rgba, void const* block, bool isDxt1 ); + +// -- GODOT start -- +++void DecompressColourBc4( u8* rgba, void const* block ); + +void DecompressColourBc5( u8* rgba, void const* block ); + +// -- GODOT end -- + + } // namespace squish + + diff --git a/thirdparty/squish/config.h b/thirdparty/squish/config.h +-index 92edefe96..05f8d7259 100644 ++index 92edefe966..05f8d72598 100644 + --- a/thirdparty/squish/config.h + +++ b/thirdparty/squish/config.h + @@ -32,6 +32,26 @@ +@@ -82,17 +94,19 @@ index 92edefe96..05f8d7259 100644 + #define SQUISH_USE_SSE 0 + #endif + diff --git a/thirdparty/squish/squish.cpp b/thirdparty/squish/squish.cpp +-index 1d22a64ad..fd11a147d 100644 ++index 1d22a64ad6..086ba11cd0 100644 + --- a/thirdparty/squish/squish.cpp + +++ b/thirdparty/squish/squish.cpp +-@@ -135,7 +135,13 @@ void Decompress( u8* rgba, void const* block, int flags ) ++@@ -135,7 +135,15 @@ void Decompress( u8* rgba, void const* block, int flags ) + colourBlock = reinterpret_cast< u8 const* >( block ) + 8; + + // decompress colour + - DecompressColour( rgba, colourBlock, ( flags & kDxt1 ) != 0 ); + + // -- GODOT start -- + + //DecompressColour( rgba, colourBlock, ( flags & kDxt1 ) != 0 ); +-+ if(( flags & ( kBc5 ) ) != 0) +++ if(( flags & ( kBc4 ) ) != 0) +++ DecompressColourBc4( rgba, colourBlock); +++ else if(( flags & ( kBc5 ) ) != 0) + + DecompressColourBc5( rgba, colourBlock); + + else + + DecompressColour( rgba, colourBlock, ( flags & kDxt1 ) != 0 );
\ No newline at end of file diff --git a/thirdparty/squish/squish.cpp b/thirdparty/squish/squish.cpp index fd11a147de..1de1da3e52 100644 --- a/thirdparty/squish/squish.cpp +++ b/thirdparty/squish/squish.cpp @@ -137,7 +137,9 @@ void Decompress( u8* rgba, void const* block, int flags ) // decompress colour // -- GODOT start -- //DecompressColour( rgba, colourBlock, ( flags & kDxt1 ) != 0 ); - if(( flags & ( kBc5 ) ) != 0) + if(( flags & ( kBc4 ) ) != 0) + DecompressColourBc4( rgba, colourBlock); + else if(( flags & ( kBc5 ) ) != 0) DecompressColourBc5( rgba, colourBlock); else DecompressColour( rgba, colourBlock, ( flags & kDxt1 ) != 0 ); |