diff options
56 files changed, 2024 insertions, 763 deletions
diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt index 653ebd46cd..078aac1f13 100644 --- a/COPYRIGHT.txt +++ b/COPYRIGHT.txt @@ -261,7 +261,7 @@ License: Apache-2.0 Files: ./thirdparty/meshoptimizer/ Comment: meshoptimizer -Copyright: 2016-2020, Arseny Kapoulkine +Copyright: 2016-2021, Arseny Kapoulkine License: Expat Files: ./thirdparty/minimp3/ diff --git a/core/input/input.cpp b/core/input/input.cpp index 627944210f..2304c05bf8 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -1329,9 +1329,10 @@ void Input::add_joy_mapping(String p_mapping, bool p_update_existing) { if (p_update_existing) { Vector<String> entry = p_mapping.split(","); String uid = entry[0]; - for (int i = 0; i < joy_names.size(); i++) { - if (uid == joy_names[i].uid) { - joy_names[i].mapping = map_db.size() - 1; + for (Map<int, Joypad>::Element *E = joy_names.front(); E; E = E->next()) { + Joypad &joy = E->get(); + if (joy.uid == uid) { + joy.mapping = map_db.size() - 1; } } } @@ -1343,9 +1344,10 @@ void Input::remove_joy_mapping(String p_guid) { map_db.remove(i); } } - for (int i = 0; i < joy_names.size(); i++) { - if (joy_names[i].uid == p_guid) { - joy_names[i].mapping = -1; + for (Map<int, Joypad>::Element *E = joy_names.front(); E; E = E->next()) { + Joypad &joy = E->get(); + if (joy.uid == p_guid) { + joy.mapping = -1; } } } @@ -1361,8 +1363,13 @@ void Input::set_fallback_mapping(String p_guid) { //platforms that use the remapping system can override and call to these ones bool Input::is_joy_known(int p_device) { - int mapping = joy_names[p_device].mapping; - return mapping != -1 ? (mapping != fallback_mapping) : false; + if (joy_names.has(p_device)) { + int mapping = joy_names[p_device].mapping; + if (mapping != -1 && mapping != fallback_mapping) { + return true; + } + } + return false; } String Input::get_joy_guid(int p_device) const { diff --git a/core/input/input_map.cpp b/core/input/input_map.cpp index 7d85fd6492..aab4e6593c 100644 --- a/core/input/input_map.cpp +++ b/core/input/input_map.cpp @@ -54,8 +54,36 @@ void InputMap::_bind_methods() { ClassDB::bind_method(D_METHOD("load_from_project_settings"), &InputMap::load_from_project_settings); } +/** + * Returns an nonexistent action error message with a suggestion of the closest + * matching action name (if possible). + */ +String InputMap::_suggest_actions(const StringName &p_action) const { + List<StringName> actions = get_actions(); + StringName closest_action; + float closest_similarity = 0.0; + + // Find the most action with the most similar name. + for (List<StringName>::Element *E = actions.front(); E; E = E->next()) { + const float similarity = String(E->get()).similarity(p_action); + + if (similarity > closest_similarity) { + closest_action = E->get(); + closest_similarity = similarity; + } + } + + String error_message = vformat("The InputMap action \"%s\" doesn't exist.", p_action); + + if (closest_similarity >= 0.4) { + // Only include a suggestion in the error message if it's similar enough. + error_message += vformat(" Did you mean \"%s\"?", closest_action); + } + return error_message; +} + void InputMap::add_action(const StringName &p_action, float p_deadzone) { - ERR_FAIL_COND_MSG(input_map.has(p_action), "InputMap already has action '" + String(p_action) + "'."); + ERR_FAIL_COND_MSG(input_map.has(p_action), "InputMap already has action \"" + String(p_action) + "\"."); input_map[p_action] = Action(); static int last_id = 1; input_map[p_action].id = last_id; @@ -64,7 +92,8 @@ void InputMap::add_action(const StringName &p_action, float p_deadzone) { } void InputMap::erase_action(const StringName &p_action) { - ERR_FAIL_COND_MSG(!input_map.has(p_action), "Request for nonexistent InputMap action '" + String(p_action) + "'."); + ERR_FAIL_COND_MSG(!input_map.has(p_action), _suggest_actions(p_action)); + input_map.erase(p_action); } @@ -122,20 +151,20 @@ bool InputMap::has_action(const StringName &p_action) const { } float InputMap::action_get_deadzone(const StringName &p_action) { - ERR_FAIL_COND_V_MSG(!input_map.has(p_action), 0.0f, "Request for nonexistent InputMap action '" + String(p_action) + "'."); + ERR_FAIL_COND_V_MSG(!input_map.has(p_action), 0.0f, _suggest_actions(p_action)); return input_map[p_action].deadzone; } void InputMap::action_set_deadzone(const StringName &p_action, float p_deadzone) { - ERR_FAIL_COND_MSG(!input_map.has(p_action), "Request for nonexistent InputMap action '" + String(p_action) + "'."); + ERR_FAIL_COND_MSG(!input_map.has(p_action), _suggest_actions(p_action)); input_map[p_action].deadzone = p_deadzone; } void InputMap::action_add_event(const StringName &p_action, const Ref<InputEvent> &p_event) { ERR_FAIL_COND_MSG(p_event.is_null(), "It's not a reference to a valid InputEvent object."); - ERR_FAIL_COND_MSG(!input_map.has(p_action), "Request for nonexistent InputMap action '" + String(p_action) + "'."); + ERR_FAIL_COND_MSG(!input_map.has(p_action), _suggest_actions(p_action)); if (_find_event(input_map[p_action], p_event, true)) { return; // Already addded. } @@ -144,12 +173,12 @@ void InputMap::action_add_event(const StringName &p_action, const Ref<InputEvent } bool InputMap::action_has_event(const StringName &p_action, const Ref<InputEvent> &p_event) { - ERR_FAIL_COND_V_MSG(!input_map.has(p_action), false, "Request for nonexistent InputMap action '" + String(p_action) + "'."); + ERR_FAIL_COND_V_MSG(!input_map.has(p_action), false, _suggest_actions(p_action)); return (_find_event(input_map[p_action], p_event, true) != nullptr); } void InputMap::action_erase_event(const StringName &p_action, const Ref<InputEvent> &p_event) { - ERR_FAIL_COND_MSG(!input_map.has(p_action), "Request for nonexistent InputMap action '" + String(p_action) + "'."); + ERR_FAIL_COND_MSG(!input_map.has(p_action), _suggest_actions(p_action)); List<Ref<InputEvent>>::Element *E = _find_event(input_map[p_action], p_event, true); if (E) { @@ -161,7 +190,7 @@ void InputMap::action_erase_event(const StringName &p_action, const Ref<InputEve } void InputMap::action_erase_events(const StringName &p_action) { - ERR_FAIL_COND_MSG(!input_map.has(p_action), "Request for nonexistent InputMap action '" + String(p_action) + "'."); + ERR_FAIL_COND_MSG(!input_map.has(p_action), _suggest_actions(p_action)); input_map[p_action].inputs.clear(); } @@ -193,7 +222,7 @@ bool InputMap::event_is_action(const Ref<InputEvent> &p_event, const StringName bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match, bool *p_pressed, float *p_strength, float *p_raw_strength) const { OrderedHashMap<StringName, Action>::Element E = input_map.find(p_action); - ERR_FAIL_COND_V_MSG(!E, false, "Request for nonexistent InputMap action '" + String(p_action) + "'."); + ERR_FAIL_COND_V_MSG(!E, false, _suggest_actions(p_action)); Ref<InputEventAction> input_event_action = p_event; if (input_event_action.is_valid()) { diff --git a/core/input/input_map.h b/core/input/input_map.h index 99c71e1e53..0e0567464a 100644 --- a/core/input/input_map.h +++ b/core/input/input_map.h @@ -61,6 +61,7 @@ private: Array _action_get_events(const StringName &p_action); Array _get_actions(); + String _suggest_actions(const StringName &p_action) const; protected: static void _bind_methods(); diff --git a/core/io/resource_importer.cpp b/core/io/resource_importer.cpp index 5ca0eb884a..b503655edd 100644 --- a/core/io/resource_importer.cpp +++ b/core/io/resource_importer.cpp @@ -192,6 +192,34 @@ bool ResourceFormatImporter::recognize_path(const String &p_path, const String & return FileAccess::exists(p_path + ".import"); } +Error ResourceFormatImporter::get_import_order_threads_and_importer(const String &p_path, int &r_order, bool &r_can_threads, String &r_importer) const { + r_order = 0; + r_importer = ""; + + r_can_threads = false; + Ref<ResourceImporter> importer; + + if (FileAccess::exists(p_path + ".import")) { + PathAndType pat; + Error err = _get_path_and_type(p_path, pat); + + if (err == OK) { + importer = get_importer_by_name(pat.importer); + } + } else { + importer = get_importer_by_extension(p_path.get_extension().to_lower()); + } + + if (importer.is_valid()) { + r_order = importer->get_import_order(); + r_importer = importer->get_importer_name(); + r_can_threads = importer->can_import_threaded(); + return OK; + } else { + return ERR_INVALID_PARAMETER; + } +} + int ResourceFormatImporter::get_import_order(const String &p_path) const { Ref<ResourceImporter> importer; diff --git a/core/io/resource_importer.h b/core/io/resource_importer.h index eeb486073e..a14d6ba52c 100644 --- a/core/io/resource_importer.h +++ b/core/io/resource_importer.h @@ -72,6 +72,8 @@ public: virtual int get_import_order(const String &p_path) const; + Error get_import_order_threads_and_importer(const String &p_path, int &r_order, bool &r_can_threads, String &r_importer) const; + String get_internal_resource_path(const String &p_path) const; void get_internal_resource_path_list(const String &p_path, List<String> *r_paths); @@ -126,6 +128,9 @@ public: virtual String get_option_group_file() const { return String(); } virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) = 0; + virtual bool can_import_threaded() const { return true; } + virtual void import_threaded_begin() {} + virtual void import_threaded_end() {} virtual Error import_group_file(const String &p_group_file, const Map<String, Map<StringName, Variant>> &p_source_file_options, const Map<String, String> &p_base_paths) { return ERR_UNAVAILABLE; } virtual bool are_import_settings_valid(const String &p_path) const { return true; } diff --git a/core/templates/thread_work_pool.h b/core/templates/thread_work_pool.h index 19ab1dda3a..9f7a692cc5 100644 --- a/core/templates/thread_work_pool.h +++ b/core/templates/thread_work_pool.h @@ -83,7 +83,7 @@ public: ERR_FAIL_COND(!threads); //never initialized ERR_FAIL_COND(current_work != nullptr); - index.store(0); + index.store(0, std::memory_order_release); Work<C, M, U> *w = memnew((Work<C, M, U>)); w->instance = p_instance; @@ -104,8 +104,15 @@ public: return current_work != nullptr; } + bool is_done_dispatching() const { + ERR_FAIL_COND_V(current_work == nullptr, false); + return index.load(std::memory_order_acquire) >= current_work->max_elements; + } + uint32_t get_work_index() const { - return index; + ERR_FAIL_COND_V(current_work == nullptr, 0); + uint32_t idx = index.load(std::memory_order_acquire); + return MIN(idx, current_work->max_elements); } void end_work() { diff --git a/doc/classes/ColorPicker.xml b/doc/classes/ColorPicker.xml index 83223bb645..fddfd27573 100644 --- a/doc/classes/ColorPicker.xml +++ b/doc/classes/ColorPicker.xml @@ -51,7 +51,7 @@ If [code]true[/code], allows editing the color with Hue/Saturation/Value sliders. [b]Note:[/b] Cannot be enabled if raw mode is on. </member> - <member name="picker_shape" type="int" setter="set_picker_shape" getter="get_picker_shape" default="0"> + <member name="picker_shape" type="int" setter="set_picker_shape" getter="get_picker_shape" enum="ColorPicker.PickerShapeType" default="0"> The shape of the color space view. See [enum PickerShapeType]. </member> <member name="presets_enabled" type="bool" setter="set_presets_enabled" getter="are_presets_enabled" default="true"> @@ -122,6 +122,8 @@ <theme_item name="overbright_indicator" type="Texture2D"> The indicator used to signalize that the color value is outside the 0-1 range. </theme_item> + <theme_item name="picker_cursor" type="Texture2D"> + </theme_item> <theme_item name="preset_bg" type="Texture2D"> </theme_item> <theme_item name="screen_picker" type="Texture2D"> diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml index 9d87c9bf9a..91a07f66e0 100644 --- a/doc/classes/Image.xml +++ b/doc/classes/Image.xml @@ -186,7 +186,8 @@ <return type="int" enum="Error"> </return> <description> - Decompresses the image if it is compressed. Returns an error if decompress function is not available. + Decompresses the image if it is VRAM compressed in a supported format. Returns [constant OK] if the format is supported, otherwise [constant ERR_UNAVAILABLE]. + [b]Note:[/b] The following formats can be decompressed: DXT, RGTC, BPTC, PVRTC1. The formats ETC1 and ETC2 are not supported. </description> </method> <method name="detect_alpha" qualifiers="const"> diff --git a/doc/classes/LineEdit.xml b/doc/classes/LineEdit.xml index 360f5c451e..7adf19632e 100644 --- a/doc/classes/LineEdit.xml +++ b/doc/classes/LineEdit.xml @@ -12,34 +12,25 @@ - [kbd]Ctrl + Z[/kbd]: Undo - [kbd]Ctrl + ~[/kbd]: Swap input direction. - [kbd]Ctrl + Shift + Z[/kbd]: Redo - - [kbd]Ctrl + U[/kbd]: Delete text from the cursor position to the beginning of the line - - [kbd]Ctrl + K[/kbd]: Delete text from the cursor position to the end of the line + - [kbd]Ctrl + U[/kbd]: Delete text from the caret position to the beginning of the line + - [kbd]Ctrl + K[/kbd]: Delete text from the caret position to the end of the line - [kbd]Ctrl + A[/kbd]: Select all text - - [kbd]Up Arrow[/kbd]/[kbd]Down Arrow[/kbd]: Move the cursor to the beginning/end of the line + - [kbd]Up Arrow[/kbd]/[kbd]Down Arrow[/kbd]: Move the caret to the beginning/end of the line On macOS, some extra keyboard shortcuts are available: - - [kbd]Ctrl + F[/kbd]: Same as [kbd]Right Arrow[/kbd], move the cursor one character right - - [kbd]Ctrl + B[/kbd]: Same as [kbd]Left Arrow[/kbd], move the cursor one character left - - [kbd]Ctrl + P[/kbd]: Same as [kbd]Up Arrow[/kbd], move the cursor to the previous line - - [kbd]Ctrl + N[/kbd]: Same as [kbd]Down Arrow[/kbd], move the cursor to the next line - - [kbd]Ctrl + D[/kbd]: Same as [kbd]Delete[/kbd], delete the character on the right side of cursor - - [kbd]Ctrl + H[/kbd]: Same as [kbd]Backspace[/kbd], delete the character on the left side of the cursor - - [kbd]Ctrl + A[/kbd]: Same as [kbd]Home[/kbd], move the cursor to the beginning of the line - - [kbd]Ctrl + E[/kbd]: Same as [kbd]End[/kbd], move the cursor to the end of the line - - [kbd]Cmd + Left Arrow[/kbd]: Same as [kbd]Home[/kbd], move the cursor to the beginning of the line - - [kbd]Cmd + Right Arrow[/kbd]: Same as [kbd]End[/kbd], move the cursor to the end of the line + - [kbd]Ctrl + F[/kbd]: Same as [kbd]Right Arrow[/kbd], move the caret one character right + - [kbd]Ctrl + B[/kbd]: Same as [kbd]Left Arrow[/kbd], move the caret one character left + - [kbd]Ctrl + P[/kbd]: Same as [kbd]Up Arrow[/kbd], move the caret to the previous line + - [kbd]Ctrl + N[/kbd]: Same as [kbd]Down Arrow[/kbd], move the caret to the next line + - [kbd]Ctrl + D[/kbd]: Same as [kbd]Delete[/kbd], delete the character on the right side of caret + - [kbd]Ctrl + H[/kbd]: Same as [kbd]Backspace[/kbd], delete the character on the left side of the caret + - [kbd]Ctrl + A[/kbd]: Same as [kbd]Home[/kbd], move the caret to the beginning of the line + - [kbd]Ctrl + E[/kbd]: Same as [kbd]End[/kbd], move the caret to the end of the line + - [kbd]Cmd + Left Arrow[/kbd]: Same as [kbd]Home[/kbd], move the caret to the beginning of the line + - [kbd]Cmd + Right Arrow[/kbd]: Same as [kbd]End[/kbd], move the caret to the end of the line </description> <tutorials> </tutorials> <methods> - <method name="append_at_cursor"> - <return type="void"> - </return> - <argument index="0" name="text" type="String"> - </argument> - <description> - Adds [code]text[/code] after the cursor. If the resulting value is longer than [member max_length], nothing happens. - </description> - </method> <method name="clear"> <return type="void"> </return> @@ -54,11 +45,11 @@ Removes all OpenType features. </description> </method> - <method name="delete_char_at_cursor"> + <method name="delete_char_at_caret"> <return type="void"> </return> <description> - Deletes one character at the cursor's current position (equivalent to pressing [kbd]Delete[/kbd]). + Deletes one character at the caret's current position (equivalent to pressing [kbd]Delete[/kbd]). </description> </method> <method name="delete_text"> @@ -99,7 +90,16 @@ <return type="int"> </return> <description> - Returns the scroll offset due to [member caret_position], as a number of characters. + Returns the scroll offset due to [member caret_column], as a number of characters. + </description> + </method> + <method name="insert_text_at_caret"> + <return type="void"> + </return> + <argument index="0" name="text" type="String"> + </argument> + <description> + Inserts [code]text[/code] at the caret. If the resulting value is longer than [member max_length], nothing happens. </description> </method> <method name="menu_option"> @@ -159,21 +159,21 @@ <member name="align" type="int" setter="set_align" getter="get_align" enum="LineEdit.Align" default="0"> Text alignment as defined in the [enum Align] enum. </member> - <member name="caret_blink" type="bool" setter="cursor_set_blink_enabled" getter="cursor_get_blink_enabled" default="false"> - If [code]true[/code], the caret (visual cursor) blinks. + <member name="caret_blink" type="bool" setter="set_caret_blink_enabled" getter="is_caret_blink_enabled" default="false"> + If [code]true[/code], the caret (text cursor) blinks. </member> - <member name="caret_blink_speed" type="float" setter="cursor_set_blink_speed" getter="cursor_get_blink_speed" default="0.65"> + <member name="caret_blink_speed" type="float" setter="set_caret_blink_speed" getter="get_caret_blink_speed" default="0.65"> Duration (in seconds) of a caret's blinking cycle. </member> - <member name="caret_force_displayed" type="bool" setter="cursor_set_force_displayed" getter="cursor_get_force_displayed" default="false"> + <member name="caret_column" type="int" setter="set_caret_column" getter="get_caret_column" default="0"> + The caret's column position inside the [LineEdit]. When set, the text may scroll to accommodate it. </member> - <member name="caret_mid_grapheme" type="bool" setter="set_mid_grapheme_caret_enabled" getter="get_mid_grapheme_caret_enabled" default="false"> + <member name="caret_force_displayed" type="bool" setter="set_caret_force_displayed" getter="is_caret_force_displayed" default="false"> + </member> + <member name="caret_mid_grapheme" type="bool" setter="set_caret_mid_grapheme_enabled" getter="is_caret_mid_grapheme_enabled" default="false"> Allow moving caret, selecting and removing the individual composite character components. Note: [kbd]Backspace[/kbd] is always removing individual composite character components. </member> - <member name="caret_position" type="int" setter="set_cursor_position" getter="get_cursor_position" default="0"> - The cursor's position inside the [LineEdit]. When set, the text may scroll to accommodate it. - </member> <member name="clear_button_enabled" type="bool" setter="set_clear_button_enabled" getter="is_clear_button_enabled" default="false"> If [code]true[/code], the [LineEdit] will show a clear button if [code]text[/code] is not empty, which can be used to clear the text quickly. </member> @@ -186,7 +186,7 @@ <member name="editable" type="bool" setter="set_editable" getter="is_editable" default="true"> If [code]false[/code], existing text cannot be modified and new text cannot be added. </member> - <member name="expand_to_text_length" type="bool" setter="set_expand_to_text_length" getter="get_expand_to_text_length" default="false"> + <member name="expand_to_text_length" type="bool" setter="set_expand_to_text_length_enabled" getter="is_expand_to_text_length_enabled" default="false"> If [code]true[/code], the [LineEdit] width will increase to stay longer than the [member text]. It will [b]not[/b] compress if the [member text] is shortened. </member> <member name="focus_mode" type="int" setter="set_focus_mode" getter="get_focus_mode" override="true" enum="Control.FocusMode" default="2" /> @@ -276,7 +276,7 @@ Copies the selected text. </constant> <constant name="MENU_PASTE" value="2" enum="MenuItems"> - Pastes the clipboard text over the selected text (or at the cursor's position). + Pastes the clipboard text over the selected text (or at the caret's position). Non-printable escape characters are automatically stripped from the OS clipboard via [method String.strip_escapes]. </constant> <constant name="MENU_CLEAR" value="3" enum="MenuItems"> @@ -359,6 +359,9 @@ </constant> </constants> <theme_items> + <theme_item name="caret_color" type="Color" default="Color( 0.94, 0.94, 0.94, 1 )"> + Color of the [LineEdit]'s caret (text cursor). + </theme_item> <theme_item name="clear" type="Texture2D"> Texture for the clear button. See [member clear_button_enabled]. </theme_item> @@ -368,9 +371,6 @@ <theme_item name="clear_button_color_pressed" type="Color" default="Color( 1, 1, 1, 1 )"> Color used for the clear button when it's pressed. </theme_item> - <theme_item name="cursor_color" type="Color" default="Color( 0.94, 0.94, 0.94, 1 )"> - Color of the [LineEdit]'s visual cursor (caret). - </theme_item> <theme_item name="focus" type="StyleBox"> Background used when [LineEdit] has GUI focus. </theme_item> diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml index b5335e47cd..523f3a0c17 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -38,7 +38,7 @@ </description> </method> <method name="_get_configuration_warnings" qualifiers="virtual"> - <return type="Array"> + <return type="String[]"> </return> <description> The elements in the array returned from this method are displayed as warnings in the Scene Dock if the script that overrides it is a [code]tool[/code] script. diff --git a/doc/classes/PackedByteArray.xml b/doc/classes/PackedByteArray.xml index 21f835a53c..24178c3ff6 100644 --- a/doc/classes/PackedByteArray.xml +++ b/doc/classes/PackedByteArray.xml @@ -61,6 +61,114 @@ Returns a new [PackedByteArray] with the data compressed. Set the compression mode using one of [enum File.CompressionMode]'s constants. </description> </method> + <method name="decode_double" qualifiers="const"> + <return type="float"> + </return> + <argument index="0" name="byte_offset" type="int"> + </argument> + <description> + </description> + </method> + <method name="decode_float" qualifiers="const"> + <return type="float"> + </return> + <argument index="0" name="byte_offset" type="int"> + </argument> + <description> + </description> + </method> + <method name="decode_half" qualifiers="const"> + <return type="float"> + </return> + <argument index="0" name="byte_offset" type="int"> + </argument> + <description> + </description> + </method> + <method name="decode_s16" qualifiers="const"> + <return type="int"> + </return> + <argument index="0" name="byte_offset" type="int"> + </argument> + <description> + </description> + </method> + <method name="decode_s32" qualifiers="const"> + <return type="int"> + </return> + <argument index="0" name="byte_offset" type="int"> + </argument> + <description> + </description> + </method> + <method name="decode_s64" qualifiers="const"> + <return type="int"> + </return> + <argument index="0" name="byte_offset" type="int"> + </argument> + <description> + </description> + </method> + <method name="decode_s8" qualifiers="const"> + <return type="int"> + </return> + <argument index="0" name="byte_offset" type="int"> + </argument> + <description> + </description> + </method> + <method name="decode_u16" qualifiers="const"> + <return type="int"> + </return> + <argument index="0" name="byte_offset" type="int"> + </argument> + <description> + </description> + </method> + <method name="decode_u32" qualifiers="const"> + <return type="int"> + </return> + <argument index="0" name="byte_offset" type="int"> + </argument> + <description> + </description> + </method> + <method name="decode_u64" qualifiers="const"> + <return type="int"> + </return> + <argument index="0" name="byte_offset" type="int"> + </argument> + <description> + </description> + </method> + <method name="decode_u8" qualifiers="const"> + <return type="int"> + </return> + <argument index="0" name="byte_offset" type="int"> + </argument> + <description> + </description> + </method> + <method name="decode_var" qualifiers="const"> + <return type="Variant"> + </return> + <argument index="0" name="byte_offset" type="int"> + </argument> + <argument index="1" name="allow_objects" type="bool" default="false"> + </argument> + <description> + </description> + </method> + <method name="decode_var_size" qualifiers="const"> + <return type="int"> + </return> + <argument index="0" name="byte_offset" type="int"> + </argument> + <argument index="1" name="allow_objects" type="bool" default="false"> + </argument> + <description> + </description> + </method> <method name="decompress" qualifiers="const"> <return type="PackedByteArray"> </return> @@ -92,6 +200,128 @@ Creates a copy of the array, and returns it. </description> </method> + <method name="encode_double"> + <return type="void"> + </return> + <argument index="0" name="byte_offset" type="int"> + </argument> + <argument index="1" name="value" type="float"> + </argument> + <description> + </description> + </method> + <method name="encode_float"> + <return type="void"> + </return> + <argument index="0" name="byte_offset" type="int"> + </argument> + <argument index="1" name="value" type="float"> + </argument> + <description> + </description> + </method> + <method name="encode_half"> + <return type="void"> + </return> + <argument index="0" name="byte_offset" type="int"> + </argument> + <argument index="1" name="value" type="float"> + </argument> + <description> + </description> + </method> + <method name="encode_s16"> + <return type="void"> + </return> + <argument index="0" name="byte_offset" type="int"> + </argument> + <argument index="1" name="value" type="int"> + </argument> + <description> + </description> + </method> + <method name="encode_s32"> + <return type="void"> + </return> + <argument index="0" name="byte_offset" type="int"> + </argument> + <argument index="1" name="value" type="int"> + </argument> + <description> + </description> + </method> + <method name="encode_s64"> + <return type="void"> + </return> + <argument index="0" name="byte_offset" type="int"> + </argument> + <argument index="1" name="value" type="int"> + </argument> + <description> + </description> + </method> + <method name="encode_s8"> + <return type="void"> + </return> + <argument index="0" name="byte_offset" type="int"> + </argument> + <argument index="1" name="value" type="int"> + </argument> + <description> + </description> + </method> + <method name="encode_u16"> + <return type="void"> + </return> + <argument index="0" name="byte_offset" type="int"> + </argument> + <argument index="1" name="value" type="int"> + </argument> + <description> + </description> + </method> + <method name="encode_u32"> + <return type="void"> + </return> + <argument index="0" name="byte_offset" type="int"> + </argument> + <argument index="1" name="value" type="int"> + </argument> + <description> + </description> + </method> + <method name="encode_u64"> + <return type="void"> + </return> + <argument index="0" name="byte_offset" type="int"> + </argument> + <argument index="1" name="value" type="int"> + </argument> + <description> + </description> + </method> + <method name="encode_u8"> + <return type="void"> + </return> + <argument index="0" name="byte_offset" type="int"> + </argument> + <argument index="1" name="value" type="int"> + </argument> + <description> + </description> + </method> + <method name="encode_var"> + <return type="int"> + </return> + <argument index="0" name="byte_offset" type="int"> + </argument> + <argument index="1" name="value" type="Variant"> + </argument> + <argument index="2" name="allow_objects" type="bool" default="false"> + </argument> + <description> + </description> + </method> <method name="get_string_from_ascii" qualifiers="const"> <return type="String"> </return> @@ -129,6 +359,16 @@ Returns [code]true[/code] if the array contains [code]value[/code]. </description> </method> + <method name="has_encoded_var" qualifiers="const"> + <return type="bool"> + </return> + <argument index="0" name="byte_offset" type="int"> + </argument> + <argument index="1" name="allow_objects" type="bool" default="false"> + </argument> + <description> + </description> + </method> <method name="hex_encode" qualifiers="const"> <return type="String"> </return> diff --git a/doc/classes/PackedFloat32Array.xml b/doc/classes/PackedFloat32Array.xml index 6be1d24b5d..5e0008852c 100644 --- a/doc/classes/PackedFloat32Array.xml +++ b/doc/classes/PackedFloat32Array.xml @@ -111,6 +111,14 @@ <description> </description> </method> + <method name="operator []" qualifiers="operator"> + <return type="float"> + </return> + <argument index="0" name="index" type="int"> + </argument> + <description> + </description> + </method> <method name="push_back"> <return type="bool"> </return> diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 5cac715408..c8fd0d667b 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -1149,7 +1149,7 @@ <member name="navigation/3d/default_cell_size" type="float" setter="" getter="" default="0.3"> Default cell size for 3D navigation maps. See [method NavigationServer3D.map_set_cell_size]. </member> - <member name="navigation/3d/default_edge_connection_margin" type="float" setter="" getter="" default="5.0"> + <member name="navigation/3d/default_edge_connection_margin" type="float" setter="" getter="" default="0.3"> Default edge connection margin for 3D navigation maps. See [method NavigationServer3D.map_set_edge_connection_margin]. </member> <member name="network/limits/debugger/max_chars_per_second" type="int" setter="" getter="" default="32768"> diff --git a/doc/classes/VisualShader.xml b/doc/classes/VisualShader.xml index c29c30289a..ff00a848b9 100644 --- a/doc/classes/VisualShader.xml +++ b/doc/classes/VisualShader.xml @@ -228,7 +228,9 @@ </constant> <constant name="TYPE_END" value="5" enum="Type"> </constant> - <constant name="TYPE_MAX" value="6" enum="Type"> + <constant name="TYPE_SKY" value="6" enum="Type"> + </constant> + <constant name="TYPE_MAX" value="7" enum="Type"> Represents the size of the [enum Type] enum. </constant> <constant name="NODE_ID_INVALID" value="-1"> diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 4fe2d2bb2a..9db2f0a287 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -2734,7 +2734,7 @@ void AnimationTrackEdit::_gui_input(const Ref<InputEvent> &p_event) { path_popup->set_size(path_rect.size); path_popup->popup(); path->grab_focus(); - path->set_cursor_position(path->get_text().length()); + path->set_caret_column(path->get_text().length()); clicking_on_name = false; } diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index 6ed2cb9d9c..1c62c3d3e1 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -488,10 +488,10 @@ void FindReplaceBar::_show_search(bool p_focus_replace, bool p_show_only) { if (!get_search_text().is_empty()) { if (p_focus_replace) { replace_text->select_all(); - replace_text->set_cursor_position(replace_text->get_text().length()); + replace_text->set_caret_column(replace_text->get_text().length()); } else { search_text->select_all(); - search_text->set_cursor_position(search_text->get_text().length()); + search_text->set_caret_column(search_text->get_text().length()); } results_count = -1; diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index fb0dc57501..59d3b09678 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -1922,6 +1922,11 @@ void EditorFileSystem::reimport_file_with_custom_parameters(const String &p_file _reimport_file(p_file, &p_custom_params, p_importer); } +void EditorFileSystem::_reimport_thread(uint32_t p_index, ImportThreadData *p_import_data) { + p_import_data->max_index = MAX(p_import_data->reimport_from + int(p_index), p_import_data->max_index); + _reimport_file(p_import_data->reimport_files[p_import_data->reimport_from + p_index].path); +} + void EditorFileSystem::reimport_files(const Vector<String> &p_files) { { // Ensure that ProjectSettings::IMPORTED_FILES_PATH exists. @@ -1939,7 +1944,8 @@ void EditorFileSystem::reimport_files(const Vector<String> &p_files) { importing = true; EditorProgress pr("reimport", TTR("(Re)Importing Assets"), p_files.size()); - Vector<ImportFile> files; + Vector<ImportFile> reimport_files; + Set<String> groups_to_reimport; for (int i = 0; i < p_files.size(); i++) { @@ -1957,8 +1963,8 @@ void EditorFileSystem::reimport_files(const Vector<String> &p_files) { //it's a regular file ImportFile ifile; ifile.path = p_files[i]; - ifile.order = ResourceFormatImporter::get_singleton()->get_import_order(p_files[i]); - files.push_back(ifile); + ResourceFormatImporter::get_singleton()->get_import_order_threads_and_importer(p_files[i], ifile.order, ifile.threaded, ifile.importer); + reimport_files.push_back(ifile); } //group may have changed, so also update group reference @@ -1969,11 +1975,51 @@ void EditorFileSystem::reimport_files(const Vector<String> &p_files) { } } - files.sort(); + reimport_files.sort(); - for (int i = 0; i < files.size(); i++) { - pr.step(files[i].path.get_file(), i); - _reimport_file(files[i].path); + bool use_threads = GLOBAL_GET("editor/import/use_multiple_threads"); + + int from = 0; + for (int i = 0; i < reimport_files.size(); i++) { + if (use_threads && reimport_files[i].threaded) { + if (i + 1 == reimport_files.size() || reimport_files[i + 1].importer != reimport_files[from].importer) { + if (from - i == 0) { + //single file, do not use threads + pr.step(reimport_files[i].path.get_file(), i); + _reimport_file(reimport_files[i].path); + } else { + Ref<ResourceImporter> importer = ResourceFormatImporter::get_singleton()->get_importer_by_name(reimport_files[from].importer); + ERR_CONTINUE(!importer.is_valid()); + + importer->import_threaded_begin(); + + ImportThreadData data; + data.max_index = from; + data.reimport_from = from; + data.reimport_files = reimport_files.ptr(); + + import_threads.begin_work(i - from + 1, this, &EditorFileSystem::_reimport_thread, &data); + int current_index = from - 1; + do { + if (current_index < data.max_index) { + current_index = data.max_index; + pr.step(reimport_files[current_index].path.get_file(), current_index); + } + OS::get_singleton()->delay_usec(1); + } while (!import_threads.is_done_dispatching()); + + import_threads.end_work(); + + importer->import_threaded_end(); + } + + from = i + 1; + } + + } else { + pr.step(reimport_files[i].path.get_file(), i); + _reimport_file(reimport_files[i].path); + } } //reimport groups @@ -2111,7 +2157,7 @@ void EditorFileSystem::_update_extensions() { EditorFileSystem::EditorFileSystem() { ResourceLoader::import = _resource_import; reimport_on_missing_imported_files = GLOBAL_DEF("editor/import/reimport_missing_imported_files", true); - + GLOBAL_DEF("editor/import/use_multiple_threads", true); singleton = this; filesystem = memnew(EditorFileSystemDirectory); //like, empty filesystem->parent = nullptr; @@ -2138,7 +2184,9 @@ EditorFileSystem::EditorFileSystem() { first_scan = true; scan_changes_pending = false; revalidate_import_files = false; + import_threads.init(); } EditorFileSystem::~EditorFileSystem() { + import_threads.finish(); } diff --git a/editor/editor_file_system.h b/editor/editor_file_system.h index 6f4f058503..9c9076106c 100644 --- a/editor/editor_file_system.h +++ b/editor/editor_file_system.h @@ -36,7 +36,9 @@ #include "core/os/thread_safe.h" #include "core/templates/safe_refcount.h" #include "core/templates/set.h" +#include "core/templates/thread_work_pool.h" #include "scene/main/node.h" + class FileAccess; struct EditorProgressBG; @@ -214,9 +216,11 @@ class EditorFileSystem : public Node { struct ImportFile { String path; + String importer; + bool threaded = false; int order = 0; bool operator<(const ImportFile &p_if) const { - return order < p_if.order; + return order == p_if.order ? (importer < p_if.importer) : (order < p_if.order); } }; @@ -236,6 +240,16 @@ class EditorFileSystem : public Node { Set<String> group_file_cache; + ThreadWorkPool import_threads; + + struct ImportThreadData { + const ImportFile *reimport_files; + int reimport_from; + int max_index = 0; + }; + + void _reimport_thread(uint32_t p_index, ImportThreadData *p_import_data); + protected: void _notification(int p_what); static void _bind_methods(); diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index a747652a2f..6039f64b7c 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -1801,7 +1801,7 @@ void FindBar::popup_search() { if (!search_text->get_text().is_empty()) { search_text->select_all(); - search_text->set_cursor_position(search_text->get_text().length()); + search_text->set_caret_column(search_text->get_text().length()); if (grabbed_focus) { _search(); } diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index 4c5c3af765..7cc9ebd63e 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -963,7 +963,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { theme->set_color("read_only", "LineEdit", font_disabled_color); theme->set_color("font_color", "LineEdit", font_color); theme->set_color("font_selected_color", "LineEdit", mono_color); - theme->set_color("cursor_color", "LineEdit", font_color); + theme->set_color("caret_color", "LineEdit", font_color); theme->set_color("selection_color", "LineEdit", selection_color); theme->set_color("clear_button_color", "LineEdit", font_color); theme->set_color("clear_button_color_pressed", "LineEdit", accent_color); diff --git a/editor/import/resource_importer_scene.h b/editor/import/resource_importer_scene.h index 6c6af57c4c..00039f2ac6 100644 --- a/editor/import/resource_importer_scene.h +++ b/editor/import/resource_importer_scene.h @@ -173,6 +173,8 @@ public: virtual bool has_advanced_options() const override; virtual void show_advanced_options(const String &p_path) override; + virtual bool can_import_threaded() const override { return false; } + ResourceImporterScene(); }; diff --git a/editor/plugins/animation_blend_space_1d_editor.cpp b/editor/plugins/animation_blend_space_1d_editor.cpp index 025fcaf818..f7c0ebcfaf 100644 --- a/editor/plugins/animation_blend_space_1d_editor.cpp +++ b/editor/plugins/animation_blend_space_1d_editor.cpp @@ -698,7 +698,7 @@ AnimationNodeBlendSpace1DEditor::AnimationNodeBlendSpace1DEditor() { max_value->set_step(0.01); label_value = memnew(LineEdit); - label_value->set_expand_to_text_length(true); + label_value->set_expand_to_text_length_enabled(true); // now add diff --git a/editor/plugins/animation_blend_space_2d_editor.cpp b/editor/plugins/animation_blend_space_2d_editor.cpp index af9c391174..e719df53d5 100644 --- a/editor/plugins/animation_blend_space_2d_editor.cpp +++ b/editor/plugins/animation_blend_space_2d_editor.cpp @@ -942,7 +942,7 @@ AnimationNodeBlendSpace2DEditor::AnimationNodeBlendSpace2DEditor() { left_vbox->add_spacer(); label_y = memnew(LineEdit); left_vbox->add_child(label_y); - label_y->set_expand_to_text_length(true); + label_y->set_expand_to_text_length_enabled(true); left_vbox->add_spacer(); min_y_value = memnew(SpinBox); left_vbox->add_child(min_y_value); @@ -978,7 +978,7 @@ AnimationNodeBlendSpace2DEditor::AnimationNodeBlendSpace2DEditor() { bottom_vbox->add_spacer(); label_x = memnew(LineEdit); bottom_vbox->add_child(label_x); - label_x->set_expand_to_text_length(true); + label_x->set_expand_to_text_length_enabled(true); bottom_vbox->add_spacer(); max_x_value = memnew(SpinBox); bottom_vbox->add_child(max_x_value); diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp index fdbbe5184b..48fb507bb1 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.cpp +++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp @@ -136,7 +136,7 @@ void AnimationNodeBlendTreeEditor::_update_graph() { if (String(E->get()) != "output") { LineEdit *name = memnew(LineEdit); name->set_text(E->get()); - name->set_expand_to_text_length(true); + name->set_expand_to_text_length_enabled(true); node->add_child(name); node->set_slot(0, false, 0, Color(), true, 0, get_theme_color("font_color", "Label")); name->connect("text_entered", callable_mp(this, &AnimationNodeBlendTreeEditor::_node_renamed), varray(agnode), CONNECT_DEFERRED); diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index e5b8dfd464..dea85e8799 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -1133,16 +1133,24 @@ void VisualShaderEditor::_update_options_menu() { } void VisualShaderEditor::_set_mode(int p_which) { - if (p_which == VisualShader::MODE_PARTICLES) { + if (p_which == VisualShader::MODE_SKY) { + edit_type_standart->set_visible(false); + edit_type_particles->set_visible(false); + edit_type_sky->set_visible(true); + edit_type = edit_type_sky; + mode = MODE_FLAGS_SKY; + } else if (p_which == VisualShader::MODE_PARTICLES) { edit_type_standart->set_visible(false); edit_type_particles->set_visible(true); + edit_type_sky->set_visible(false); edit_type = edit_type_particles; - particles_mode = true; + mode = MODE_FLAGS_PARTICLES; } else { edit_type_particles->set_visible(false); edit_type_standart->set_visible(true); + edit_type_sky->set_visible(false); edit_type = edit_type_standart; - particles_mode = false; + mode = MODE_FLAGS_SPATIAL_CANVASITEM; } visual_shader->set_shader_type(get_current_shader_type()); } @@ -1303,8 +1311,10 @@ void VisualShaderEditor::_update_graph() { VisualShader::Type VisualShaderEditor::get_current_shader_type() const { VisualShader::Type type; - if (particles_mode) { + if (mode & MODE_FLAGS_PARTICLES) { type = VisualShader::Type(edit_type->get_selected() + 3); + } else if (mode & MODE_FLAGS_SKY) { + type = VisualShader::Type(edit_type->get_selected() + 6); } else { type = VisualShader::Type(edit_type->get_selected()); } @@ -3025,7 +3035,14 @@ void VisualShaderEditor::_paste_nodes(bool p_use_custom_position, const Vector2 } void VisualShaderEditor::_mode_selected(int p_id) { - visual_shader->set_shader_type(particles_mode ? VisualShader::Type(p_id + 3) : VisualShader::Type(p_id)); + int offset = 0; + if (mode & MODE_FLAGS_PARTICLES) { + offset = 3; + } else if (mode & MODE_FLAGS_SKY) { + offset = 6; + } + + visual_shader->set_shader_type(VisualShader::Type(p_id + offset)); _update_options_menu(); _update_graph(); } @@ -3531,10 +3548,17 @@ VisualShaderEditor::VisualShaderEditor() { edit_type_particles->select(0); edit_type_particles->connect("item_selected", callable_mp(this, &VisualShaderEditor::_mode_selected)); + edit_type_sky = memnew(OptionButton); + edit_type_sky->add_item(TTR("Sky")); + edit_type_sky->select(0); + edit_type_sky->connect("item_selected", callable_mp(this, &VisualShaderEditor::_mode_selected)); + edit_type = edit_type_standart; graph->get_zoom_hbox()->add_child(edit_type_particles); graph->get_zoom_hbox()->move_child(edit_type_particles, 0); + graph->get_zoom_hbox()->add_child(edit_type_sky); + graph->get_zoom_hbox()->move_child(edit_type_sky, 0); graph->get_zoom_hbox()->add_child(edit_type_standart); graph->get_zoom_hbox()->move_child(edit_type_standart, 0); @@ -3671,7 +3695,7 @@ VisualShaderEditor::VisualShaderEditor() { comment_title_change_popup = memnew(PopupPanel); comment_title_change_edit = memnew(LineEdit); - comment_title_change_edit->set_expand_to_text_length(true); + comment_title_change_edit->set_expand_to_text_length_enabled(true); comment_title_change_edit->connect("text_changed", callable_mp(this, &VisualShaderEditor::_comment_title_text_changed)); comment_title_change_edit->connect("text_entered", callable_mp(this, &VisualShaderEditor::_comment_title_text_entered)); comment_title_change_popup->add_child(comment_title_change_edit); @@ -3782,6 +3806,7 @@ VisualShaderEditor::VisualShaderEditor() { const String input_param_for_vertex_and_fragment_shader_modes = TTR("'%s' input parameter for vertex and fragment shader modes."); const String input_param_for_fragment_and_light_shader_modes = TTR("'%s' input parameter for fragment and light shader modes."); const String input_param_for_fragment_shader_mode = TTR("'%s' input parameter for fragment shader mode."); + const String input_param_for_sky_shader_mode = TTR("'%s' input parameter for sky shader mode."); const String input_param_for_light_shader_mode = TTR("'%s' input parameter for light shader mode."); const String input_param_for_vertex_shader_mode = TTR("'%s' input parameter for vertex shader mode."); const String input_param_for_emit_shader_mode = TTR("'%s' input parameter for emit shader mode."); @@ -3911,35 +3936,35 @@ VisualShaderEditor::VisualShaderEditor() { // SKY INPUTS - add_options.push_back(AddOption("AtCubeMapPass", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "at_cubemap_pass"), "at_cubemap_pass", VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY)); - add_options.push_back(AddOption("AtHalfResPass", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "at_half_res_pass"), "at_half_res_pass", VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY)); - add_options.push_back(AddOption("AtQuarterResPass", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "at_quarter_res_pass"), "at_quarter_res_pass", VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY)); - add_options.push_back(AddOption("EyeDir", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "eyedir"), "eyedir", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY)); - add_options.push_back(AddOption("HalfResColor", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "half_res_color"), "half_res_color", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY)); - add_options.push_back(AddOption("HalfResAlpha", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "half_res_alpha"), "half_res_alpha", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY)); - add_options.push_back(AddOption("Light0Color", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "light0_color"), "light0_color", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY)); - add_options.push_back(AddOption("Light0Direction", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "light0_direction"), "light0_direction", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY)); - add_options.push_back(AddOption("Light0Enabled", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "light0_enabled"), "light0_enabled", VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY)); - add_options.push_back(AddOption("Light0Energy", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "light0_energy"), "light0_energy", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY)); - add_options.push_back(AddOption("Light1Color", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "light1_color"), "light1_color", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY)); - add_options.push_back(AddOption("Light1Direction", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "light1_direction"), "light1_direction", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY)); - add_options.push_back(AddOption("Light1Enabled", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "light1_enabled"), "light1_enabled", VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY)); - add_options.push_back(AddOption("Light1Energy", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "light1_energy"), "light1_energy", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY)); - add_options.push_back(AddOption("Light2Color", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "light2_color"), "light2_color", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY)); - add_options.push_back(AddOption("Light2Direction", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "light2_direction"), "light2_direction", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY)); - add_options.push_back(AddOption("Light2Enabled", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "light2_enabled"), "light2_enabled", VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY)); - add_options.push_back(AddOption("Light2Energy", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "light2_energy"), "light2_energy", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY)); - add_options.push_back(AddOption("Light3Color", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "light3_color"), "light3_color", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY)); - add_options.push_back(AddOption("Light3Direction", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "light3_direction"), "light3_direction", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY)); - add_options.push_back(AddOption("Light3Enabled", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "light3_enabled"), "light3_enabled", VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY)); - add_options.push_back(AddOption("Light3Energy", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "light3_energy"), "light3_energy", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY)); - add_options.push_back(AddOption("Position", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "position"), "position", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY)); - add_options.push_back(AddOption("QuarterResColor", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "quarter_res_color"), "quarter_res_color", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY)); - add_options.push_back(AddOption("QuarterResAlpha", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "quarter_res_alpha"), "quarter_res_alpha", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY)); - add_options.push_back(AddOption("Radiance", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "radiance"), "radiance", VisualShaderNode::PORT_TYPE_SAMPLER, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY)); - add_options.push_back(AddOption("ScreenUV", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "screen_uv"), "screen_uv", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY)); - add_options.push_back(AddOption("SkyCoords", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "sky_coords"), "sky_coords", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY)); - add_options.push_back(AddOption("Time", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "time"), "time", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SKY)); + add_options.push_back(AddOption("AtCubeMapPass", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "at_cubemap_pass"), "at_cubemap_pass", VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_SKY, Shader::MODE_SKY)); + add_options.push_back(AddOption("AtHalfResPass", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "at_half_res_pass"), "at_half_res_pass", VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_SKY, Shader::MODE_SKY)); + add_options.push_back(AddOption("AtQuarterResPass", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "at_quarter_res_pass"), "at_quarter_res_pass", VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_SKY, Shader::MODE_SKY)); + add_options.push_back(AddOption("EyeDir", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "eyedir"), "eyedir", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY)); + add_options.push_back(AddOption("HalfResColor", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "half_res_color"), "half_res_color", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY)); + add_options.push_back(AddOption("HalfResAlpha", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "half_res_alpha"), "half_res_alpha", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_SKY, Shader::MODE_SKY)); + add_options.push_back(AddOption("Light0Color", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light0_color"), "light0_color", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY)); + add_options.push_back(AddOption("Light0Direction", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light0_direction"), "light0_direction", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY)); + add_options.push_back(AddOption("Light0Enabled", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light0_enabled"), "light0_enabled", VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_SKY, Shader::MODE_SKY)); + add_options.push_back(AddOption("Light0Energy", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light0_energy"), "light0_energy", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_SKY, Shader::MODE_SKY)); + add_options.push_back(AddOption("Light1Color", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light1_color"), "light1_color", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY)); + add_options.push_back(AddOption("Light1Direction", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light1_direction"), "light1_direction", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY)); + add_options.push_back(AddOption("Light1Enabled", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light1_enabled"), "light1_enabled", VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_SKY, Shader::MODE_SKY)); + add_options.push_back(AddOption("Light1Energy", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light1_energy"), "light1_energy", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_SKY, Shader::MODE_SKY)); + add_options.push_back(AddOption("Light2Color", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light2_color"), "light2_color", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY)); + add_options.push_back(AddOption("Light2Direction", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light2_direction"), "light2_direction", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY)); + add_options.push_back(AddOption("Light2Enabled", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light2_enabled"), "light2_enabled", VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_SKY, Shader::MODE_SKY)); + add_options.push_back(AddOption("Light2Energy", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light2_energy"), "light2_energy", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_SKY, Shader::MODE_SKY)); + add_options.push_back(AddOption("Light3Color", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light3_color"), "light3_color", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY)); + add_options.push_back(AddOption("Light3Direction", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light3_direction"), "light3_direction", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY)); + add_options.push_back(AddOption("Light3Enabled", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light3_enabled"), "light3_enabled", VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_SKY, Shader::MODE_SKY)); + add_options.push_back(AddOption("Light3Energy", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light3_energy"), "light3_energy", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_SKY, Shader::MODE_SKY)); + add_options.push_back(AddOption("Position", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "position"), "position", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY)); + add_options.push_back(AddOption("QuarterResColor", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "quarter_res_color"), "quarter_res_color", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY)); + add_options.push_back(AddOption("QuarterResAlpha", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "quarter_res_alpha"), "quarter_res_alpha", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_SKY, Shader::MODE_SKY)); + add_options.push_back(AddOption("Radiance", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "radiance"), "radiance", VisualShaderNode::PORT_TYPE_SAMPLER, TYPE_FLAGS_SKY, Shader::MODE_SKY)); + add_options.push_back(AddOption("ScreenUV", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "screen_uv"), "screen_uv", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY)); + add_options.push_back(AddOption("SkyCoords", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "sky_coords"), "sky_coords", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY)); + add_options.push_back(AddOption("Time", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "time"), "time", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_SKY, Shader::MODE_SKY)); // SCALAR diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h index 517dc6056f..6d57d38cab 100644 --- a/editor/plugins/visual_shader_editor_plugin.h +++ b/editor/plugins/visual_shader_editor_plugin.h @@ -141,6 +141,7 @@ class VisualShaderEditor : public VBoxContainer { OptionButton *edit_type = nullptr; OptionButton *edit_type_standart; OptionButton *edit_type_particles; + OptionButton *edit_type_sky; PanelContainer *error_panel; Label *error_label; @@ -169,7 +170,14 @@ class VisualShaderEditor : public VBoxContainer { bool preview_first = true; bool preview_showed = false; - bool particles_mode; + + enum ShaderModeFlags { + MODE_FLAGS_SPATIAL_CANVASITEM = 1, + MODE_FLAGS_SKY = 2, + MODE_FLAGS_PARTICLES = 4 + }; + + int mode = MODE_FLAGS_SPATIAL_CANVASITEM; enum TypeFlags { TYPE_FLAGS_VERTEX = 1, @@ -183,6 +191,10 @@ class VisualShaderEditor : public VBoxContainer { TYPE_FLAGS_END = 4 }; + enum SkyTypeFlags { + TYPE_FLAGS_SKY = 1, + }; + enum ToolsMenuOptions { EXPAND_ALL, COLLAPSE_ALL diff --git a/editor/rename_dialog.cpp b/editor/rename_dialog.cpp index b51524b299..0f15d4b119 100644 --- a/editor/rename_dialog.cpp +++ b/editor/rename_dialog.cpp @@ -632,7 +632,7 @@ void RenameDialog::_insert_text(String text) { if (_is_main_field(focus_owner)) { focus_owner->selection_delete(); - focus_owner->append_at_cursor(text); + focus_owner->insert_text_at_caret(text); _update_preview(); } } diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 5e6ebc22a3..a6d1a118b8 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -140,7 +140,11 @@ void SceneTreeDock::instance_scenes(const Vector<String> &p_files, Node *p_paren parent = scene_tree->get_selected(); } - if (!parent || !edited_scene) { + if (!parent) { + parent = edited_scene; + } + + if (!parent) { if (p_files.size() == 1) { accept->set_text(TTR("No parent to instance a child at.")); } else { diff --git a/editor/script_create_dialog.cpp b/editor/script_create_dialog.cpp index 288cc2db48..f3addd8904 100644 --- a/editor/script_create_dialog.cpp +++ b/editor/script_create_dialog.cpp @@ -87,8 +87,8 @@ void ScriptCreateDialog::_path_hbox_sorted() { // First set cursor to the end of line to scroll LineEdit view // to the right and then set the actual cursor position. - file_path->set_cursor_position(file_path->get_text().length()); - file_path->set_cursor_position(filename_start_pos); + file_path->set_caret_column(file_path->get_text().length()); + file_path->set_caret_column(filename_start_pos); file_path->grab_focus(); } @@ -557,7 +557,7 @@ void ScriptCreateDialog::_file_selected(const String &p_file) { String filename = p.get_file().get_basename(); int select_start = p.rfind(filename); file_path->select(select_start, select_start + filename.length()); - file_path->set_cursor_position(select_start + filename.length()); + file_path->set_caret_column(select_start + filename.length()); file_path->grab_focus(); } } diff --git a/modules/etcpak/image_etcpak.cpp b/modules/etcpak/image_compress_etcpak.cpp index 251d2cd7b0..abc3c26188 100644 --- a/modules/etcpak/image_etcpak.cpp +++ b/modules/etcpak/image_compress_etcpak.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* image_etcpak.cpp */ +/* image_compress_etcpak.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,21 +28,16 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "image_etcpak.h" +#include "image_compress_etcpak.h" -#include "core/os/copymem.h" #include "core/os/os.h" #include "core/string/print_string.h" #include "thirdparty/etcpak/ProcessDxtc.hpp" #include "thirdparty/etcpak/ProcessRGB.hpp" -// thresholds for the early compression-mode decision scheme in QuickETC2 -// which can be changed by the option -e -float ecmd_threshold[3] = { 0.03f, 0.09f, 0.38f }; - -EtcpakType _determine_etc_type(Image::UsedChannels p_source) { - switch (p_source) { +EtcpakType _determine_etc_type(Image::UsedChannels p_channels) { + switch (p_channels) { case Image::USED_CHANNELS_L: return EtcpakType::ETCPAK_TYPE_ETC1; case Image::USED_CHANNELS_LA: @@ -60,8 +55,8 @@ EtcpakType _determine_etc_type(Image::UsedChannels p_source) { } } -EtcpakType _determine_dxt_type(Image::UsedChannels p_source) { - switch (p_source) { +EtcpakType _determine_dxt_type(Image::UsedChannels p_channels) { + switch (p_channels) { case Image::USED_CHANNELS_L: return EtcpakType::ETCPAK_TYPE_DXT1; case Image::USED_CHANNELS_LA: @@ -78,93 +73,112 @@ EtcpakType _determine_dxt_type(Image::UsedChannels p_source) { return EtcpakType::ETCPAK_TYPE_DXT5; } } -void _compress_etc2(Image *p_img, float p_lossy_quality, Image::UsedChannels p_source) { - EtcpakType type = _determine_etc_type(p_source); - _compress_etcpak(type, p_img, p_lossy_quality, false, p_source); + +void _compress_etc1(Image *r_img, float p_lossy_quality) { + _compress_etcpak(EtcpakType::ETCPAK_TYPE_ETC1, r_img, p_lossy_quality); } -void _compress_bc(Image *p_img, float p_lossy_quality, Image::UsedChannels p_source) { - EtcpakType type = _determine_dxt_type(p_source); - _compress_etcpak(type, p_img, p_lossy_quality, false, p_source); + +void _compress_etc2(Image *r_img, float p_lossy_quality, Image::UsedChannels p_channels) { + EtcpakType type = _determine_etc_type(p_channels); + _compress_etcpak(type, r_img, p_lossy_quality); } -void _compress_etc1(Image *p_img, float p_lossy_quality) { - _compress_etcpak(EtcpakType::ETCPAK_TYPE_ETC1, p_img, p_lossy_quality, true, Image::USED_CHANNELS_RGB); + +void _compress_bc(Image *r_img, float p_lossy_quality, Image::UsedChannels p_channels) { + EtcpakType type = _determine_dxt_type(p_channels); + _compress_etcpak(type, r_img, p_lossy_quality); } -void _compress_etcpak(EtcpakType p_compresstype, Image *p_img, float p_lossy_quality, bool force_etc1_format, Image::UsedChannels p_channels) { - uint64_t t = OS::get_singleton()->get_ticks_msec(); - Image::Format img_format = p_img->get_format(); +void _compress_etcpak(EtcpakType p_compresstype, Image *r_img, float p_lossy_quality) { + uint64_t start_time = OS::get_singleton()->get_ticks_msec(); + // TODO: See how to handle lossy quality. + + Image::Format img_format = r_img->get_format(); if (img_format >= Image::FORMAT_DXT1) { - return; //do not compress, already compressed + return; // Do not compress, already compressed. } - if (img_format > Image::FORMAT_RGBA8) { // TODO: we should be able to handle FORMAT_RGBA4444 and FORMAT_RGBA5551 eventually return; } - Image::Format format = Image::FORMAT_RGBA8; - if (p_img->get_format() != Image::FORMAT_RGBA8) { - p_img->convert(Image::FORMAT_RGBA8); + // Use RGBA8 to convert. + if (img_format != Image::FORMAT_RGBA8) { + r_img->convert(Image::FORMAT_RGBA8); } - if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC1 || force_etc1_format) { - format = Image::FORMAT_ETC; + + // Determine output format based on Etcpak type. + Image::Format target_format = Image::FORMAT_RGBA8; + if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC1) { + target_format = Image::FORMAT_ETC; } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2) { - format = Image::FORMAT_ETC2_RGB8; + target_format = Image::FORMAT_ETC2_RGB8; } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_RA_AS_RG) { - format = Image::FORMAT_ETC2_RA_AS_RG; - p_img->convert_rg_to_ra_rgba8(); + target_format = Image::FORMAT_ETC2_RA_AS_RG; + r_img->convert_rg_to_ra_rgba8(); } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_ALPHA) { - format = Image::FORMAT_ETC2_RGBA8; + target_format = Image::FORMAT_ETC2_RGBA8; } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_DXT1) { - format = Image::FORMAT_DXT1; + target_format = Image::FORMAT_DXT1; } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_DXT5_RA_AS_RG) { - format = Image::FORMAT_DXT5_RA_AS_RG; - p_img->convert_rg_to_ra_rgba8(); + target_format = Image::FORMAT_DXT5_RA_AS_RG; + r_img->convert_rg_to_ra_rgba8(); } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_DXT5) { - format = Image::FORMAT_DXT5; + target_format = Image::FORMAT_DXT5; } else { - ERR_FAIL(); + ERR_FAIL_MSG("Invalid or unsupported Etcpak compression format."); } - const bool mipmap = p_img->has_mipmaps(); - print_verbose("Encoding format: " + Image::get_format_name(format)); + // Compress image data and (if required) mipmaps. + + const bool mipmaps = r_img->has_mipmaps(); + const int width = r_img->get_width(); + const int height = r_img->get_height(); + const uint8_t *src_read = r_img->get_data().ptr(); - Ref<Image> new_img; - new_img.instance(); - new_img->create(p_img->get_width(), p_img->get_height(), mipmap, format); - Vector<uint8_t> data = new_img->get_data(); - uint8_t *wr = data.ptrw(); + print_verbose(vformat("ETCPAK: Encoding image size %dx%d to format %s.", width, height, Image::get_format_name(target_format))); - Ref<Image> image = p_img->duplicate(); - int mmc = 1 + (mipmap ? Image::get_image_required_mipmaps(new_img->get_width(), new_img->get_height(), format) : 0); - for (int i = 0; i < mmc; i++) { - int ofs, size, mip_w, mip_h; - new_img->get_mipmap_offset_size_and_dimensions(i, ofs, size, mip_w, mip_h); + int dest_size = Image::get_image_data_size(width, height, target_format, mipmaps); + Vector<uint8_t> dest_data; + dest_data.resize(dest_size); + uint8_t *dest_write = dest_data.ptrw(); + + int mip_count = mipmaps ? Image::get_image_required_mipmaps(width, height, target_format) : 0; + + for (int i = 0; i < mip_count + 1; i++) { + // Get write mip metrics for target image. + int mip_w, mip_h; + int mip_ofs = Image::get_image_mipmap_offset_and_dimensions(width, height, target_format, i, mip_w, mip_h); + // Ensure that mip offset is a multiple of 8 (etcpak expects uint64_t pointer). + ERR_FAIL_COND(mip_ofs % 8 != 0); + uint64_t *dest_mip_write = (uint64_t *)&dest_write[mip_ofs]; + + // Block size. Align stride to multiple of 4 (RGBA8). mip_w = (mip_w + 3) & ~3; mip_h = (mip_h + 3) & ~3; - Vector<uint8_t> dst_data; - dst_data.resize(size); - int mipmap_ofs = image->get_mipmap_offset(i); - - const uint32_t *image_read = (const uint32_t *)&image->get_data().ptr()[mipmap_ofs]; - uint64_t *dst_write = (uint64_t *)dst_data.ptrw(); - if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC1 || force_etc1_format) { - CompressEtc1RgbDither(image_read, dst_write, mip_w * mip_h / 16, mip_w); + const uint32_t blocks = mip_w * mip_h / 16; + + // Get mip data from source image for reading. + int src_mip_ofs = r_img->get_mipmap_offset(i); + const uint32_t *src_mip_read = (const uint32_t *)&src_read[src_mip_ofs]; + + if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC1) { + CompressEtc1RgbDither(src_mip_read, dest_mip_write, blocks, mip_w); } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2 || p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_RA_AS_RG) { - CompressEtc2Rgb(image_read, dst_write, mip_w * mip_h / 16, mip_w); + CompressEtc2Rgb(src_mip_read, dest_mip_write, blocks, mip_w); } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_ALPHA) { - CompressEtc2Rgba(image_read, dst_write, mip_w * mip_h / 16, mip_w); - } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_DXT5 || p_compresstype == EtcpakType::ETCPAK_TYPE_DXT5_RA_AS_RG) { - CompressDxt5(image_read, dst_write, mip_w * mip_h / 16, mip_w); + CompressEtc2Rgba(src_mip_read, dest_mip_write, blocks, mip_w); } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_DXT1) { - CompressDxt1Dither(image_read, dst_write, mip_w * mip_h / 16, mip_w); + CompressDxt1Dither(src_mip_read, dest_mip_write, blocks, mip_w); + } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_DXT5 || p_compresstype == EtcpakType::ETCPAK_TYPE_DXT5_RA_AS_RG) { + CompressDxt5(src_mip_read, dest_mip_write, blocks, mip_w); } else { - ERR_FAIL(); + ERR_FAIL_MSG("Invalid or unsupported Etcpak compression format."); } - copymem(&wr[ofs], dst_data.ptr(), size); } - p_img->create(new_img->get_width(), new_img->get_height(), mipmap, format, data); - print_verbose(vformat("ETCPAK encode took %s ms.", rtos(OS::get_singleton()->get_ticks_msec() - t))); + // Replace original image with compressed one. + r_img->create(width, height, mipmaps, target_format, dest_data); + + print_verbose(vformat("ETCPAK encode took %s ms.", rtos(OS::get_singleton()->get_ticks_msec() - start_time))); } diff --git a/modules/etcpak/image_etcpak.h b/modules/etcpak/image_compress_etcpak.h index 0137bab7cc..ccf157fada 100644 --- a/modules/etcpak/image_etcpak.h +++ b/modules/etcpak/image_compress_etcpak.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* image_etcpak.h */ +/* image_compress_etcpak.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef IMAGE_ETCPAK_H -#define IMAGE_ETCPAK_H +#ifndef IMAGE_COMPRESS_ETCPAK_H +#define IMAGE_COMPRESS_ETCPAK_H #include "core/io/image.h" @@ -43,9 +43,10 @@ enum class EtcpakType { ETCPAK_TYPE_DXT5_RA_AS_RG, }; -void _compress_etcpak(EtcpakType p_compresstype, Image *p_img, float p_lossy_quality, bool force_etc1_format, Image::UsedChannels p_channels); -void _compress_etc1(Image *p_img, float p_lossy_quality); -void _compress_etc2(Image *p_img, float p_lossy_quality, Image::UsedChannels p_source); -void _compress_bc(Image *p_img, float p_lossy_quality, Image::UsedChannels p_source); +void _compress_etc1(Image *r_img, float p_lossy_quality); +void _compress_etc2(Image *r_img, float p_lossy_quality, Image::UsedChannels p_channels); +void _compress_bc(Image *r_img, float p_lossy_quality, Image::UsedChannels p_channels); -#endif // IMAGE_ETCPAK_H +void _compress_etcpak(EtcpakType p_compresstype, Image *r_img, float p_lossy_quality); + +#endif // IMAGE_COMPRESS_ETCPAK_H diff --git a/modules/etcpak/register_types.cpp b/modules/etcpak/register_types.cpp index fcc0bc8b6f..d57d2f747a 100644 --- a/modules/etcpak/register_types.cpp +++ b/modules/etcpak/register_types.cpp @@ -30,7 +30,7 @@ #include "register_types.h" -#include "image_etcpak.h" +#include "image_compress_etcpak.h" void register_etcpak_types() { Image::_image_compress_etc1_func = _compress_etc1; diff --git a/modules/etcpak/register_types.h b/modules/etcpak/register_types.h index 9b300a3275..a9e10a4aae 100644 --- a/modules/etcpak/register_types.h +++ b/modules/etcpak/register_types.h @@ -28,5 +28,10 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#ifndef ETCPAK_REGISTER_TYPES_H +#define ETCPAK_REGISTER_TYPES_H + void register_etcpak_types(); void unregister_etcpak_types(); + +#endif // ETCPAK_REGISTER_TYPES_H diff --git a/modules/squish/image_compress_squish.cpp b/modules/squish/image_decompress_squish.cpp index fb0c7aba1d..1450b0fe88 100644 --- a/modules/squish/image_compress_squish.cpp +++ b/modules/squish/image_decompress_squish.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* image_compress_squish.cpp */ +/* image_decompress_squish.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "image_compress_squish.h" +#include "image_decompress_squish.h" #include <squish.h> diff --git a/modules/squish/image_compress_squish.h b/modules/squish/image_decompress_squish.h index ebc5a41887..fff5839ac4 100644 --- a/modules/squish/image_compress_squish.h +++ b/modules/squish/image_decompress_squish.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* image_compress_squish.h */ +/* image_decompress_squish.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,11 +28,11 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef IMAGE_COMPRESS_SQUISH_H -#define IMAGE_COMPRESS_SQUISH_H +#ifndef IMAGE_DECOMPRESS_SQUISH_H +#define IMAGE_DECOMPRESS_SQUISH_H #include "core/io/image.h" void image_decompress_squish(Image *p_image); -#endif // IMAGE_COMPRESS_SQUISH_H +#endif // IMAGE_DECOMPRESS_SQUISH_H diff --git a/modules/squish/register_types.cpp b/modules/squish/register_types.cpp index c51cdc9521..51aab040e7 100644 --- a/modules/squish/register_types.cpp +++ b/modules/squish/register_types.cpp @@ -29,7 +29,8 @@ /*************************************************************************/ #include "register_types.h" -#include "image_compress_squish.h" + +#include "image_decompress_squish.h" void register_squish_types() { Image::_image_decompress_bc = image_decompress_squish; diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp index 3cdf60708b..02ec9ccd06 100644 --- a/modules/visual_script/visual_script_editor.cpp +++ b/modules/visual_script/visual_script_editor.cpp @@ -710,7 +710,7 @@ void VisualScriptEditor::_update_graph(int p_only_id) { has_gnode_text = true; LineEdit *line_edit = memnew(LineEdit); line_edit->set_text(node->get_text()); - line_edit->set_expand_to_text_length(true); + line_edit->set_expand_to_text_length_enabled(true); line_edit->add_theme_font_override("font", get_theme_font("source", "EditorFonts")); gnode->add_child(line_edit); line_edit->connect("text_changed", callable_mp(this, &VisualScriptEditor::_expression_text_changed), varray(E->get())); @@ -843,7 +843,7 @@ void VisualScriptEditor::_update_graph(int p_only_id) { hbc->add_child(name_box); name_box->set_custom_minimum_size(Size2(60 * EDSCALE, 0)); name_box->set_text(left_name); - name_box->set_expand_to_text_length(true); + name_box->set_expand_to_text_length_enabled(true); name_box->connect("resized", callable_mp(this, &VisualScriptEditor::_update_node_size), varray(E->get())); name_box->connect("focus_exited", callable_mp(this, &VisualScriptEditor::_port_name_focus_out), varray(name_box, E->get(), i, true)); } else { @@ -938,7 +938,7 @@ void VisualScriptEditor::_update_graph(int p_only_id) { hbc->add_child(name_box); name_box->set_custom_minimum_size(Size2(60 * EDSCALE, 0)); name_box->set_text(right_name); - name_box->set_expand_to_text_length(true); + name_box->set_expand_to_text_length_enabled(true); name_box->connect("resized", callable_mp(this, &VisualScriptEditor::_update_node_size), varray(E->get())); name_box->connect("focus_exited", callable_mp(this, &VisualScriptEditor::_port_name_focus_out), varray(name_box, E->get(), i, false)); } else { @@ -4322,7 +4322,7 @@ VisualScriptEditor::VisualScriptEditor() { function_name_box = memnew(LineEdit); function_name_edit->add_child(function_name_box); function_name_box->connect("gui_input", callable_mp(this, &VisualScriptEditor::_fn_name_box_input)); - function_name_box->set_expand_to_text_length(true); + function_name_box->set_expand_to_text_length_enabled(true); add_child(function_name_edit); /// Actual Graph /// diff --git a/scene/3d/gpu_particles_collision_3d.cpp b/scene/3d/gpu_particles_collision_3d.cpp index 97241be60f..628b823f89 100644 --- a/scene/3d/gpu_particles_collision_3d.cpp +++ b/scene/3d/gpu_particles_collision_3d.cpp @@ -346,7 +346,7 @@ void GPUParticlesCollisionSDF::_compute_sdf(ComputeSDFParams *params) { ThreadWorkPool work_pool; work_pool.init(); work_pool.begin_work(params->size.z, this, &GPUParticlesCollisionSDF::_compute_sdf_z, params); - while (work_pool.get_work_index() < (uint32_t)params->size.z) { + while (!work_pool.is_done_dispatching()) { OS::get_singleton()->delay_usec(10000); bake_step_function(work_pool.get_work_index() * 100 / params->size.z, "Baking SDF"); } diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index 6282549ab4..124a07fa65 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -51,13 +51,13 @@ void LineEdit::_swap_current_input_direction() { } else { input_direction = TEXT_DIRECTION_LTR; } - set_cursor_position(get_cursor_position()); + set_caret_column(get_caret_column()); update(); } -void LineEdit::_move_cursor_left(bool p_select, bool p_move_by_word) { +void LineEdit::_move_caret_left(bool p_select, bool p_move_by_word) { if (selection.enabled && !p_select) { - set_cursor_position(selection.begin); + set_caret_column(selection.begin); deselect(); return; } @@ -65,7 +65,7 @@ void LineEdit::_move_cursor_left(bool p_select, bool p_move_by_word) { shift_selection_check_pre(p_select); if (p_move_by_word) { - int cc = cursor_pos; + int cc = caret_column; Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid); for (int i = words.size() - 1; i >= 0; i--) { @@ -75,21 +75,21 @@ void LineEdit::_move_cursor_left(bool p_select, bool p_move_by_word) { } } - set_cursor_position(cc); + set_caret_column(cc); } else { - if (mid_grapheme_caret_enabled) { - set_cursor_position(get_cursor_position() - 1); + if (caret_mid_grapheme_enabled) { + set_caret_column(get_caret_column() - 1); } else { - set_cursor_position(TS->shaped_text_prev_grapheme_pos(text_rid, get_cursor_position())); + set_caret_column(TS->shaped_text_prev_grapheme_pos(text_rid, get_caret_column())); } } shift_selection_check_post(p_select); } -void LineEdit::_move_cursor_right(bool p_select, bool p_move_by_word) { +void LineEdit::_move_caret_right(bool p_select, bool p_move_by_word) { if (selection.enabled && !p_select) { - set_cursor_position(selection.end); + set_caret_column(selection.end); deselect(); return; } @@ -97,7 +97,7 @@ void LineEdit::_move_cursor_right(bool p_select, bool p_move_by_word) { shift_selection_check_pre(p_select); if (p_move_by_word) { - int cc = cursor_pos; + int cc = caret_column; Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid); for (int i = 0; i < words.size(); i++) { @@ -107,27 +107,27 @@ void LineEdit::_move_cursor_right(bool p_select, bool p_move_by_word) { } } - set_cursor_position(cc); + set_caret_column(cc); } else { - if (mid_grapheme_caret_enabled) { - set_cursor_position(get_cursor_position() + 1); + if (caret_mid_grapheme_enabled) { + set_caret_column(get_caret_column() + 1); } else { - set_cursor_position(TS->shaped_text_next_grapheme_pos(text_rid, get_cursor_position())); + set_caret_column(TS->shaped_text_next_grapheme_pos(text_rid, get_caret_column())); } } shift_selection_check_post(p_select); } -void LineEdit::_move_cursor_start(bool p_select) { +void LineEdit::_move_caret_start(bool p_select) { shift_selection_check_pre(p_select); - set_cursor_position(0); + set_caret_column(0); shift_selection_check_post(p_select); } -void LineEdit::_move_cursor_end(bool p_select) { +void LineEdit::_move_caret_end(bool p_select) { shift_selection_check_pre(p_select); - set_cursor_position(text.length()); + set_caret_column(text.length()); shift_selection_check_post(p_select); } @@ -138,7 +138,7 @@ void LineEdit::_backspace(bool p_word, bool p_all_to_left) { if (p_all_to_left) { deselect(); - text = text.substr(0, cursor_pos); + text = text.substr(0, caret_column); _text_changed(); return; } @@ -149,7 +149,7 @@ void LineEdit::_backspace(bool p_word, bool p_all_to_left) { } if (p_word) { - int cc = cursor_pos; + int cc = caret_column; Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid); for (int i = words.size() - 1; i >= 0; i--) { @@ -158,9 +158,9 @@ void LineEdit::_backspace(bool p_word, bool p_all_to_left) { } } - delete_text(cc, cursor_pos); + delete_text(cc, caret_column); - set_cursor_position(cc); + set_caret_column(cc); } else { delete_char(); } @@ -173,9 +173,9 @@ void LineEdit::_delete(bool p_word, bool p_all_to_right) { if (p_all_to_right) { deselect(); - text = text.substr(cursor_pos, text.length() - cursor_pos); + text = text.substr(caret_column, text.length() - caret_column); _shape(); - set_cursor_position(0); + set_caret_column(0); _text_changed(); return; } @@ -187,12 +187,12 @@ void LineEdit::_delete(bool p_word, bool p_all_to_right) { int text_len = text.length(); - if (cursor_pos == text_len) { + if (caret_column == text_len) { return; // Nothing to do. } if (p_word) { - int cc = cursor_pos; + int cc = caret_column; Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid); for (int i = 0; i < words.size(); i++) { if (words[i].y > cc) { @@ -201,15 +201,15 @@ void LineEdit::_delete(bool p_word, bool p_all_to_right) { } } - delete_text(cursor_pos, cc); + delete_text(caret_column, cc); } else { - if (mid_grapheme_caret_enabled) { - set_cursor_position(cursor_pos + 1); + if (caret_mid_grapheme_enabled) { + set_caret_column(caret_column + 1); delete_char(); } else { - int cc = cursor_pos; - set_cursor_position(TS->shaped_text_next_grapheme_pos(text_rid, cursor_pos)); - delete_text(cc, cursor_pos); + int cc = caret_column; + set_caret_column(TS->shaped_text_next_grapheme_pos(text_rid, caret_column)); + delete_text(cc, caret_column); } } } @@ -250,10 +250,10 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { shift_selection_check_pre(b->get_shift()); - set_cursor_at_pixel_pos(b->get_position().x); + set_caret_at_pixel_pos(b->get_position().x); if (b->get_shift()) { - selection_fill_at_cursor(); + selection_fill_at_caret(); selection.creating = true; } else { @@ -265,18 +265,18 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { selection.end = text.length(); selection.doubleclick = true; selection.last_dblclk = 0; - cursor_pos = selection.begin; + caret_column = selection.begin; } else if (b->is_doubleclick()) { // Double-click select word. Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid); for (int i = 0; i < words.size(); i++) { - if (words[i].x < cursor_pos && words[i].y > cursor_pos) { + if (words[i].x < caret_column && words[i].y > caret_column) { selection.enabled = true; selection.begin = words[i].x; selection.end = words[i].y; selection.doubleclick = true; selection.last_dblclk = OS::get_singleton()->get_ticks_msec(); - cursor_pos = selection.end; + caret_column = selection.end; break; } } @@ -285,9 +285,9 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { selection.drag_attempt = false; - if ((cursor_pos < selection.begin) || (cursor_pos > selection.end) || !selection.enabled) { + if ((caret_column < selection.begin) || (caret_column > selection.end) || !selection.enabled) { deselect(); - selection.cursor_start = cursor_pos; + selection.start_column = caret_column; selection.creating = true; } else if (selection.enabled) { selection.drag_attempt = true; @@ -331,8 +331,8 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { if (m->get_button_mask() & MOUSE_BUTTON_LEFT) { if (selection.creating) { - set_cursor_at_pixel_pos(m->get_position().x); - selection_fill_at_cursor(); + set_caret_at_pixel_pos(m->get_position().x); + selection_fill_at_caret(); } } } @@ -346,7 +346,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { if (context_menu_enabled) { if (k->is_action("ui_menu", true)) { - Point2 pos = Point2(get_cursor_pixel_pos().x, (get_size().y + get_theme_font("font")->get_height(get_theme_font_size("font_size"))) / 2); + Point2 pos = Point2(get_caret_pixel_pos().x, (get_size().y + get_theme_font("font")->get_height(get_theme_font_size("font_size"))) / 2); menu->set_position(get_global_transform().xform(pos)); menu->set_size(Vector2(1, 1)); _generate_context_menu(); @@ -445,34 +445,34 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { k->set_shift(false); if (k->is_action("ui_text_caret_word_left", true)) { - _move_cursor_left(shift_pressed, true); + _move_caret_left(shift_pressed, true); accept_event(); return; } if (k->is_action("ui_text_caret_left", true)) { - _move_cursor_left(shift_pressed); + _move_caret_left(shift_pressed); accept_event(); return; } if (k->is_action("ui_text_caret_word_right", true)) { - _move_cursor_right(shift_pressed, true); + _move_caret_right(shift_pressed, true); accept_event(); return; } if (k->is_action("ui_text_caret_right", true)) { - _move_cursor_right(shift_pressed, false); + _move_caret_right(shift_pressed, false); accept_event(); return; } // Up = Home, Down = End if (k->is_action("ui_text_caret_up", true) || k->is_action("ui_text_caret_line_start", true) || k->is_action("ui_text_caret_page_up", true)) { - _move_cursor_start(shift_pressed); + _move_caret_start(shift_pressed); accept_event(); return; } if (k->is_action("ui_text_caret_down", true) || k->is_action("ui_text_caret_line_end", true) || k->is_action("ui_text_caret_page_down", true)) { - _move_cursor_end(shift_pressed); + _move_caret_end(shift_pressed); accept_event(); return; } @@ -495,7 +495,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { selection_delete(); char32_t ucodestr[2] = { (char32_t)k->get_unicode(), 0 }; int prev_len = text.length(); - append_at_cursor(ucodestr); + insert_text_at_caret(ucodestr); if (text.length() != prev_len) { _text_changed(); } @@ -542,15 +542,15 @@ void LineEdit::drop_data(const Point2 &p_point, const Variant &p_data) { Control::drop_data(p_point, p_data); if (p_data.get_type() == Variant::STRING) { - set_cursor_at_pixel_pos(p_point.x); + set_caret_at_pixel_pos(p_point.x); int selected = selection.end - selection.begin; text.erase(selection.begin, selected); _shape(); - append_at_cursor(p_data); - selection.begin = cursor_pos - selected; - selection.end = cursor_pos; + insert_text_at_caret(p_data); + selection.begin = caret_column - selected; + selection.end = caret_column; } } @@ -575,8 +575,8 @@ void LineEdit::_notification(int p_what) { #ifdef TOOLS_ENABLED case NOTIFICATION_ENTER_TREE: { if (Engine::get_singleton()->is_editor_hint() && !get_tree()->is_node_being_edited(this)) { - cursor_set_blink_enabled(EDITOR_DEF("text_editor/cursor/caret_blink", false)); - cursor_set_blink_speed(EDITOR_DEF("text_editor/cursor/caret_blink_speed", 0.65)); + set_caret_blink_enabled(EDITOR_DEF("text_editor/cursor/caret_blink", false)); + set_caret_blink_speed(EDITOR_DEF("text_editor/cursor/caret_blink_speed", 0.65)); if (!EditorSettings::get_singleton()->is_connected("settings_changed", callable_mp(this, &LineEdit::_editor_settings_changed))) { EditorSettings::get_singleton()->connect("settings_changed", callable_mp(this, &LineEdit::_editor_settings_changed)); @@ -587,7 +587,7 @@ void LineEdit::_notification(int p_what) { case NOTIFICATION_RESIZED: { _fit_to_width(); scroll_offset = 0; - set_cursor_position(get_cursor_position()); + set_caret_column(get_caret_column()); } break; case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: case NOTIFICATION_THEME_CHANGED: { @@ -674,7 +674,7 @@ void LineEdit::_notification(int p_what) { Color selection_color = get_theme_color("selection_color"); Color font_color = is_editable() ? get_theme_color("font_color") : get_theme_color("font_uneditable_color"); Color font_selected_color = get_theme_color("font_selected_color"); - Color cursor_color = get_theme_color("cursor_color"); + Color caret_color = get_theme_color("caret_color"); // Draw placeholder color. if (using_placeholder) { @@ -778,7 +778,7 @@ void LineEdit::_notification(int p_what) { // Normal caret. Rect2 l_caret, t_caret; TextServer::Direction l_dir, t_dir; - TS->shaped_text_get_carets(text_rid, cursor_pos, l_caret, l_dir, t_caret, t_dir); + TS->shaped_text_get_carets(text_rid, caret_column, l_caret, l_dir, t_caret, t_dir); if (l_caret == Rect2() && t_caret == Rect2()) { // No carets, add one at the start. @@ -791,28 +791,28 @@ void LineEdit::_notification(int p_what) { l_dir = TextServer::DIRECTION_LTR; l_caret = Rect2(Vector2(x_ofs, y), Size2(caret_width, h)); } - RenderingServer::get_singleton()->canvas_item_add_rect(ci, l_caret, cursor_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, l_caret, caret_color); } else { if (l_caret != Rect2() && l_dir == TextServer::DIRECTION_AUTO) { // Draw extra marker on top of mid caret. Rect2 trect = Rect2(l_caret.position.x - 3 * caret_width, l_caret.position.y, 6 * caret_width, caret_width); trect.position += ofs; - RenderingServer::get_singleton()->canvas_item_add_rect(ci, trect, cursor_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, trect, caret_color); } l_caret.position += ofs; l_caret.size.x = caret_width; - RenderingServer::get_singleton()->canvas_item_add_rect(ci, l_caret, cursor_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, l_caret, caret_color); t_caret.position += ofs; t_caret.size.x = caret_width; - RenderingServer::get_singleton()->canvas_item_add_rect(ci, t_caret, cursor_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, t_caret, caret_color); } } else { { // IME intermediate text range. - Vector<Vector2> sel = TS->shaped_text_get_selection(text_rid, cursor_pos, cursor_pos + ime_text.length()); + Vector<Vector2> sel = TS->shaped_text_get_selection(text_rid, caret_column, caret_column + ime_text.length()); for (int i = 0; i < sel.size(); i++) { Rect2 rect = Rect2(sel[i].x + ofs.x, ofs.y, sel[i].y - sel[i].x, text_height); if (rect.position.x + rect.size.x <= x_ofs || rect.position.x > ofs_max) { @@ -825,12 +825,12 @@ void LineEdit::_notification(int p_what) { rect.size.x = ofs_max - rect.position.x; } rect.size.y = caret_width; - RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, cursor_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, caret_color); } } { // IME caret. - Vector<Vector2> sel = TS->shaped_text_get_selection(text_rid, cursor_pos + ime_selection.x, cursor_pos + ime_selection.x + ime_selection.y); + Vector<Vector2> sel = TS->shaped_text_get_selection(text_rid, caret_column + ime_selection.x, caret_column + ime_selection.x + ime_selection.y); for (int i = 0; i < sel.size(); i++) { Rect2 rect = Rect2(sel[i].x + ofs.x, ofs.y, sel[i].y - sel[i].x, text_height); if (rect.position.x + rect.size.x <= x_ofs || rect.position.x > ofs_max) { @@ -843,7 +843,7 @@ void LineEdit::_notification(int p_what) { rect.size.x = ofs_max - rect.position.x; } rect.size.y = caret_width * 3; - RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, cursor_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, caret_color); } } } @@ -869,8 +869,8 @@ void LineEdit::_notification(int p_what) { if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) { DisplayServer::get_singleton()->window_set_ime_active(true, get_viewport()->get_window_id()); - Point2 cursor_pos = Point2(get_cursor_position(), 1) * get_minimum_size().height; - DisplayServer::get_singleton()->window_set_ime_position(get_global_position() + cursor_pos, get_viewport()->get_window_id()); + Point2 caret_column = Point2(get_caret_column(), 1) * get_minimum_size().height; + DisplayServer::get_singleton()->window_set_ime_position(get_global_position() + caret_column, get_viewport()->get_window_id()); } show_virtual_keyboard(); @@ -887,7 +887,7 @@ void LineEdit::_notification(int p_what) { ime_text = ""; ime_selection = Point2(); _shape(); - set_cursor_position(cursor_pos); // Update scroll_offset + set_caret_column(caret_column); // Update scroll_offset if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) { DisplayServer::get_singleton()->virtual_keyboard_hide(); @@ -899,7 +899,7 @@ void LineEdit::_notification(int p_what) { ime_text = DisplayServer::get_singleton()->ime_get_text(); ime_selection = DisplayServer::get_singleton()->ime_get_selection(); _shape(); - set_cursor_position(cursor_pos); // Update scroll_offset + set_caret_column(caret_column); // Update scroll_offset update(); } @@ -933,7 +933,7 @@ void LineEdit::paste_text() { if (selection.enabled) { selection_delete(); } - append_at_cursor(paste_buffer); + insert_text_at_caret(paste_buffer); if (!text_changed_dirty) { if (is_inside_tree() && text.length() != prev_len) { @@ -961,7 +961,7 @@ void LineEdit::undo() { TextOperation op = undo_stack_pos->get(); text = op.text; scroll_offset = op.scroll_offset; - set_cursor_position(op.cursor_pos); + set_caret_column(op.caret_column); _shape(); _emit_text_change(); @@ -982,7 +982,7 @@ void LineEdit::redo() { TextOperation op = undo_stack_pos->get(); text = op.text; scroll_offset = op.scroll_offset; - set_cursor_position(op.cursor_pos); + set_caret_column(op.caret_column); _shape(); _emit_text_change(); @@ -990,7 +990,7 @@ void LineEdit::redo() { void LineEdit::shift_selection_check_pre(bool p_shift) { if (!selection.enabled && p_shift) { - selection.cursor_start = cursor_pos; + selection.start_column = caret_column; } if (!p_shift) { deselect(); @@ -999,11 +999,11 @@ void LineEdit::shift_selection_check_pre(bool p_shift) { void LineEdit::shift_selection_check_post(bool p_shift) { if (p_shift) { - selection_fill_at_cursor(); + selection_fill_at_caret(); } } -void LineEdit::set_cursor_at_pixel_pos(int p_x) { +void LineEdit::set_caret_at_pixel_pos(int p_x) { Ref<StyleBox> style = get_theme_stylebox("normal"); bool rtl = is_layout_rtl(); @@ -1048,10 +1048,10 @@ void LineEdit::set_cursor_at_pixel_pos(int p_x) { } int ofs = TS->shaped_text_hit_test_position(text_rid, p_x - x_ofs - scroll_offset); - set_cursor_position(ofs); + set_caret_column(ofs); } -Vector2i LineEdit::get_cursor_pixel_pos() { +Vector2i LineEdit::get_caret_pixel_pos() { Ref<StyleBox> style = get_theme_stylebox("normal"); bool rtl = is_layout_rtl(); @@ -1100,9 +1100,9 @@ Vector2i LineEdit::get_cursor_pixel_pos() { TextServer::Direction l_dir, t_dir; // Get position of the start of caret. if (ime_text.length() != 0 && ime_selection.x != 0) { - TS->shaped_text_get_carets(text_rid, cursor_pos + ime_selection.x, l_caret, l_dir, t_caret, t_dir); + TS->shaped_text_get_carets(text_rid, caret_column + ime_selection.x, l_caret, l_dir, t_caret, t_dir); } else { - TS->shaped_text_get_carets(text_rid, cursor_pos, l_caret, l_dir, t_caret, t_dir); + TS->shaped_text_get_carets(text_rid, caret_column, l_caret, l_dir, t_caret, t_dir); } if ((l_caret != Rect2() && (l_dir == TextServer::DIRECTION_AUTO || l_dir == (TextServer::Direction)input_direction)) || (t_caret == Rect2())) { @@ -1114,9 +1114,9 @@ Vector2i LineEdit::get_cursor_pixel_pos() { // Get position of the end of caret. if (ime_text.length() != 0) { if (ime_selection.y != 0) { - TS->shaped_text_get_carets(text_rid, cursor_pos + ime_selection.x + ime_selection.y, l_caret, l_dir, t_caret, t_dir); + TS->shaped_text_get_carets(text_rid, caret_column + ime_selection.x + ime_selection.y, l_caret, l_dir, t_caret, t_dir); } else { - TS->shaped_text_get_carets(text_rid, cursor_pos + ime_text.size(), l_caret, l_dir, t_caret, t_dir); + TS->shaped_text_get_carets(text_rid, caret_column + ime_text.size(), l_caret, l_dir, t_caret, t_dir); } if ((l_caret != Rect2() && (l_dir == TextServer::DIRECTION_AUTO || l_dir == (TextServer::Direction)input_direction)) || (t_caret == Rect2())) { ret.y = x_ofs + l_caret.position.x + scroll_offset; @@ -1130,19 +1130,19 @@ Vector2i LineEdit::get_cursor_pixel_pos() { return ret; } -void LineEdit::set_mid_grapheme_caret_enabled(const bool p_enabled) { - mid_grapheme_caret_enabled = p_enabled; +void LineEdit::set_caret_mid_grapheme_enabled(const bool p_enabled) { + caret_mid_grapheme_enabled = p_enabled; } -bool LineEdit::get_mid_grapheme_caret_enabled() const { - return mid_grapheme_caret_enabled; +bool LineEdit::is_caret_mid_grapheme_enabled() const { + return caret_mid_grapheme_enabled; } -bool LineEdit::cursor_get_blink_enabled() const { +bool LineEdit::is_caret_blink_enabled() const { return caret_blink_enabled; } -void LineEdit::cursor_set_blink_enabled(const bool p_enabled) { +void LineEdit::set_caret_blink_enabled(const bool p_enabled) { caret_blink_enabled = p_enabled; if (has_focus() || caret_force_displayed) { @@ -1160,21 +1160,21 @@ void LineEdit::cursor_set_blink_enabled(const bool p_enabled) { notify_property_list_changed(); } -bool LineEdit::cursor_get_force_displayed() const { +bool LineEdit::is_caret_force_displayed() const { return caret_force_displayed; } -void LineEdit::cursor_set_force_displayed(const bool p_enabled) { +void LineEdit::set_caret_force_displayed(const bool p_enabled) { caret_force_displayed = p_enabled; - cursor_set_blink_enabled(caret_blink_enabled); + set_caret_blink_enabled(caret_blink_enabled); update(); } -float LineEdit::cursor_get_blink_speed() const { +float LineEdit::get_caret_blink_speed() const { return caret_blink_timer->get_wait_time(); } -void LineEdit::cursor_set_blink_speed(const float p_speed) { +void LineEdit::set_caret_blink_speed(const float p_speed) { ERR_FAIL_COND(p_speed <= 0); caret_blink_timer->set_wait_time(p_speed); } @@ -1198,14 +1198,14 @@ void LineEdit::_toggle_draw_caret() { } void LineEdit::delete_char() { - if ((text.length() <= 0) || (cursor_pos == 0)) { + if ((text.length() <= 0) || (caret_column == 0)) { return; } - text.erase(cursor_pos - 1, 1); + text.erase(caret_column - 1, 1); _shape(); - set_cursor_position(get_cursor_position() - 1); + set_caret_column(get_caret_column() - 1); _text_changed(); } @@ -1217,10 +1217,10 @@ void LineEdit::delete_text(int p_from_column, int p_to_column) { text.erase(p_from_column, p_to_column - p_from_column); _shape(); - cursor_pos -= CLAMP(cursor_pos - p_from_column, 0, p_to_column - p_from_column); + caret_column -= CLAMP(caret_column - p_from_column, 0, p_to_column - p_from_column); - if (cursor_pos >= text.length()) { - cursor_pos = text.length(); + if (caret_column >= text.length()) { + caret_column = text.length(); } if (!text_changed_dirty) { @@ -1233,11 +1233,11 @@ void LineEdit::delete_text(int p_from_column, int p_to_column) { void LineEdit::set_text(String p_text) { clear_internal(); - append_at_cursor(p_text); + insert_text_at_caret(p_text); _create_undo_state(); update(); - cursor_pos = 0; + caret_column = 0; scroll_offset = 0; } @@ -1347,7 +1347,7 @@ void LineEdit::show_virtual_keyboard() { if (selection.enabled) { DisplayServer::get_singleton()->virtual_keyboard_show(text, get_global_rect(), false, max_length, selection.begin, selection.end); } else { - DisplayServer::get_singleton()->virtual_keyboard_show(text, get_global_rect(), false, max_length, cursor_pos); + DisplayServer::get_singleton()->virtual_keyboard_show(text, get_global_rect(), false, max_length, caret_column); } } } @@ -1376,16 +1376,16 @@ float LineEdit::get_placeholder_alpha() const { return placeholder_alpha; } -void LineEdit::set_cursor_position(int p_pos) { - if (p_pos > (int)text.length()) { - p_pos = text.length(); +void LineEdit::set_caret_column(int p_column) { + if (p_column > (int)text.length()) { + p_column = text.length(); } - if (p_pos < 0) { - p_pos = 0; + if (p_column < 0) { + p_column = 0; } - cursor_pos = p_pos; + caret_column = p_column; // Fit to window. @@ -1440,7 +1440,7 @@ void LineEdit::set_cursor_position(int p_pos) { } // Note: Use two coordinates to fit IME input range. - Vector2i primary_catret_offset = get_cursor_pixel_pos(); + Vector2i primary_catret_offset = get_caret_pixel_pos(); if (MIN(primary_catret_offset.x, primary_catret_offset.y) <= x_ofs) { scroll_offset += (x_ofs - MIN(primary_catret_offset.x, primary_catret_offset.y)); @@ -1452,8 +1452,8 @@ void LineEdit::set_cursor_position(int p_pos) { update(); } -int LineEdit::get_cursor_position() const { - return cursor_pos; +int LineEdit::get_caret_column() const { + return caret_column; } void LineEdit::set_scroll_offset(int p_pos) { @@ -1467,17 +1467,17 @@ int LineEdit::get_scroll_offset() const { return scroll_offset; } -void LineEdit::append_at_cursor(String p_text) { +void LineEdit::insert_text_at_caret(String p_text) { if ((max_length <= 0) || (text.length() + p_text.length() <= max_length)) { - String pre = text.substr(0, cursor_pos); - String post = text.substr(cursor_pos, text.length() - cursor_pos); + String pre = text.substr(0, caret_column); + String post = text.substr(caret_column, text.length() - caret_column); text = pre + p_text + post; _shape(); - TextServer::Direction dir = TS->shaped_text_get_dominant_direciton_in_range(text_rid, cursor_pos, cursor_pos + p_text.length()); + TextServer::Direction dir = TS->shaped_text_get_dominant_direciton_in_range(text_rid, caret_column, caret_column + p_text.length()); if (dir != TextServer::DIRECTION_AUTO) { input_direction = (TextDirection)dir; } - set_cursor_position(cursor_pos + p_text.length()); + set_caret_column(caret_column + p_text.length()); } else { emit_signal("text_change_rejected"); } @@ -1486,7 +1486,7 @@ void LineEdit::append_at_cursor(String p_text) { void LineEdit::clear_internal() { deselect(); _clear_undo_stack(); - cursor_pos = 0; + caret_column = 0; scroll_offset = 0; undo_text = ""; text = ""; @@ -1506,7 +1506,7 @@ Size2 LineEdit::get_minimum_size() const { min_size.width = get_theme_constant("minimum_character_width") * em_space_size; if (expand_to_text_length) { - // Add a space because some fonts are too exact, and because cursor needs a bit more when at the end. + // Add a space because some fonts are too exact, and because caret needs a bit more when at the end. min_size.width = MAX(min_size.width, full_width + em_space_size); } @@ -1527,7 +1527,7 @@ Size2 LineEdit::get_minimum_size() const { void LineEdit::deselect() { selection.begin = 0; selection.end = 0; - selection.cursor_start = 0; + selection.start_column = 0; selection.enabled = false; selection.creating = false; selection.doubleclick = false; @@ -1552,13 +1552,13 @@ int LineEdit::get_max_length() const { return max_length; } -void LineEdit::selection_fill_at_cursor() { +void LineEdit::selection_fill_at_caret() { if (!selecting_enabled) { return; } - selection.begin = cursor_pos; - selection.end = selection.cursor_start; + selection.begin = caret_column; + selection.end = selection.start_column; if (selection.end < selection.begin) { int aux = selection.end; @@ -1715,82 +1715,82 @@ void LineEdit::menu_option(int p_option) { } break; case MENU_INSERT_LRM: { if (editable) { - append_at_cursor(String::chr(0x200E)); + insert_text_at_caret(String::chr(0x200E)); } } break; case MENU_INSERT_RLM: { if (editable) { - append_at_cursor(String::chr(0x200F)); + insert_text_at_caret(String::chr(0x200F)); } } break; case MENU_INSERT_LRE: { if (editable) { - append_at_cursor(String::chr(0x202A)); + insert_text_at_caret(String::chr(0x202A)); } } break; case MENU_INSERT_RLE: { if (editable) { - append_at_cursor(String::chr(0x202B)); + insert_text_at_caret(String::chr(0x202B)); } } break; case MENU_INSERT_LRO: { if (editable) { - append_at_cursor(String::chr(0x202D)); + insert_text_at_caret(String::chr(0x202D)); } } break; case MENU_INSERT_RLO: { if (editable) { - append_at_cursor(String::chr(0x202E)); + insert_text_at_caret(String::chr(0x202E)); } } break; case MENU_INSERT_PDF: { if (editable) { - append_at_cursor(String::chr(0x202C)); + insert_text_at_caret(String::chr(0x202C)); } } break; case MENU_INSERT_ALM: { if (editable) { - append_at_cursor(String::chr(0x061C)); + insert_text_at_caret(String::chr(0x061C)); } } break; case MENU_INSERT_LRI: { if (editable) { - append_at_cursor(String::chr(0x2066)); + insert_text_at_caret(String::chr(0x2066)); } } break; case MENU_INSERT_RLI: { if (editable) { - append_at_cursor(String::chr(0x2067)); + insert_text_at_caret(String::chr(0x2067)); } } break; case MENU_INSERT_FSI: { if (editable) { - append_at_cursor(String::chr(0x2068)); + insert_text_at_caret(String::chr(0x2068)); } } break; case MENU_INSERT_PDI: { if (editable) { - append_at_cursor(String::chr(0x2069)); + insert_text_at_caret(String::chr(0x2069)); } } break; case MENU_INSERT_ZWJ: { if (editable) { - append_at_cursor(String::chr(0x200D)); + insert_text_at_caret(String::chr(0x200D)); } } break; case MENU_INSERT_ZWNJ: { if (editable) { - append_at_cursor(String::chr(0x200C)); + insert_text_at_caret(String::chr(0x200C)); } } break; case MENU_INSERT_WJ: { if (editable) { - append_at_cursor(String::chr(0x2060)); + insert_text_at_caret(String::chr(0x2060)); } } break; case MENU_INSERT_SHY: { if (editable) { - append_at_cursor(String::chr(0x00AD)); + insert_text_at_caret(String::chr(0x00AD)); } } } @@ -1810,18 +1810,18 @@ PopupMenu *LineEdit::get_menu() const { void LineEdit::_editor_settings_changed() { #ifdef TOOLS_ENABLED - cursor_set_blink_enabled(EDITOR_DEF("text_editor/cursor/caret_blink", false)); - cursor_set_blink_speed(EDITOR_DEF("text_editor/cursor/caret_blink_speed", 0.65)); + set_caret_blink_enabled(EDITOR_DEF("text_editor/cursor/caret_blink", false)); + set_caret_blink_speed(EDITOR_DEF("text_editor/cursor/caret_blink_speed", 0.65)); #endif } -void LineEdit::set_expand_to_text_length(bool p_enabled) { +void LineEdit::set_expand_to_text_length_enabled(bool p_enabled) { expand_to_text_length = p_enabled; minimum_size_changed(); - set_cursor_position(cursor_pos); + set_caret_column(caret_column); } -bool LineEdit::get_expand_to_text_length() const { +bool LineEdit::is_expand_to_text_length_enabled() const { return expand_to_text_length; } @@ -1906,7 +1906,7 @@ void LineEdit::_shape() { t = secret_character.repeat(text.length() + ime_text.length()); } else { if (ime_text.length() > 0) { - t = text.substr(0, cursor_pos) + ime_text + text.substr(cursor_pos, text.length()); + t = text.substr(0, caret_column) + ime_text + text.substr(caret_column, text.length()); } else { t = text; } @@ -1971,7 +1971,7 @@ void LineEdit::_clear_undo_stack() { void LineEdit::_create_undo_state() { TextOperation op; op.text = text; - op.cursor_pos = cursor_pos; + op.caret_column = caret_column; op.scroll_offset = scroll_offset; undo_stack.push_back(op); } @@ -2116,23 +2116,23 @@ void LineEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("get_placeholder"), &LineEdit::get_placeholder); ClassDB::bind_method(D_METHOD("set_placeholder_alpha", "alpha"), &LineEdit::set_placeholder_alpha); ClassDB::bind_method(D_METHOD("get_placeholder_alpha"), &LineEdit::get_placeholder_alpha); - ClassDB::bind_method(D_METHOD("set_cursor_position", "position"), &LineEdit::set_cursor_position); - ClassDB::bind_method(D_METHOD("get_cursor_position"), &LineEdit::get_cursor_position); + ClassDB::bind_method(D_METHOD("set_caret_column", "position"), &LineEdit::set_caret_column); + ClassDB::bind_method(D_METHOD("get_caret_column"), &LineEdit::get_caret_column); ClassDB::bind_method(D_METHOD("get_scroll_offset"), &LineEdit::get_scroll_offset); - ClassDB::bind_method(D_METHOD("set_expand_to_text_length", "enabled"), &LineEdit::set_expand_to_text_length); - ClassDB::bind_method(D_METHOD("get_expand_to_text_length"), &LineEdit::get_expand_to_text_length); - ClassDB::bind_method(D_METHOD("cursor_set_blink_enabled", "enabled"), &LineEdit::cursor_set_blink_enabled); - ClassDB::bind_method(D_METHOD("cursor_get_blink_enabled"), &LineEdit::cursor_get_blink_enabled); - ClassDB::bind_method(D_METHOD("set_mid_grapheme_caret_enabled", "enabled"), &LineEdit::set_mid_grapheme_caret_enabled); - ClassDB::bind_method(D_METHOD("get_mid_grapheme_caret_enabled"), &LineEdit::get_mid_grapheme_caret_enabled); - ClassDB::bind_method(D_METHOD("cursor_set_force_displayed", "enabled"), &LineEdit::cursor_set_force_displayed); - ClassDB::bind_method(D_METHOD("cursor_get_force_displayed"), &LineEdit::cursor_get_force_displayed); - ClassDB::bind_method(D_METHOD("cursor_set_blink_speed", "blink_speed"), &LineEdit::cursor_set_blink_speed); - ClassDB::bind_method(D_METHOD("cursor_get_blink_speed"), &LineEdit::cursor_get_blink_speed); + ClassDB::bind_method(D_METHOD("set_expand_to_text_length_enabled", "enabled"), &LineEdit::set_expand_to_text_length_enabled); + ClassDB::bind_method(D_METHOD("is_expand_to_text_length_enabled"), &LineEdit::is_expand_to_text_length_enabled); + ClassDB::bind_method(D_METHOD("set_caret_blink_enabled", "enabled"), &LineEdit::set_caret_blink_enabled); + ClassDB::bind_method(D_METHOD("is_caret_blink_enabled"), &LineEdit::is_caret_blink_enabled); + ClassDB::bind_method(D_METHOD("set_caret_mid_grapheme_enabled", "enabled"), &LineEdit::set_caret_mid_grapheme_enabled); + ClassDB::bind_method(D_METHOD("is_caret_mid_grapheme_enabled"), &LineEdit::is_caret_mid_grapheme_enabled); + ClassDB::bind_method(D_METHOD("set_caret_force_displayed", "enabled"), &LineEdit::set_caret_force_displayed); + ClassDB::bind_method(D_METHOD("is_caret_force_displayed"), &LineEdit::is_caret_force_displayed); + ClassDB::bind_method(D_METHOD("set_caret_blink_speed", "blink_speed"), &LineEdit::set_caret_blink_speed); + ClassDB::bind_method(D_METHOD("get_caret_blink_speed"), &LineEdit::get_caret_blink_speed); ClassDB::bind_method(D_METHOD("set_max_length", "chars"), &LineEdit::set_max_length); ClassDB::bind_method(D_METHOD("get_max_length"), &LineEdit::get_max_length); - ClassDB::bind_method(D_METHOD("append_at_cursor", "text"), &LineEdit::append_at_cursor); - ClassDB::bind_method(D_METHOD("delete_char_at_cursor"), &LineEdit::delete_char); + ClassDB::bind_method(D_METHOD("insert_text_at_caret", "text"), &LineEdit::insert_text_at_caret); + ClassDB::bind_method(D_METHOD("delete_char_at_caret"), &LineEdit::delete_char); ClassDB::bind_method(D_METHOD("delete_text", "from_column", "to_column"), &LineEdit::delete_text); ClassDB::bind_method(D_METHOD("set_editable", "enabled"), &LineEdit::set_editable); ClassDB::bind_method(D_METHOD("is_editable"), &LineEdit::is_editable); @@ -2200,7 +2200,7 @@ void LineEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "secret"), "set_secret", "is_secret"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "secret_character"), "set_secret_character", "get_secret_character"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand_to_text_length"), "set_expand_to_text_length", "get_expand_to_text_length"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand_to_text_length"), "set_expand_to_text_length_enabled", "is_expand_to_text_length_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "virtual_keyboard_enabled"), "set_virtual_keyboard_enabled", "is_virtual_keyboard_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clear_button_enabled"), "set_clear_button_enabled", "is_clear_button_enabled"); @@ -2217,11 +2217,11 @@ void LineEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::STRING, "placeholder_text"), "set_placeholder", "get_placeholder"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "placeholder_alpha", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_placeholder_alpha", "get_placeholder_alpha"); ADD_GROUP("Caret", "caret_"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_blink"), "cursor_set_blink_enabled", "cursor_get_blink_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.01"), "cursor_set_blink_speed", "cursor_get_blink_speed"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "caret_position"), "set_cursor_position", "get_cursor_position"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_force_displayed"), "cursor_set_force_displayed", "cursor_get_force_displayed"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_mid_grapheme"), "set_mid_grapheme_caret_enabled", "get_mid_grapheme_caret_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_blink"), "set_caret_blink_enabled", "is_caret_blink_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.01"), "set_caret_blink_speed", "get_caret_blink_speed"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "caret_column"), "set_caret_column", "get_caret_column"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_force_displayed"), "set_caret_force_displayed", "is_caret_force_displayed"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_mid_grapheme"), "set_caret_mid_grapheme_enabled", "is_caret_mid_grapheme_enabled"); } LineEdit::LineEdit() { @@ -2237,7 +2237,7 @@ LineEdit::LineEdit() { add_child(caret_blink_timer); caret_blink_timer->set_wait_time(0.65); caret_blink_timer->connect("timeout", callable_mp(this, &LineEdit::_toggle_draw_caret)); - cursor_set_blink_enabled(false); + set_caret_blink_enabled(false); menu = memnew(PopupMenu); add_child(menu); diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h index ef36377f2e..f4f0ff0629 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -103,9 +103,9 @@ private: PopupMenu *menu_dir = nullptr; PopupMenu *menu_ctl = nullptr; - bool mid_grapheme_caret_enabled = false; + bool caret_mid_grapheme_enabled = false; - int cursor_pos = 0; + int caret_column = 0; int scroll_offset = 0; int max_length = 0; // 0 for no maximum. @@ -131,7 +131,7 @@ private: struct Selection { int begin = 0; int end = 0; - int cursor_start = 0; + int start_column = 0; bool enabled = false; bool creating = false; bool doubleclick = false; @@ -140,7 +140,7 @@ private: } selection; struct TextOperation { - int cursor_pos = 0; + int caret_column = 0; int scroll_offset = 0; int cached_width = 0; String text; @@ -175,12 +175,12 @@ private: void shift_selection_check_pre(bool); void shift_selection_check_post(bool); - void selection_fill_at_cursor(); + void selection_fill_at_caret(); void set_scroll_offset(int p_pos); int get_scroll_offset() const; - void set_cursor_at_pixel_pos(int p_x); - Vector2i get_cursor_pixel_pos(); + void set_caret_at_pixel_pos(int p_x); + Vector2i get_caret_pixel_pos(); void _reset_caret_blink_timer(); void _toggle_draw_caret(); @@ -191,10 +191,10 @@ private: void _editor_settings_changed(); void _swap_current_input_direction(); - void _move_cursor_left(bool p_select, bool p_move_by_word = false); - void _move_cursor_right(bool p_select, bool p_move_by_word = false); - void _move_cursor_start(bool p_select); - void _move_cursor_end(bool p_select); + void _move_caret_left(bool p_select, bool p_move_by_word = false); + void _move_caret_right(bool p_select, bool p_move_by_word = false); + void _move_caret_start(bool p_select); + void _move_caret_end(bool p_select); void _backspace(bool p_word = false, bool p_all_to_left = false); void _delete(bool p_word = false, bool p_all_to_right = false); @@ -259,26 +259,26 @@ public: void set_placeholder_alpha(float p_alpha); float get_placeholder_alpha() const; - void set_cursor_position(int p_pos); - int get_cursor_position() const; + void set_caret_column(int p_column); + int get_caret_column() const; void set_max_length(int p_max_length); int get_max_length() const; - void append_at_cursor(String p_text); + void insert_text_at_caret(String p_text); void clear(); - void set_mid_grapheme_caret_enabled(const bool p_enabled); - bool get_mid_grapheme_caret_enabled() const; + void set_caret_mid_grapheme_enabled(const bool p_enabled); + bool is_caret_mid_grapheme_enabled() const; - bool cursor_get_blink_enabled() const; - void cursor_set_blink_enabled(const bool p_enabled); + bool is_caret_blink_enabled() const; + void set_caret_blink_enabled(const bool p_enabled); - float cursor_get_blink_speed() const; - void cursor_set_blink_speed(const float p_speed); + float get_caret_blink_speed() const; + void set_caret_blink_speed(const float p_speed); - bool cursor_get_force_displayed() const; - void cursor_set_force_displayed(const bool p_enabled); + void set_caret_force_displayed(const bool p_enabled); + bool is_caret_force_displayed() const; void copy_text(); void cut_text(); @@ -297,8 +297,8 @@ public: virtual Size2 get_minimum_size() const override; - void set_expand_to_text_length(bool p_enabled); - bool get_expand_to_text_length() const; + void set_expand_to_text_length_enabled(bool p_enabled); + bool is_expand_to_text_length_enabled() const; void set_clear_button_enabled(bool p_enabled); bool is_clear_button_enabled() const; diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index f05b43377f..b9f4a7a741 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -438,7 +438,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("font_selected_color", "LineEdit", Color(0, 0, 0)); theme->set_color("font_uneditable_color", "LineEdit", Color(control_font_color.r, control_font_color.g, control_font_color.b, 0.5f)); theme->set_color("font_outline_color", "LineEdit", Color(1, 1, 1)); - theme->set_color("cursor_color", "LineEdit", control_font_hover_color); + theme->set_color("caret_color", "LineEdit", control_font_hover_color); theme->set_color("selection_color", "LineEdit", control_selection_color); theme->set_color("clear_button_color", "LineEdit", control_font_color); theme->set_color("clear_button_color_pressed", "LineEdit", control_font_pressed_color); diff --git a/scene/resources/particles_material.cpp b/scene/resources/particles_material.cpp index 6a65173176..bb47eebe9b 100644 --- a/scene/resources/particles_material.cpp +++ b/scene/resources/particles_material.cpp @@ -289,7 +289,7 @@ void ParticlesMaterial::_update_shader() { code += "}\n"; code += "\n"; - code += "void process() {\n"; + code += "void start() {\n"; code += " uint base_number = NUMBER;\n"; code += " uint alt_seed = hash(base_number + uint(1) + RANDOM_SEED);\n"; code += " float angle_rand = rand_from_seed(alt_seed);\n"; @@ -305,97 +305,94 @@ void ParticlesMaterial::_update_shader() { code += " ivec2 emission_tex_size = textureSize(emission_texture_points, 0);\n"; code += " ivec2 emission_tex_ofs = ivec2(point % emission_tex_size.x, point / emission_tex_size.x);\n"; } - code += " float tv = 0.0;\n"; - code += " if (RESTART) {\n"; - if (tex_parameters[PARAM_ANGLE].is_valid()) { - code += " float tex_angle = textureLod(angle_texture, vec2(0.0, 0.0), 0.0).r;\n"; + code += " float tex_angle = textureLod(angle_texture, vec2(0.0, 0.0), 0.0).r;\n"; } else { - code += " float tex_angle = 0.0;\n"; + code += " float tex_angle = 0.0;\n"; } if (tex_parameters[PARAM_ANIM_OFFSET].is_valid()) { - code += " float tex_anim_offset = textureLod(anim_offset_texture, vec2(0.0, 0.0), 0.0).r;\n"; + code += " float tex_anim_offset = textureLod(anim_offset_texture, vec2(0.0, 0.0), 0.0).r;\n"; } else { - code += " float tex_anim_offset = 0.0;\n"; + code += " float tex_anim_offset = 0.0;\n"; } - code += " float spread_rad = spread * degree_to_rad;\n"; + code += " float spread_rad = spread * degree_to_rad;\n"; - code += " if (RESTART_VELOCITY) {\n"; + code += " if (RESTART_VELOCITY) {\n"; if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) { - code += " float tex_linear_velocity = textureLod(linear_velocity_texture, vec2(0.0, 0.0), 0.0).r;\n"; + code += " float tex_linear_velocity = textureLod(linear_velocity_texture, vec2(0.0, 0.0), 0.0).r;\n"; } else { - code += " float tex_linear_velocity = 0.0;\n"; + code += " float tex_linear_velocity = 0.0;\n"; } if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { - code += " float angle1_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad;\n"; - code += " angle1_rad += direction.x != 0.0 ? atan(direction.y, direction.x) : sign(direction.y) * (pi / 2.0);\n"; - code += " vec3 rot = vec3(cos(angle1_rad), sin(angle1_rad), 0.0);\n"; - code += " VELOCITY = rot * initial_linear_velocity * mix(1.0, rand_from_seed(alt_seed), initial_linear_velocity_random);\n"; + code += " float angle1_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad;\n"; + code += " angle1_rad += direction.x != 0.0 ? atan(direction.y, direction.x) : sign(direction.y) * (pi / 2.0);\n"; + code += " vec3 rot = vec3(cos(angle1_rad), sin(angle1_rad), 0.0);\n"; + code += " VELOCITY = rot * initial_linear_velocity * mix(1.0, rand_from_seed(alt_seed), initial_linear_velocity_random);\n"; } else { //initiate velocity spread in 3D - code += " float angle1_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad;\n"; - code += " float angle2_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad * (1.0 - flatness);\n"; - code += " vec3 direction_xz = vec3(sin(angle1_rad), 0.0, cos(angle1_rad));\n"; - code += " vec3 direction_yz = vec3(0.0, sin(angle2_rad), cos(angle2_rad));\n"; - code += " direction_yz.z = direction_yz.z / max(0.0001,sqrt(abs(direction_yz.z))); // better uniform distribution\n"; - code += " vec3 spread_direction = vec3(direction_xz.x * direction_yz.z, direction_yz.y, direction_xz.z * direction_yz.z);\n"; - code += " vec3 direction_nrm = normalize(direction);\n"; - code += " // rotate spread to direction\n"; - code += " vec3 binormal = cross(vec3(0.0, 1.0, 0.0), direction_nrm);\n"; - code += " if (length(binormal) < 0.0001) {\n"; - code += " // direction is parallel to Y. Choose Z as the binormal.\n"; - code += " binormal = vec3(0.0, 0.0, 1.0);\n"; - code += " }\n"; - code += " binormal = normalize(binormal);\n"; - code += " vec3 normal = cross(binormal, direction_nrm);\n"; - code += " spread_direction = binormal * spread_direction.x + normal * spread_direction.y + direction_nrm * spread_direction.z;\n"; - code += " VELOCITY = spread_direction * initial_linear_velocity * mix(1.0, rand_from_seed(alt_seed), initial_linear_velocity_random);\n"; + code += " float angle1_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad;\n"; + code += " float angle2_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad * (1.0 - flatness);\n"; + code += " vec3 direction_xz = vec3(sin(angle1_rad), 0.0, cos(angle1_rad));\n"; + code += " vec3 direction_yz = vec3(0.0, sin(angle2_rad), cos(angle2_rad));\n"; + code += " direction_yz.z = direction_yz.z / max(0.0001,sqrt(abs(direction_yz.z))); // better uniform distribution\n"; + code += " vec3 spread_direction = vec3(direction_xz.x * direction_yz.z, direction_yz.y, direction_xz.z * direction_yz.z);\n"; + code += " vec3 direction_nrm = normalize(direction);\n"; + code += " // rotate spread to direction\n"; + code += " vec3 binormal = cross(vec3(0.0, 1.0, 0.0), direction_nrm);\n"; + code += " if (length(binormal) < 0.0001) {\n"; + code += " // direction is parallel to Y. Choose Z as the binormal.\n"; + code += " binormal = vec3(0.0, 0.0, 1.0);\n"; + code += " }\n"; + code += " binormal = normalize(binormal);\n"; + code += " vec3 normal = cross(binormal, direction_nrm);\n"; + code += " spread_direction = binormal * spread_direction.x + normal * spread_direction.y + direction_nrm * spread_direction.z;\n"; + code += " VELOCITY = spread_direction * initial_linear_velocity * mix(1.0, rand_from_seed(alt_seed), initial_linear_velocity_random);\n"; } - code += " }\n"; + code += " }\n"; - code += " float base_angle = (initial_angle + tex_angle) * mix(1.0, angle_rand, initial_angle_random);\n"; - code += " CUSTOM.x = base_angle * degree_to_rad;\n"; // angle - code += " CUSTOM.y = 0.0;\n"; // phase - code += " CUSTOM.w = (1.0 - lifetime_randomness * rand_from_seed(alt_seed));\n"; - code += " CUSTOM.z = (anim_offset + tex_anim_offset) * mix(1.0, anim_offset_rand, anim_offset_random);\n"; // animation offset (0-1) + code += " float base_angle = (initial_angle + tex_angle) * mix(1.0, angle_rand, initial_angle_random);\n"; + code += " CUSTOM.x = base_angle * degree_to_rad;\n"; // angle + code += " CUSTOM.y = 0.0;\n"; // phase + code += " CUSTOM.w = (1.0 - lifetime_randomness * rand_from_seed(alt_seed));\n"; + code += " CUSTOM.z = (anim_offset + tex_anim_offset) * mix(1.0, anim_offset_rand, anim_offset_random);\n"; // animation offset (0-1) - code += " if (RESTART_POSITION) {\n"; + code += " if (RESTART_POSITION) {\n"; switch (emission_shape) { case EMISSION_SHAPE_POINT: { //do none, identity (will later be multiplied by emission transform) - code += " TRANSFORM = mat4(vec4(1,0,0,0),vec4(0,1,0,0),vec4(0,0,1,0),vec4(0,0,0,1));\n"; + code += " TRANSFORM = mat4(vec4(1,0,0,0),vec4(0,1,0,0),vec4(0,0,1,0),vec4(0,0,0,1));\n"; } break; case EMISSION_SHAPE_SPHERE: { - code += " float s = rand_from_seed(alt_seed) * 2.0 - 1.0;\n"; - code += " float t = rand_from_seed(alt_seed) * 2.0 * pi;\n"; - code += " float radius = emission_sphere_radius * sqrt(1.0 - s * s);\n"; - code += " TRANSFORM[3].xyz = vec3(radius * cos(t), radius * sin(t), emission_sphere_radius * s);\n"; + code += " float s = rand_from_seed(alt_seed) * 2.0 - 1.0;\n"; + code += " float t = rand_from_seed(alt_seed) * 2.0 * pi;\n"; + code += " float radius = emission_sphere_radius * sqrt(1.0 - s * s);\n"; + code += " TRANSFORM[3].xyz = vec3(radius * cos(t), radius * sin(t), emission_sphere_radius * s);\n"; } break; case EMISSION_SHAPE_BOX: { - code += " TRANSFORM[3].xyz = vec3(rand_from_seed(alt_seed) * 2.0 - 1.0, rand_from_seed(alt_seed) * 2.0 - 1.0, rand_from_seed(alt_seed) * 2.0 - 1.0) * emission_box_extents;\n"; + code += " TRANSFORM[3].xyz = vec3(rand_from_seed(alt_seed) * 2.0 - 1.0, rand_from_seed(alt_seed) * 2.0 - 1.0, rand_from_seed(alt_seed) * 2.0 - 1.0) * emission_box_extents;\n"; } break; case EMISSION_SHAPE_POINTS: case EMISSION_SHAPE_DIRECTED_POINTS: { - code += " TRANSFORM[3].xyz = texelFetch(emission_texture_points, emission_tex_ofs, 0).xyz;\n"; + code += " TRANSFORM[3].xyz = texelFetch(emission_texture_points, emission_tex_ofs, 0).xyz;\n"; if (emission_shape == EMISSION_SHAPE_DIRECTED_POINTS) { if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { - code += " mat2 rotm;"; - code += " rotm[0] = texelFetch(emission_texture_normal, emission_tex_ofs, 0).xy;\n"; - code += " rotm[1] = rotm[0].yx * vec2(1.0, -1.0);\n"; - code += " if (RESTART_VELOCITY) VELOCITY.xy = rotm * VELOCITY.xy;\n"; + code += " mat2 rotm;"; + code += " rotm[0] = texelFetch(emission_texture_normal, emission_tex_ofs, 0).xy;\n"; + code += " rotm[1] = rotm[0].yx * vec2(1.0, -1.0);\n"; + code += " if (RESTART_VELOCITY) VELOCITY.xy = rotm * VELOCITY.xy;\n"; } else { - code += " vec3 normal = texelFetch(emission_texture_normal, emission_tex_ofs, 0).xyz;\n"; - code += " vec3 v0 = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0);\n"; - code += " vec3 tangent = normalize(cross(v0, normal));\n"; - code += " vec3 bitangent = normalize(cross(tangent, normal));\n"; - code += " if (RESTART_VELOCITY) VELOCITY = mat3(tangent, bitangent, normal) * VELOCITY;\n"; + code += " vec3 normal = texelFetch(emission_texture_normal, emission_tex_ofs, 0).xyz;\n"; + code += " vec3 v0 = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0);\n"; + code += " vec3 tangent = normalize(cross(v0, normal));\n"; + code += " vec3 bitangent = normalize(cross(tangent, normal));\n"; + code += " if (RESTART_VELOCITY) VELOCITY = mat3(tangent, bitangent, normal) * VELOCITY;\n"; } } } break; @@ -404,134 +401,144 @@ void ParticlesMaterial::_update_shader() { } } - code += " if (RESTART_VELOCITY) VELOCITY = (EMISSION_TRANSFORM * vec4(VELOCITY, 0.0)).xyz;\n"; - code += " TRANSFORM = EMISSION_TRANSFORM * TRANSFORM;\n"; + code += " if (RESTART_VELOCITY) VELOCITY = (EMISSION_TRANSFORM * vec4(VELOCITY, 0.0)).xyz;\n"; + code += " TRANSFORM = EMISSION_TRANSFORM * TRANSFORM;\n"; if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { - code += " VELOCITY.z = 0.0;\n"; - code += " TRANSFORM[3].z = 0.0;\n"; + code += " VELOCITY.z = 0.0;\n"; + code += " TRANSFORM[3].z = 0.0;\n"; } - code += " }\n"; + code += " }\n"; + code += "}\n\n"; - code += " } else {\n"; + code += "void process() {\n"; + code += " uint base_number = NUMBER;\n"; + code += " uint alt_seed = hash(base_number + uint(1) + RANDOM_SEED);\n"; + code += " float angle_rand = rand_from_seed(alt_seed);\n"; + code += " float scale_rand = rand_from_seed(alt_seed);\n"; + code += " float hue_rot_rand = rand_from_seed(alt_seed);\n"; + code += " float anim_offset_rand = rand_from_seed(alt_seed);\n"; + code += " float pi = 3.14159;\n"; + code += " float degree_to_rad = pi / 180.0;\n"; + code += "\n"; - code += " CUSTOM.y += DELTA / LIFETIME;\n"; - code += " tv = CUSTOM.y / CUSTOM.w;\n"; + code += " CUSTOM.y += DELTA / LIFETIME;\n"; + code += " float tv = CUSTOM.y / CUSTOM.w;\n"; if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) { - code += " float tex_linear_velocity = textureLod(linear_velocity_texture, vec2(tv, 0.0), 0.0).r;\n"; + code += " float tex_linear_velocity = textureLod(linear_velocity_texture, vec2(tv, 0.0), 0.0).r;\n"; } else { - code += " float tex_linear_velocity = 0.0;\n"; + code += " float tex_linear_velocity = 0.0;\n"; } if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { if (tex_parameters[PARAM_ORBIT_VELOCITY].is_valid()) { - code += " float tex_orbit_velocity = textureLod(orbit_velocity_texture, vec2(tv, 0.0), 0.0).r;\n"; + code += " float tex_orbit_velocity = textureLod(orbit_velocity_texture, vec2(tv, 0.0), 0.0).r;\n"; } else { - code += " float tex_orbit_velocity = 0.0;\n"; + code += " float tex_orbit_velocity = 0.0;\n"; } } if (tex_parameters[PARAM_ANGULAR_VELOCITY].is_valid()) { - code += " float tex_angular_velocity = textureLod(angular_velocity_texture, vec2(tv, 0.0), 0.0).r;\n"; + code += " float tex_angular_velocity = textureLod(angular_velocity_texture, vec2(tv, 0.0), 0.0).r;\n"; } else { - code += " float tex_angular_velocity = 0.0;\n"; + code += " float tex_angular_velocity = 0.0;\n"; } if (tex_parameters[PARAM_LINEAR_ACCEL].is_valid()) { - code += " float tex_linear_accel = textureLod(linear_accel_texture, vec2(tv, 0.0), 0.0).r;\n"; + code += " float tex_linear_accel = textureLod(linear_accel_texture, vec2(tv, 0.0), 0.0).r;\n"; } else { - code += " float tex_linear_accel = 0.0;\n"; + code += " float tex_linear_accel = 0.0;\n"; } if (tex_parameters[PARAM_RADIAL_ACCEL].is_valid()) { - code += " float tex_radial_accel = textureLod(radial_accel_texture, vec2(tv, 0.0), 0.0).r;\n"; + code += " float tex_radial_accel = textureLod(radial_accel_texture, vec2(tv, 0.0), 0.0).r;\n"; } else { - code += " float tex_radial_accel = 0.0;\n"; + code += " float tex_radial_accel = 0.0;\n"; } if (tex_parameters[PARAM_TANGENTIAL_ACCEL].is_valid()) { - code += " float tex_tangent_accel = textureLod(tangent_accel_texture, vec2(tv, 0.0), 0.0).r;\n"; + code += " float tex_tangent_accel = textureLod(tangent_accel_texture, vec2(tv, 0.0), 0.0).r;\n"; } else { - code += " float tex_tangent_accel = 0.0;\n"; + code += " float tex_tangent_accel = 0.0;\n"; } if (tex_parameters[PARAM_DAMPING].is_valid()) { - code += " float tex_damping = textureLod(damping_texture, vec2(tv, 0.0), 0.0).r;\n"; + code += " float tex_damping = textureLod(damping_texture, vec2(tv, 0.0), 0.0).r;\n"; } else { - code += " float tex_damping = 0.0;\n"; + code += " float tex_damping = 0.0;\n"; } if (tex_parameters[PARAM_ANGLE].is_valid()) { - code += " float tex_angle = textureLod(angle_texture, vec2(tv, 0.0), 0.0).r;\n"; + code += " float tex_angle = textureLod(angle_texture, vec2(tv, 0.0), 0.0).r;\n"; } else { - code += " float tex_angle = 0.0;\n"; + code += " float tex_angle = 0.0;\n"; } if (tex_parameters[PARAM_ANIM_SPEED].is_valid()) { - code += " float tex_anim_speed = textureLod(anim_speed_texture, vec2(tv, 0.0), 0.0).r;\n"; + code += " float tex_anim_speed = textureLod(anim_speed_texture, vec2(tv, 0.0), 0.0).r;\n"; } else { - code += " float tex_anim_speed = 0.0;\n"; + code += " float tex_anim_speed = 0.0;\n"; } if (tex_parameters[PARAM_ANIM_OFFSET].is_valid()) { - code += " float tex_anim_offset = textureLod(anim_offset_texture, vec2(tv, 0.0), 0.0).r;\n"; + code += " float tex_anim_offset = textureLod(anim_offset_texture, vec2(tv, 0.0), 0.0).r;\n"; } else { - code += " float tex_anim_offset = 0.0;\n"; + code += " float tex_anim_offset = 0.0;\n"; } - code += " vec3 force = gravity;\n"; - code += " vec3 pos = TRANSFORM[3].xyz;\n"; + code += " vec3 force = gravity;\n"; + code += " vec3 pos = TRANSFORM[3].xyz;\n"; if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { - code += " pos.z = 0.0;\n"; - } - code += " // apply linear acceleration\n"; - code += " force += length(VELOCITY) > 0.0 ? normalize(VELOCITY) * (linear_accel + tex_linear_accel) * mix(1.0, rand_from_seed(alt_seed), linear_accel_random) : vec3(0.0);\n"; - code += " // apply radial acceleration\n"; - code += " vec3 org = EMISSION_TRANSFORM[3].xyz;\n"; - code += " vec3 diff = pos - org;\n"; - code += " force += length(diff) > 0.0 ? normalize(diff) * (radial_accel + tex_radial_accel) * mix(1.0, rand_from_seed(alt_seed), radial_accel_random) : vec3(0.0);\n"; - code += " // apply tangential acceleration;\n"; + code += " pos.z = 0.0;\n"; + } + code += " // apply linear acceleration\n"; + code += " force += length(VELOCITY) > 0.0 ? normalize(VELOCITY) * (linear_accel + tex_linear_accel) * mix(1.0, rand_from_seed(alt_seed), linear_accel_random) : vec3(0.0);\n"; + code += " // apply radial acceleration\n"; + code += " vec3 org = EMISSION_TRANSFORM[3].xyz;\n"; + code += " vec3 diff = pos - org;\n"; + code += " force += length(diff) > 0.0 ? normalize(diff) * (radial_accel + tex_radial_accel) * mix(1.0, rand_from_seed(alt_seed), radial_accel_random) : vec3(0.0);\n"; + code += " // apply tangential acceleration;\n"; if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { - code += " force += length(diff.yx) > 0.0 ? vec3(normalize(diff.yx * vec2(-1.0, 1.0)), 0.0) * ((tangent_accel + tex_tangent_accel) * mix(1.0, rand_from_seed(alt_seed), tangent_accel_random)) : vec3(0.0);\n"; + code += " force += length(diff.yx) > 0.0 ? vec3(normalize(diff.yx * vec2(-1.0, 1.0)), 0.0) * ((tangent_accel + tex_tangent_accel) * mix(1.0, rand_from_seed(alt_seed), tangent_accel_random)) : vec3(0.0);\n"; } else { - code += " vec3 crossDiff = cross(normalize(diff), normalize(gravity));\n"; - code += " force += length(crossDiff) > 0.0 ? normalize(crossDiff) * ((tangent_accel + tex_tangent_accel) * mix(1.0, rand_from_seed(alt_seed), tangent_accel_random)) : vec3(0.0);\n"; + code += " vec3 crossDiff = cross(normalize(diff), normalize(gravity));\n"; + code += " force += length(crossDiff) > 0.0 ? normalize(crossDiff) * ((tangent_accel + tex_tangent_accel) * mix(1.0, rand_from_seed(alt_seed), tangent_accel_random)) : vec3(0.0);\n"; } if (attractor_interaction_enabled) { - code += " force += ATTRACTOR_FORCE;\n\n"; + code += " force += ATTRACTOR_FORCE;\n\n"; } - code += " // apply attractor forces\n"; - code += " VELOCITY += force * DELTA;\n"; - code += " // orbit velocity\n"; + code += " // apply attractor forces\n"; + code += " VELOCITY += force * DELTA;\n"; + code += " // orbit velocity\n"; if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { - code += " float orbit_amount = (orbit_velocity + tex_orbit_velocity) * mix(1.0, rand_from_seed(alt_seed), orbit_velocity_random);\n"; - code += " if (orbit_amount != 0.0) {\n"; - code += " float ang = orbit_amount * DELTA * pi * 2.0;\n"; - code += " mat2 rot = mat2(vec2(cos(ang), -sin(ang)), vec2(sin(ang), cos(ang)));\n"; - code += " TRANSFORM[3].xy -= diff.xy;\n"; - code += " TRANSFORM[3].xy += rot * diff.xy;\n"; - code += " }\n"; + code += " float orbit_amount = (orbit_velocity + tex_orbit_velocity) * mix(1.0, rand_from_seed(alt_seed), orbit_velocity_random);\n"; + code += " if (orbit_amount != 0.0) {\n"; + code += " float ang = orbit_amount * DELTA * pi * 2.0;\n"; + code += " mat2 rot = mat2(vec2(cos(ang), -sin(ang)), vec2(sin(ang), cos(ang)));\n"; + code += " TRANSFORM[3].xy -= diff.xy;\n"; + code += " TRANSFORM[3].xy += rot * diff.xy;\n"; + code += " }\n"; } if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) { - code += " VELOCITY = normalize(VELOCITY) * tex_linear_velocity;\n"; - } - code += " if (damping + tex_damping > 0.0) {\n"; - code += " float v = length(VELOCITY);\n"; - code += " float damp = (damping + tex_damping) * mix(1.0, rand_from_seed(alt_seed), damping_random);\n"; - code += " v -= damp * DELTA;\n"; - code += " if (v < 0.0) {\n"; - code += " VELOCITY = vec3(0.0);\n"; - code += " } else {\n"; - code += " VELOCITY = normalize(VELOCITY) * v;\n"; - code += " }\n"; + code += " VELOCITY = normalize(VELOCITY) * tex_linear_velocity;\n"; + } + code += " if (damping + tex_damping > 0.0) {\n"; + code += " float v = length(VELOCITY);\n"; + code += " float damp = (damping + tex_damping) * mix(1.0, rand_from_seed(alt_seed), damping_random);\n"; + code += " v -= damp * DELTA;\n"; + code += " if (v < 0.0) {\n"; + code += " VELOCITY = vec3(0.0);\n"; + code += " } else {\n"; + code += " VELOCITY = normalize(VELOCITY) * v;\n"; code += " }\n"; - code += " float base_angle = (initial_angle + tex_angle) * mix(1.0, angle_rand, initial_angle_random);\n"; - code += " base_angle += CUSTOM.y * LIFETIME * (angular_velocity + tex_angular_velocity) * mix(1.0, rand_from_seed(alt_seed) * 2.0 - 1.0, angular_velocity_random);\n"; - code += " CUSTOM.x = base_angle * degree_to_rad;\n"; // angle - code += " CUSTOM.z = (anim_offset + tex_anim_offset) * mix(1.0, anim_offset_rand, anim_offset_random) + CUSTOM.y * (anim_speed + tex_anim_speed) * mix(1.0, rand_from_seed(alt_seed), anim_speed_random);\n"; // angle code += " }\n"; + code += " float base_angle = (initial_angle + tex_angle) * mix(1.0, angle_rand, initial_angle_random);\n"; + code += " base_angle += CUSTOM.y * LIFETIME * (angular_velocity + tex_angular_velocity) * mix(1.0, rand_from_seed(alt_seed) * 2.0 - 1.0, angular_velocity_random);\n"; + code += " CUSTOM.x = base_angle * degree_to_rad;\n"; // angle + code += " CUSTOM.z = (anim_offset + tex_anim_offset) * mix(1.0, anim_offset_rand, anim_offset_random) + CUSTOM.y * (anim_speed + tex_anim_speed) * mix(1.0, rand_from_seed(alt_seed), anim_speed_random);\n"; // angle + // apply color // apply hue rotation if (tex_parameters[PARAM_SCALE].is_valid()) { @@ -659,7 +666,7 @@ void ParticlesMaterial::_update_shader() { code += " }"; } - code += " if (CUSTOM.y > CUSTOM.w) {"; + code += " if (CUSTOM.y > CUSTOM.w) {\n"; code += " ACTIVE = false;\n"; code += " }\n"; code += "}\n"; diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index e1e24ddab2..d09cf4b971 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -961,7 +961,8 @@ static const char *type_string[VisualShader::TYPE_MAX] = { "light", "emit", "process", - "end" + "end", + "sky", }; bool VisualShader::_set(const StringName &p_name, const Variant &p_value) { @@ -1476,7 +1477,7 @@ void VisualShader::_update_shader() const { global_code += "render_mode " + render_mode + ";\n\n"; } - static const char *func_name[TYPE_MAX] = { "vertex", "fragment", "light", "emit", "process", "end" }; + static const char *func_name[TYPE_MAX] = { "vertex", "fragment", "light", "emit", "process", "end", "sky" }; String global_expressions; Set<String> used_uniform_names; @@ -1667,6 +1668,7 @@ void VisualShader::_bind_methods() { BIND_ENUM_CONSTANT(TYPE_EMIT); BIND_ENUM_CONSTANT(TYPE_PROCESS); BIND_ENUM_CONSTANT(TYPE_END); + BIND_ENUM_CONSTANT(TYPE_SKY); BIND_ENUM_CONSTANT(TYPE_MAX); BIND_CONSTANT(NODE_ID_INVALID); @@ -1698,7 +1700,6 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "point_size", "POINT_SIZE" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "modelview", "MODELVIEW_MATRIX" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "camera", "CAMERA_MATRIX" }, @@ -1721,10 +1722,8 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "point_coord", "vec3(POINT_COORD, 0.0)" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "side", "float(FRONT_FACING ? 1.0 : 0.0)" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_camera", "INV_CAMERA_MATRIX" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "camera", "CAMERA_MATRIX" }, @@ -1750,7 +1749,6 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "specular", "SPECULAR_LIGHT" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "roughness", "ROUGHNESS" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "metallic", "METALLIC" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_camera", "INV_CAMERA_MATRIX" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "camera", "CAMERA_MATRIX" }, @@ -1759,6 +1757,7 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "viewport_size", "vec3(VIEWPORT_SIZE, 0.0)" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_BOOLEAN, "output_is_srgb", "OUTPUT_IS_SRGB" }, + // Canvas Item, Vertex { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "vec3(VERTEX, 0.0)" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" }, @@ -1766,12 +1765,12 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "point_size", "POINT_SIZE" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "texture_pixel_size", "vec3(TEXTURE_PIXEL_SIZE, 1.0)" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "canvas", "CANVAS_MATRIX" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "screen", "SCREEN_MATRIX" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_light_pass", "AT_LIGHT_PASS" }, + // Canvas Item, Fragment { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.xyz" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" }, @@ -1789,6 +1788,7 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "specular_shininess", "SPECULAR_SHININESS.rgb" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "specular_shininess_alpha", "SPECULAR_SHININESS.a" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "specular_shininess_texture", "SPECULAR_SHININESS_TEXTURE" }, + // Canvas Item, Light { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.xyz" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" }, @@ -1856,36 +1856,36 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_TRANSFORM, "emission_transform", "EMISSION_TRANSFORM" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, - // Sky, Fragment - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_cubemap_pass", "AT_CUBEMAP_PASS" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_half_res_pass", "AT_HALF_RES_PASS" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_quarter_res_pass", "AT_QUARTER_RES_PASS" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "eyedir", "EYEDIR" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "half_res_color", "HALF_RES_COLOR.rgb" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "half_res_alpha", "HALF_RES_COLOR.a" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "light0_color", "LIGHT0_COLOR" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "light0_direction", "LIGHT0_DIRECTION" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "light0_enabled", "LIGHT0_ENABLED" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "light0_energy", "LIGHT0_ENERGY" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "light1_color", "LIGHT1_COLOR" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "light1_direction", "LIGHT1_DIRECTION" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "light1_enabled", "LIGHT1_ENABLED" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "light1_energy", "LIGHT1_ENERGY" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "light2_color", "LIGHT2_COLOR" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "light2_direction", "LIGHT2_DIRECTION" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "light2_enabled", "LIGHT2_ENABLED" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "light2_energy", "LIGHT2_ENERGY" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "light3_color", "LIGHT3_COLOR" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "light3_direction", "LIGHT3_DIRECTION" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "light3_enabled", "LIGHT3_ENABLED" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "light3_energy", "LIGHT3_ENERGY" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "position", "POSITION" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "quarter_res_color", "QUARTER_RES_COLOR.rgb" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "quarter_res_alpha", "QUARTER_RES_COLOR.a" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "radiance", "RADIANCE" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "sky_coords", "vec3(SKY_COORDS, 0.0)" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + // Sky, Sky + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_cubemap_pass", "AT_CUBEMAP_PASS" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_half_res_pass", "AT_HALF_RES_PASS" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_quarter_res_pass", "AT_QUARTER_RES_PASS" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "eyedir", "EYEDIR" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "half_res_color", "HALF_RES_COLOR.rgb" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "half_res_alpha", "HALF_RES_COLOR.a" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light0_color", "LIGHT0_COLOR" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light0_direction", "LIGHT0_DIRECTION" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "light0_enabled", "LIGHT0_ENABLED" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "light0_energy", "LIGHT0_ENERGY" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light1_color", "LIGHT1_COLOR" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light1_direction", "LIGHT1_DIRECTION" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "light1_enabled", "LIGHT1_ENABLED" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "light1_energy", "LIGHT1_ENERGY" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light2_color", "LIGHT2_COLOR" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light2_direction", "LIGHT2_DIRECTION" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "light2_enabled", "LIGHT2_ENABLED" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "light2_energy", "LIGHT2_ENERGY" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light3_color", "LIGHT3_COLOR" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light3_direction", "LIGHT3_DIRECTION" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "light3_enabled", "LIGHT3_ENABLED" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "light3_energy", "LIGHT3_ENERGY" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "position", "POSITION" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "quarter_res_color", "QUARTER_RES_COLOR.rgb" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "quarter_res_alpha", "QUARTER_RES_COLOR.a" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SAMPLER, "radiance", "RADIANCE" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "sky_coords", "vec3(SKY_COORDS, 0.0)" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, { Shader::MODE_MAX, VisualShader::TYPE_MAX, VisualShaderNode::PORT_TYPE_TRANSFORM, nullptr, nullptr }, }; @@ -2449,9 +2449,9 @@ const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = { { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" }, - // Sky, Fragment - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR" }, - { Shader::MODE_SKY, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "ALPHA" }, + // Sky, Sky + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "ALPHA" }, { Shader::MODE_MAX, VisualShader::TYPE_MAX, VisualShaderNode::PORT_TYPE_TRANSFORM, nullptr, nullptr }, }; diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h index 54a5c19049..e2e1b473ed 100644 --- a/scene/resources/visual_shader.h +++ b/scene/resources/visual_shader.h @@ -54,6 +54,7 @@ public: TYPE_EMIT, TYPE_PROCESS, TYPE_END, + TYPE_SKY, TYPE_MAX }; diff --git a/servers/rendering/renderer_rd/renderer_storage_rd.cpp b/servers/rendering/renderer_rd/renderer_storage_rd.cpp index 92df9cc702..189c5782f4 100644 --- a/servers/rendering/renderer_rd/renderer_storage_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_storage_rd.cpp @@ -4781,6 +4781,7 @@ void RendererStorageRD::ParticlesShaderData::set_code(const String &p_code) { ShaderCompilerRD::GeneratedCode gen_code; ShaderCompilerRD::IdentifierActions actions; + actions.entry_point_stages["start"] = ShaderCompilerRD::STAGE_COMPUTE; actions.entry_point_stages["process"] = ShaderCompilerRD::STAGE_COMPUTE; /* diff --git a/servers/rendering/renderer_rd/shader_rd.cpp b/servers/rendering/renderer_rd/shader_rd.cpp index c48a2ef48c..f7242a2b17 100644 --- a/servers/rendering/renderer_rd/shader_rd.cpp +++ b/servers/rendering/renderer_rd/shader_rd.cpp @@ -233,6 +233,7 @@ void ShaderRD::_compile_variant(uint32_t p_variant, Version *p_version) { _build_variant_code(builder, p_variant, p_version, stage_templates[STAGE_TYPE_COMPUTE]); current_source = builder.as_string(); + RD::ShaderStageData stage; stage.spir_v = RD::get_singleton()->shader_compile_from_source(RD::SHADER_STAGE_COMPUTE, current_source, RD::SHADER_LANGUAGE_GLSL, &error); if (stage.spir_v.size() == 0) { diff --git a/servers/rendering/renderer_rd/shaders/particles.glsl b/servers/rendering/renderer_rd/shaders/particles.glsl index c438352c05..3712220dc4 100644 --- a/servers/rendering/renderer_rd/shaders/particles.glsl +++ b/servers/rendering/renderer_rd/shaders/particles.glsl @@ -252,6 +252,115 @@ void main() { /* Process physics if active */ + if (params.sub_emitter_mode) { + if (!PARTICLE.is_active) { + int src_index = atomicAdd(src_particles.particle_count, -1) - 1; + + if (src_index >= 0) { + PARTICLE.is_active = true; + restart = true; + + if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_POSITION)) { + PARTICLE.xform[3] = src_particles.data[src_index].xform[3]; + } else { + PARTICLE.xform[3] = vec4(0, 0, 0, 1); + restart_position = true; + } + if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_ROTATION_SCALE)) { + PARTICLE.xform[0] = src_particles.data[src_index].xform[0]; + PARTICLE.xform[1] = src_particles.data[src_index].xform[1]; + PARTICLE.xform[2] = src_particles.data[src_index].xform[2]; + } else { + PARTICLE.xform[0] = vec4(1, 0, 0, 0); + PARTICLE.xform[1] = vec4(0, 1, 0, 0); + PARTICLE.xform[2] = vec4(0, 0, 1, 0); + restart_rotation_scale = true; + } + if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_VELOCITY)) { + PARTICLE.velocity = src_particles.data[src_index].velocity; + } else { + PARTICLE.velocity = vec3(0); + restart_velocity = true; + } + if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_COLOR)) { + PARTICLE.color = src_particles.data[src_index].color; + } else { + PARTICLE.color = vec4(1); + restart_color = true; + } + + if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_CUSTOM)) { + PARTICLE.custom = src_particles.data[src_index].custom; + } else { + PARTICLE.custom = vec4(0); + restart_custom = true; + } + } + } + + } else if (FRAME.emitting) { + float restart_phase = float(index) / float(params.total_particles); + + if (FRAME.randomness > 0.0) { + uint seed = FRAME.cycle; + if (restart_phase >= FRAME.system_phase) { + seed -= uint(1); + } + seed *= uint(params.total_particles); + seed += uint(index); + float random = float(hash(seed) % uint(65536)) / 65536.0; + restart_phase += FRAME.randomness * random * 1.0 / float(params.total_particles); + } + + restart_phase *= (1.0 - FRAME.explosiveness); + + if (FRAME.system_phase > FRAME.prev_system_phase) { + // restart_phase >= prev_system_phase is used so particles emit in the first frame they are processed + + if (restart_phase >= FRAME.prev_system_phase && restart_phase < FRAME.system_phase) { + restart = true; + if (params.use_fractional_delta) { + local_delta = (FRAME.system_phase - restart_phase) * params.lifetime; + } + } + + } else if (FRAME.delta > 0.0) { + if (restart_phase >= FRAME.prev_system_phase) { + restart = true; + if (params.use_fractional_delta) { + local_delta = (1.0 - restart_phase + FRAME.system_phase) * params.lifetime; + } + + } else if (restart_phase < FRAME.system_phase) { + restart = true; + if (params.use_fractional_delta) { + local_delta = (FRAME.system_phase - restart_phase) * params.lifetime; + } + } + } + + uint current_cycle = FRAME.cycle; + + if (FRAME.system_phase < restart_phase) { + current_cycle -= uint(1); + } + + uint particle_number = current_cycle * uint(params.total_particles) + particle; + + if (restart) { + PARTICLE.is_active = FRAME.emitting; + restart_position = true; + restart_rotation_scale = true; + restart_velocity = true; + restart_color = true; + restart_custom = true; + } + } + + if (restart && PARTICLE.is_active) { +#CODE : START + } + if (PARTICLE.is_active) { for (uint i = 0; i < FRAME.attractor_count; i++) { vec3 dir; @@ -430,111 +539,6 @@ void main() { } } - if (params.sub_emitter_mode) { - if (!PARTICLE.is_active) { - int src_index = atomicAdd(src_particles.particle_count, -1) - 1; - - if (src_index >= 0) { - PARTICLE.is_active = true; - restart = true; - - if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_POSITION)) { - PARTICLE.xform[3] = src_particles.data[src_index].xform[3]; - } else { - PARTICLE.xform[3] = vec4(0, 0, 0, 1); - restart_position = true; - } - if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_ROTATION_SCALE)) { - PARTICLE.xform[0] = src_particles.data[src_index].xform[0]; - PARTICLE.xform[1] = src_particles.data[src_index].xform[1]; - PARTICLE.xform[2] = src_particles.data[src_index].xform[2]; - } else { - PARTICLE.xform[0] = vec4(1, 0, 0, 0); - PARTICLE.xform[1] = vec4(0, 1, 0, 0); - PARTICLE.xform[2] = vec4(0, 0, 1, 0); - restart_rotation_scale = true; - } - if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_VELOCITY)) { - PARTICLE.velocity = src_particles.data[src_index].velocity; - } else { - PARTICLE.velocity = vec3(0); - restart_velocity = true; - } - if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_COLOR)) { - PARTICLE.color = src_particles.data[src_index].color; - } else { - PARTICLE.color = vec4(1); - restart_color = true; - } - - if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_CUSTOM)) { - PARTICLE.custom = src_particles.data[src_index].custom; - } else { - PARTICLE.custom = vec4(0); - restart_custom = true; - } - } - } - - } else if (FRAME.emitting) { - float restart_phase = float(index) / float(params.total_particles); - - if (FRAME.randomness > 0.0) { - uint seed = FRAME.cycle; - if (restart_phase >= FRAME.system_phase) { - seed -= uint(1); - } - seed *= uint(params.total_particles); - seed += uint(index); - float random = float(hash(seed) % uint(65536)) / 65536.0; - restart_phase += FRAME.randomness * random * 1.0 / float(params.total_particles); - } - - restart_phase *= (1.0 - FRAME.explosiveness); - - if (FRAME.system_phase > FRAME.prev_system_phase) { - // restart_phase >= prev_system_phase is used so particles emit in the first frame they are processed - - if (restart_phase >= FRAME.prev_system_phase && restart_phase < FRAME.system_phase) { - restart = true; - if (params.use_fractional_delta) { - local_delta = (FRAME.system_phase - restart_phase) * params.lifetime; - } - } - - } else if (FRAME.delta > 0.0) { - if (restart_phase >= FRAME.prev_system_phase) { - restart = true; - if (params.use_fractional_delta) { - local_delta = (1.0 - restart_phase + FRAME.system_phase) * params.lifetime; - } - - } else if (restart_phase < FRAME.system_phase) { - restart = true; - if (params.use_fractional_delta) { - local_delta = (FRAME.system_phase - restart_phase) * params.lifetime; - } - } - } - - uint current_cycle = FRAME.cycle; - - if (FRAME.system_phase < restart_phase) { - current_cycle -= uint(1); - } - - uint particle_number = current_cycle * uint(params.total_particles) + particle; - - if (restart) { - PARTICLE.is_active = FRAME.emitting; - restart_position = true; - restart_rotation_scale = true; - restart_velocity = true; - restart_color = true; - restart_custom = true; - } - } - if (PARTICLE.is_active) { #CODE : PROCESS } diff --git a/servers/rendering/shader_types.cpp b/servers/rendering/shader_types.cpp index f5f7e2e53d..460fd5fc97 100644 --- a/servers/rendering/shader_types.cpp +++ b/servers/rendering/shader_types.cpp @@ -316,6 +316,27 @@ ShaderTypes::ShaderTypes() { /************ PARTICLES **************************/ shader_modes[RS::SHADER_PARTICLES].functions["global"].built_ins["TIME"] = constt(ShaderLanguage::TYPE_FLOAT); + + shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["COLOR"] = ShaderLanguage::TYPE_VEC4; + shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["VELOCITY"] = ShaderLanguage::TYPE_VEC3; + shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["MASS"] = ShaderLanguage::TYPE_FLOAT; + shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["ACTIVE"] = ShaderLanguage::TYPE_BOOL; + shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["RESTART"] = constt(ShaderLanguage::TYPE_BOOL); + shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["CUSTOM"] = ShaderLanguage::TYPE_VEC4; + shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["TRANSFORM"] = ShaderLanguage::TYPE_MAT4; + shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["LIFETIME"] = constt(ShaderLanguage::TYPE_FLOAT); + shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["DELTA"] = constt(ShaderLanguage::TYPE_FLOAT); + shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["NUMBER"] = constt(ShaderLanguage::TYPE_UINT); + shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["INDEX"] = constt(ShaderLanguage::TYPE_INT); + shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["EMISSION_TRANSFORM"] = constt(ShaderLanguage::TYPE_MAT4); + shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["RANDOM_SEED"] = constt(ShaderLanguage::TYPE_UINT); + shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["RESTART_POSITION"] = constt(ShaderLanguage::TYPE_BOOL); + shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["RESTART_ROT_SCALE"] = constt(ShaderLanguage::TYPE_BOOL); + shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["RESTART_VELOCITY"] = constt(ShaderLanguage::TYPE_BOOL); + shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["RESTART_COLOR"] = constt(ShaderLanguage::TYPE_BOOL); + shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["RESTART_CUSTOM"] = constt(ShaderLanguage::TYPE_BOOL); + shader_modes[RS::SHADER_PARTICLES].functions["start"].main_function = true; + shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["COLOR"] = ShaderLanguage::TYPE_VEC4; shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["VELOCITY"] = ShaderLanguage::TYPE_VEC3; shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["MASS"] = ShaderLanguage::TYPE_FLOAT; @@ -334,11 +355,6 @@ ShaderTypes::ShaderTypes() { shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["FLAG_EMIT_VELOCITY"] = constt(ShaderLanguage::TYPE_UINT); shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["FLAG_EMIT_COLOR"] = constt(ShaderLanguage::TYPE_UINT); shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["FLAG_EMIT_CUSTOM"] = constt(ShaderLanguage::TYPE_UINT); - shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["RESTART_POSITION"] = constt(ShaderLanguage::TYPE_BOOL); - shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["RESTART_ROT_SCALE"] = constt(ShaderLanguage::TYPE_BOOL); - shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["RESTART_VELOCITY"] = constt(ShaderLanguage::TYPE_BOOL); - shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["RESTART_COLOR"] = constt(ShaderLanguage::TYPE_BOOL); - shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["RESTART_CUSTOM"] = constt(ShaderLanguage::TYPE_BOOL); shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["COLLIDED"] = constt(ShaderLanguage::TYPE_BOOL); shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["COLLISION_NORMAL"] = constt(ShaderLanguage::TYPE_VEC3); shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["COLLISION_DEPTH"] = constt(ShaderLanguage::TYPE_FLOAT); diff --git a/thirdparty/README.md b/thirdparty/README.md index dbdc568d64..33ce2423d9 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -101,6 +101,7 @@ Files extracted from upstream source: ``` - `AUTHORS.txt` and `LICENSE.txt` + ## fonts - `NotoSans*.ttf`, `NotoNaskhArabicUI_Regular.ttf`: @@ -343,7 +344,7 @@ File extracted from upstream release tarball: ## meshoptimizer - Upstream: https://github.com/zeux/meshoptimizer -- Version: git (e3f53f66e7a35b9b8764bee478589d79e34fa698, 2021) +- Version: 0.16 (95893c0566646434dd675b708d293fcb2d526d08, 2021) - License: MIT Files extracted from upstream repository: diff --git a/thirdparty/meshoptimizer/LICENSE.md b/thirdparty/meshoptimizer/LICENSE.md index 4fcd766d22..3c52415f62 100644 --- a/thirdparty/meshoptimizer/LICENSE.md +++ b/thirdparty/meshoptimizer/LICENSE.md @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2016-2020 Arseny Kapoulkine +Copyright (c) 2016-2021 Arseny Kapoulkine Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/thirdparty/meshoptimizer/clusterizer.cpp b/thirdparty/meshoptimizer/clusterizer.cpp index f7d88c5136..f8aad7b49c 100644 --- a/thirdparty/meshoptimizer/clusterizer.cpp +++ b/thirdparty/meshoptimizer/clusterizer.cpp @@ -2,6 +2,7 @@ #include "meshoptimizer.h" #include <assert.h> +#include <float.h> #include <math.h> #include <string.h> @@ -12,6 +13,68 @@ namespace meshopt { +// This must be <= 255 since index 0xff is used internally to indice a vertex that doesn't belong to a meshlet +const size_t kMeshletMaxVertices = 255; + +// A reasonable limit is around 2*max_vertices or less +const size_t kMeshletMaxTriangles = 512; + +struct TriangleAdjacency2 +{ + unsigned int* counts; + unsigned int* offsets; + unsigned int* data; +}; + +static void buildTriangleAdjacency(TriangleAdjacency2& adjacency, const unsigned int* indices, size_t index_count, size_t vertex_count, meshopt_Allocator& allocator) +{ + size_t face_count = index_count / 3; + + // allocate arrays + adjacency.counts = allocator.allocate<unsigned int>(vertex_count); + adjacency.offsets = allocator.allocate<unsigned int>(vertex_count); + adjacency.data = allocator.allocate<unsigned int>(index_count); + + // fill triangle counts + memset(adjacency.counts, 0, vertex_count * sizeof(unsigned int)); + + for (size_t i = 0; i < index_count; ++i) + { + assert(indices[i] < vertex_count); + + adjacency.counts[indices[i]]++; + } + + // fill offset table + unsigned int offset = 0; + + for (size_t i = 0; i < vertex_count; ++i) + { + adjacency.offsets[i] = offset; + offset += adjacency.counts[i]; + } + + assert(offset == index_count); + + // fill triangle data + for (size_t i = 0; i < face_count; ++i) + { + unsigned int a = indices[i * 3 + 0], b = indices[i * 3 + 1], c = indices[i * 3 + 2]; + + adjacency.data[adjacency.offsets[a]++] = unsigned(i); + adjacency.data[adjacency.offsets[b]++] = unsigned(i); + adjacency.data[adjacency.offsets[c]++] = unsigned(i); + } + + // fix offsets that have been disturbed by the previous pass + for (size_t i = 0; i < vertex_count; ++i) + { + assert(adjacency.offsets[i] >= adjacency.counts[i]); + + adjacency.offsets[i] -= adjacency.counts[i]; + } +} + static void computeBoundingSphere(float result[4], const float points[][3], size_t count) { assert(count > 0); @@ -82,13 +145,310 @@ static void computeBoundingSphere(float result[4], const float points[][3], size result[3] = radius; } +struct Cone +{ + float px, py, pz; + float nx, ny, nz; +}; + +static float getMeshletScore(float distance2, float spread, float cone_weight, float expected_radius) +{ + float cone = 1.f - spread * cone_weight; + float cone_clamped = cone < 1e-3f ? 1e-3f : cone; + + return (1 + sqrtf(distance2) / expected_radius * (1 - cone_weight)) * cone_clamped; +} + +static Cone getMeshletCone(const Cone& acc, unsigned int triangle_count) +{ + Cone result = acc; + + float center_scale = triangle_count == 0 ? 0.f : 1.f / float(triangle_count); + + result.px *= center_scale; + result.py *= center_scale; + result.pz *= center_scale; + + float axis_length = result.nx * result.nx + result.ny * result.ny + result.nz * result.nz; + float axis_scale = axis_length == 0.f ? 0.f : 1.f / sqrtf(axis_length); + + result.nx *= axis_scale; + result.ny *= axis_scale; + result.nz *= axis_scale; + + return result; +} + +static float computeTriangleCones(Cone* triangles, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride) +{ + (void)vertex_count; + + size_t vertex_stride_float = vertex_positions_stride / sizeof(float); + size_t face_count = index_count / 3; + + float mesh_area = 0; + + for (size_t i = 0; i < face_count; ++i) + { + unsigned int a = indices[i * 3 + 0], b = indices[i * 3 + 1], c = indices[i * 3 + 2]; + assert(a < vertex_count && b < vertex_count && c < vertex_count); + + const float* p0 = vertex_positions + vertex_stride_float * a; + const float* p1 = vertex_positions + vertex_stride_float * b; + const float* p2 = vertex_positions + vertex_stride_float * c; + + float p10[3] = {p1[0] - p0[0], p1[1] - p0[1], p1[2] - p0[2]}; + float p20[3] = {p2[0] - p0[0], p2[1] - p0[1], p2[2] - p0[2]}; + + float normalx = p10[1] * p20[2] - p10[2] * p20[1]; + float normaly = p10[2] * p20[0] - p10[0] * p20[2]; + float normalz = p10[0] * p20[1] - p10[1] * p20[0]; + + float area = sqrtf(normalx * normalx + normaly * normaly + normalz * normalz); + float invarea = (area == 0.f) ? 0.f : 1.f / area; + + triangles[i].px = (p0[0] + p1[0] + p2[0]) / 3.f; + triangles[i].py = (p0[1] + p1[1] + p2[1]) / 3.f; + triangles[i].pz = (p0[2] + p1[2] + p2[2]) / 3.f; + + triangles[i].nx = normalx * invarea; + triangles[i].ny = normaly * invarea; + triangles[i].nz = normalz * invarea; + + mesh_area += area; + } + + return mesh_area; +} + +static void finishMeshlet(meshopt_Meshlet& meshlet, unsigned char* meshlet_triangles) +{ + size_t offset = meshlet.triangle_offset + meshlet.triangle_count * 3; + + // fill 4b padding with 0 + while (offset & 3) + meshlet_triangles[offset++] = 0; +} + +static bool appendMeshlet(meshopt_Meshlet& meshlet, unsigned int a, unsigned int b, unsigned int c, unsigned char* used, meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, size_t meshlet_offset, size_t max_vertices, size_t max_triangles) +{ + unsigned char& av = used[a]; + unsigned char& bv = used[b]; + unsigned char& cv = used[c]; + + bool result = false; + + unsigned int used_extra = (av == 0xff) + (bv == 0xff) + (cv == 0xff); + + if (meshlet.vertex_count + used_extra > max_vertices || meshlet.triangle_count >= max_triangles) + { + meshlets[meshlet_offset] = meshlet; + + for (size_t j = 0; j < meshlet.vertex_count; ++j) + used[meshlet_vertices[meshlet.vertex_offset + j]] = 0xff; + + finishMeshlet(meshlet, meshlet_triangles); + + meshlet.vertex_offset += meshlet.vertex_count; + meshlet.triangle_offset += (meshlet.triangle_count * 3 + 3) & ~3; // 4b padding + meshlet.vertex_count = 0; + meshlet.triangle_count = 0; + + result = true; + } + + if (av == 0xff) + { + av = (unsigned char)meshlet.vertex_count; + meshlet_vertices[meshlet.vertex_offset + meshlet.vertex_count++] = a; + } + + if (bv == 0xff) + { + bv = (unsigned char)meshlet.vertex_count; + meshlet_vertices[meshlet.vertex_offset + meshlet.vertex_count++] = b; + } + + if (cv == 0xff) + { + cv = (unsigned char)meshlet.vertex_count; + meshlet_vertices[meshlet.vertex_offset + meshlet.vertex_count++] = c; + } + + meshlet_triangles[meshlet.triangle_offset + meshlet.triangle_count * 3 + 0] = av; + meshlet_triangles[meshlet.triangle_offset + meshlet.triangle_count * 3 + 1] = bv; + meshlet_triangles[meshlet.triangle_offset + meshlet.triangle_count * 3 + 2] = cv; + meshlet.triangle_count++; + + return result; +} + +struct KDNode +{ + union + { + float split; + unsigned int index; + }; + + // leaves: axis = 3, children = number of extra points after this one (0 if 'index' is the only point) + // branches: axis != 3, left subtree = skip 1, right subtree = skip 1+children + unsigned int axis : 2; + unsigned int children : 30; +}; + +static size_t kdtreePartition(unsigned int* indices, size_t count, const float* points, size_t stride, unsigned int axis, float pivot) +{ + size_t m = 0; + + // invariant: elements in range [0, m) are < pivot, elements in range [m, i) are >= pivot + for (size_t i = 0; i < count; ++i) + { + float v = points[indices[i] * stride + axis]; + + // swap(m, i) unconditionally + unsigned int t = indices[m]; + indices[m] = indices[i]; + indices[i] = t; + + // when v >= pivot, we swap i with m without advancing it, preserving invariants + m += v < pivot; + } + + return m; +} + +static size_t kdtreeBuildLeaf(size_t offset, KDNode* nodes, size_t node_count, unsigned int* indices, size_t count) +{ + assert(offset + count <= node_count); + (void)node_count; + + KDNode& result = nodes[offset]; + + result.index = indices[0]; + result.axis = 3; + result.children = unsigned(count - 1); + + // all remaining points are stored in nodes immediately following the leaf + for (size_t i = 1; i < count; ++i) + { + KDNode& tail = nodes[offset + i]; + + tail.index = indices[i]; + tail.axis = 3; + tail.children = ~0u >> 2; // bogus value to prevent misuse + } + + return offset + count; +} + +static size_t kdtreeBuild(size_t offset, KDNode* nodes, size_t node_count, const float* points, size_t stride, unsigned int* indices, size_t count, size_t leaf_size) +{ + assert(count > 0); + assert(offset < node_count); + + if (count <= leaf_size) + return kdtreeBuildLeaf(offset, nodes, node_count, indices, count); + + float mean[3] = {}; + float vars[3] = {}; + float runc = 1, runs = 1; + + // gather statistics on the points in the subtree using Welford's algorithm + for (size_t i = 0; i < count; ++i, runc += 1.f, runs = 1.f / runc) + { + const float* point = points + indices[i] * stride; + + for (int k = 0; k < 3; ++k) + { + float delta = point[k] - mean[k]; + mean[k] += delta * runs; + vars[k] += delta * (point[k] - mean[k]); + } + } + + // split axis is one where the variance is largest + unsigned int axis = vars[0] >= vars[1] && vars[0] >= vars[2] ? 0 : vars[1] >= vars[2] ? 1 + : 2; + + float split = mean[axis]; + size_t middle = kdtreePartition(indices, count, points, stride, axis, split); + + // when the partition is degenerate simply consolidate the points into a single node + if (middle <= leaf_size / 2 || middle >= count - leaf_size / 2) + return kdtreeBuildLeaf(offset, nodes, node_count, indices, count); + + KDNode& result = nodes[offset]; + + result.split = split; + result.axis = axis; + + // left subtree is right after our node + size_t next_offset = kdtreeBuild(offset + 1, nodes, node_count, points, stride, indices, middle, leaf_size); + + // distance to the right subtree is represented explicitly + result.children = unsigned(next_offset - offset - 1); + + return kdtreeBuild(next_offset, nodes, node_count, points, stride, indices + middle, count - middle, leaf_size); +} + +static void kdtreeNearest(KDNode* nodes, unsigned int root, const float* points, size_t stride, const unsigned char* emitted_flags, const float* position, unsigned int& result, float& limit) +{ + const KDNode& node = nodes[root]; + + if (node.axis == 3) + { + // leaf + for (unsigned int i = 0; i <= node.children; ++i) + { + unsigned int index = nodes[root + i].index; + + if (emitted_flags[index]) + continue; + + const float* point = points + index * stride; + + float distance2 = + (point[0] - position[0]) * (point[0] - position[0]) + + (point[1] - position[1]) * (point[1] - position[1]) + + (point[2] - position[2]) * (point[2] - position[2]); + float distance = sqrtf(distance2); + + if (distance < limit) + { + result = index; + limit = distance; + } + } + } + else + { + // branch; we order recursion to process the node that search position is in first + float delta = position[node.axis] - node.split; + unsigned int first = (delta <= 0) ? 0 : node.children; + unsigned int second = first ^ node.children; + + kdtreeNearest(nodes, root + 1 + first, points, stride, emitted_flags, position, result, limit); + + // only process the other node if it can have a match based on closest distance so far + if (fabsf(delta) <= limit) + kdtreeNearest(nodes, root + 1 + second, points, stride, emitted_flags, position, result, limit); + } +} + } // namespace meshopt size_t meshopt_buildMeshletsBound(size_t index_count, size_t max_vertices, size_t max_triangles) { + using namespace meshopt; + assert(index_count % 3 == 0); - assert(max_vertices >= 3); - assert(max_triangles >= 1); + assert(max_vertices >= 3 && max_vertices <= kMeshletMaxVertices); + assert(max_triangles >= 1 && max_triangles <= kMeshletMaxTriangles); + assert(max_triangles % 4 == 0); // ensures the caller will compute output space properly as index data is 4b aligned + + (void)kMeshletMaxVertices; + (void)kMeshletMaxTriangles; // meshlet construction is limited by max vertices and max triangles per meshlet // the worst case is that the input is an unindexed stream since this equally stresses both limits @@ -100,77 +460,226 @@ size_t meshopt_buildMeshletsBound(size_t index_count, size_t max_vertices, size_ return meshlet_limit_vertices > meshlet_limit_triangles ? meshlet_limit_vertices : meshlet_limit_triangles; } -size_t meshopt_buildMeshlets(meshopt_Meshlet* destination, const unsigned int* indices, size_t index_count, size_t vertex_count, size_t max_vertices, size_t max_triangles) +size_t meshopt_buildMeshlets(meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t max_vertices, size_t max_triangles, float cone_weight) { + using namespace meshopt; + assert(index_count % 3 == 0); - assert(max_vertices >= 3); - assert(max_triangles >= 1); + assert(vertex_positions_stride > 0 && vertex_positions_stride <= 256); + assert(vertex_positions_stride % sizeof(float) == 0); + + assert(max_vertices >= 3 && max_vertices <= kMeshletMaxVertices); + assert(max_triangles >= 1 && max_triangles <= kMeshletMaxTriangles); + assert(max_triangles % 4 == 0); // ensures the caller will compute output space properly as index data is 4b aligned meshopt_Allocator allocator; - meshopt_Meshlet meshlet; - memset(&meshlet, 0, sizeof(meshlet)); + TriangleAdjacency2 adjacency = {}; + buildTriangleAdjacency(adjacency, indices, index_count, vertex_count, allocator); + + unsigned int* live_triangles = allocator.allocate<unsigned int>(vertex_count); + memcpy(live_triangles, adjacency.counts, vertex_count * sizeof(unsigned int)); + + size_t face_count = index_count / 3; + + unsigned char* emitted_flags = allocator.allocate<unsigned char>(face_count); + memset(emitted_flags, 0, face_count); + + // for each triangle, precompute centroid & normal to use for scoring + Cone* triangles = allocator.allocate<Cone>(face_count); + float mesh_area = computeTriangleCones(triangles, indices, index_count, vertex_positions, vertex_count, vertex_positions_stride); + + // assuming each meshlet is a square patch, expected radius is sqrt(expected area) + float triangle_area_avg = face_count == 0 ? 0.f : mesh_area / float(face_count) * 0.5f; + float meshlet_expected_radius = sqrtf(triangle_area_avg * max_triangles) * 0.5f; + + // build a kd-tree for nearest neighbor lookup + unsigned int* kdindices = allocator.allocate<unsigned int>(face_count); + for (size_t i = 0; i < face_count; ++i) + kdindices[i] = unsigned(i); - assert(max_vertices <= sizeof(meshlet.vertices) / sizeof(meshlet.vertices[0])); - assert(max_triangles <= sizeof(meshlet.indices) / 3); + KDNode* nodes = allocator.allocate<KDNode>(face_count * 2); + kdtreeBuild(0, nodes, face_count * 2, &triangles[0].px, sizeof(Cone) / sizeof(float), kdindices, face_count, /* leaf_size= */ 8); // index of the vertex in the meshlet, 0xff if the vertex isn't used unsigned char* used = allocator.allocate<unsigned char>(vertex_count); memset(used, -1, vertex_count); - size_t offset = 0; + meshopt_Meshlet meshlet = {}; + size_t meshlet_offset = 0; - for (size_t i = 0; i < index_count; i += 3) - { - unsigned int a = indices[i + 0], b = indices[i + 1], c = indices[i + 2]; - assert(a < vertex_count && b < vertex_count && c < vertex_count); + Cone meshlet_cone_acc = {}; - unsigned char& av = used[a]; - unsigned char& bv = used[b]; - unsigned char& cv = used[c]; + for (;;) + { + unsigned int best_triangle = ~0u; + unsigned int best_extra = 5; + float best_score = FLT_MAX; - unsigned int used_extra = (av == 0xff) + (bv == 0xff) + (cv == 0xff); + Cone meshlet_cone = getMeshletCone(meshlet_cone_acc, meshlet.triangle_count); - if (meshlet.vertex_count + used_extra > max_vertices || meshlet.triangle_count >= max_triangles) + for (size_t i = 0; i < meshlet.vertex_count; ++i) { - destination[offset++] = meshlet; + unsigned int index = meshlet_vertices[meshlet.vertex_offset + i]; + + unsigned int* neighbours = &adjacency.data[0] + adjacency.offsets[index]; + size_t neighbours_size = adjacency.counts[index]; + + for (size_t j = 0; j < neighbours_size; ++j) + { + unsigned int triangle = neighbours[j]; + assert(!emitted_flags[triangle]); + + unsigned int a = indices[triangle * 3 + 0], b = indices[triangle * 3 + 1], c = indices[triangle * 3 + 2]; + assert(a < vertex_count && b < vertex_count && c < vertex_count); + + unsigned int extra = (used[a] == 0xff) + (used[b] == 0xff) + (used[c] == 0xff); + + // triangles that don't add new vertices to meshlets are max. priority + if (extra != 0) + { + // artificially increase the priority of dangling triangles as they're expensive to add to new meshlets + if (live_triangles[a] == 1 || live_triangles[b] == 1 || live_triangles[c] == 1) + extra = 0; + + extra++; + } + + // since topology-based priority is always more important than the score, we can skip scoring in some cases + if (extra > best_extra) + continue; + + const Cone& tri_cone = triangles[triangle]; + + float distance2 = + (tri_cone.px - meshlet_cone.px) * (tri_cone.px - meshlet_cone.px) + + (tri_cone.py - meshlet_cone.py) * (tri_cone.py - meshlet_cone.py) + + (tri_cone.pz - meshlet_cone.pz) * (tri_cone.pz - meshlet_cone.pz); - for (size_t j = 0; j < meshlet.vertex_count; ++j) - used[meshlet.vertices[j]] = 0xff; + float spread = tri_cone.nx * meshlet_cone.nx + tri_cone.ny * meshlet_cone.ny + tri_cone.nz * meshlet_cone.nz; - memset(&meshlet, 0, sizeof(meshlet)); + float score = getMeshletScore(distance2, spread, cone_weight, meshlet_expected_radius); + + // note that topology-based priority is always more important than the score + // this helps maintain reasonable effectiveness of meshlet data and reduces scoring cost + if (extra < best_extra || score < best_score) + { + best_triangle = triangle; + best_extra = extra; + best_score = score; + } + } } - if (av == 0xff) + if (best_triangle == ~0u) { - av = meshlet.vertex_count; - meshlet.vertices[meshlet.vertex_count++] = a; + float position[3] = {meshlet_cone.px, meshlet_cone.py, meshlet_cone.pz}; + unsigned int index = ~0u; + float limit = FLT_MAX; + + kdtreeNearest(nodes, 0, &triangles[0].px, sizeof(Cone) / sizeof(float), emitted_flags, position, index, limit); + + best_triangle = index; } - if (bv == 0xff) + if (best_triangle == ~0u) + break; + + unsigned int a = indices[best_triangle * 3 + 0], b = indices[best_triangle * 3 + 1], c = indices[best_triangle * 3 + 2]; + assert(a < vertex_count && b < vertex_count && c < vertex_count); + + // add meshlet to the output; when the current meshlet is full we reset the accumulated bounds + if (appendMeshlet(meshlet, a, b, c, used, meshlets, meshlet_vertices, meshlet_triangles, meshlet_offset, max_vertices, max_triangles)) { - bv = meshlet.vertex_count; - meshlet.vertices[meshlet.vertex_count++] = b; + meshlet_offset++; + memset(&meshlet_cone_acc, 0, sizeof(meshlet_cone_acc)); } - if (cv == 0xff) + live_triangles[a]--; + live_triangles[b]--; + live_triangles[c]--; + + // remove emitted triangle from adjacency data + // this makes sure that we spend less time traversing these lists on subsequent iterations + for (size_t k = 0; k < 3; ++k) { - cv = meshlet.vertex_count; - meshlet.vertices[meshlet.vertex_count++] = c; + unsigned int index = indices[best_triangle * 3 + k]; + + unsigned int* neighbours = &adjacency.data[0] + adjacency.offsets[index]; + size_t neighbours_size = adjacency.counts[index]; + + for (size_t i = 0; i < neighbours_size; ++i) + { + unsigned int tri = neighbours[i]; + + if (tri == best_triangle) + { + neighbours[i] = neighbours[neighbours_size - 1]; + adjacency.counts[index]--; + break; + } + } } - meshlet.indices[meshlet.triangle_count][0] = av; - meshlet.indices[meshlet.triangle_count][1] = bv; - meshlet.indices[meshlet.triangle_count][2] = cv; - meshlet.triangle_count++; + // update aggregated meshlet cone data for scoring subsequent triangles + meshlet_cone_acc.px += triangles[best_triangle].px; + meshlet_cone_acc.py += triangles[best_triangle].py; + meshlet_cone_acc.pz += triangles[best_triangle].pz; + meshlet_cone_acc.nx += triangles[best_triangle].nx; + meshlet_cone_acc.ny += triangles[best_triangle].ny; + meshlet_cone_acc.nz += triangles[best_triangle].nz; + + emitted_flags[best_triangle] = 1; + } + + if (meshlet.triangle_count) + { + finishMeshlet(meshlet, meshlet_triangles); + + meshlets[meshlet_offset++] = meshlet; + } + + assert(meshlet_offset <= meshopt_buildMeshletsBound(index_count, max_vertices, max_triangles)); + return meshlet_offset; +} + +size_t meshopt_buildMeshletsScan(meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const unsigned int* indices, size_t index_count, size_t vertex_count, size_t max_vertices, size_t max_triangles) +{ + using namespace meshopt; + + assert(index_count % 3 == 0); + + assert(max_vertices >= 3 && max_vertices <= kMeshletMaxVertices); + assert(max_triangles >= 1 && max_triangles <= kMeshletMaxTriangles); + assert(max_triangles % 4 == 0); // ensures the caller will compute output space properly as index data is 4b aligned + + meshopt_Allocator allocator; + + // index of the vertex in the meshlet, 0xff if the vertex isn't used + unsigned char* used = allocator.allocate<unsigned char>(vertex_count); + memset(used, -1, vertex_count); + + meshopt_Meshlet meshlet = {}; + size_t meshlet_offset = 0; + + for (size_t i = 0; i < index_count; i += 3) + { + unsigned int a = indices[i + 0], b = indices[i + 1], c = indices[i + 2]; + assert(a < vertex_count && b < vertex_count && c < vertex_count); + + // appends triangle to the meshlet and writes previous meshlet to the output if full + meshlet_offset += appendMeshlet(meshlet, a, b, c, used, meshlets, meshlet_vertices, meshlet_triangles, meshlet_offset, max_vertices, max_triangles); } if (meshlet.triangle_count) - destination[offset++] = meshlet; + { + finishMeshlet(meshlet, meshlet_triangles); - assert(offset <= meshopt_buildMeshletsBound(index_count, max_vertices, max_triangles)); + meshlets[meshlet_offset++] = meshlet; + } - return offset; + assert(meshlet_offset <= meshopt_buildMeshletsBound(index_count, max_vertices, max_triangles)); + return meshlet_offset; } meshopt_Bounds meshopt_computeClusterBounds(const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride) @@ -178,18 +687,17 @@ meshopt_Bounds meshopt_computeClusterBounds(const unsigned int* indices, size_t using namespace meshopt; assert(index_count % 3 == 0); + assert(index_count / 3 <= kMeshletMaxTriangles); assert(vertex_positions_stride > 0 && vertex_positions_stride <= 256); assert(vertex_positions_stride % sizeof(float) == 0); - assert(index_count / 3 <= 256); - (void)vertex_count; size_t vertex_stride_float = vertex_positions_stride / sizeof(float); // compute triangle normals and gather triangle corners - float normals[256][3]; - float corners[256][3][3]; + float normals[kMeshletMaxTriangles][3]; + float corners[kMeshletMaxTriangles][3][3]; size_t triangles = 0; for (size_t i = 0; i < index_count; i += 3) @@ -327,25 +835,23 @@ meshopt_Bounds meshopt_computeClusterBounds(const unsigned int* indices, size_t return bounds; } -meshopt_Bounds meshopt_computeMeshletBounds(const meshopt_Meshlet* meshlet, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride) +meshopt_Bounds meshopt_computeMeshletBounds(const unsigned int* meshlet_vertices, const unsigned char* meshlet_triangles, size_t triangle_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride) { + using namespace meshopt; + + assert(triangle_count <= kMeshletMaxTriangles); assert(vertex_positions_stride > 0 && vertex_positions_stride <= 256); assert(vertex_positions_stride % sizeof(float) == 0); - unsigned int indices[sizeof(meshlet->indices) / sizeof(meshlet->indices[0][0])]; + unsigned int indices[kMeshletMaxTriangles * 3]; - for (size_t i = 0; i < meshlet->triangle_count; ++i) + for (size_t i = 0; i < triangle_count * 3; ++i) { - unsigned int a = meshlet->vertices[meshlet->indices[i][0]]; - unsigned int b = meshlet->vertices[meshlet->indices[i][1]]; - unsigned int c = meshlet->vertices[meshlet->indices[i][2]]; - - assert(a < vertex_count && b < vertex_count && c < vertex_count); + unsigned int index = meshlet_vertices[meshlet_triangles[i]]; + assert(index < vertex_count); - indices[i * 3 + 0] = a; - indices[i * 3 + 1] = b; - indices[i * 3 + 2] = c; + indices[i] = index; } - return meshopt_computeClusterBounds(indices, meshlet->triangle_count * 3, vertex_positions, vertex_count, vertex_positions_stride); + return meshopt_computeClusterBounds(indices, triangle_count * 3, vertex_positions, vertex_count, vertex_positions_stride); } diff --git a/thirdparty/meshoptimizer/indexgenerator.cpp b/thirdparty/meshoptimizer/indexgenerator.cpp index aa4a30efa4..f60db0dc4f 100644 --- a/thirdparty/meshoptimizer/indexgenerator.cpp +++ b/thirdparty/meshoptimizer/indexgenerator.cpp @@ -4,6 +4,8 @@ #include <assert.h> #include <string.h> +// This work is based on: +// John McDonald, Mark Kilgard. Crack-Free Point-Normal Triangles using Adjacent Edge Normals. 2010 namespace meshopt { @@ -83,10 +85,49 @@ struct VertexStreamHasher } }; +struct EdgeHasher +{ + const unsigned int* remap; + + size_t hash(unsigned long long edge) const + { + unsigned int e0 = unsigned(edge >> 32); + unsigned int e1 = unsigned(edge); + + unsigned int h1 = remap[e0]; + unsigned int h2 = remap[e1]; + + const unsigned int m = 0x5bd1e995; + + // MurmurHash64B finalizer + h1 ^= h2 >> 18; + h1 *= m; + h2 ^= h1 >> 22; + h2 *= m; + h1 ^= h2 >> 17; + h1 *= m; + h2 ^= h1 >> 19; + h2 *= m; + + return h2; + } + + bool equal(unsigned long long lhs, unsigned long long rhs) const + { + unsigned int l0 = unsigned(lhs >> 32); + unsigned int l1 = unsigned(lhs); + + unsigned int r0 = unsigned(rhs >> 32); + unsigned int r1 = unsigned(rhs); + + return remap[l0] == remap[r0] && remap[l1] == remap[r1]; + } +}; + static size_t hashBuckets(size_t count) { size_t buckets = 1; - while (buckets < count) + while (buckets < count + count / 4) buckets *= 2; return buckets; @@ -119,6 +160,26 @@ static T* hashLookup(T* table, size_t buckets, const Hash& hash, const T& key, c return 0; } +static void buildPositionRemap(unsigned int* remap, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, meshopt_Allocator& allocator) +{ + VertexHasher vertex_hasher = {reinterpret_cast<const unsigned char*>(vertex_positions), 3 * sizeof(float), vertex_positions_stride}; + + size_t vertex_table_size = hashBuckets(vertex_count); + unsigned int* vertex_table = allocator.allocate<unsigned int>(vertex_table_size); + memset(vertex_table, -1, vertex_table_size * sizeof(unsigned int)); + + for (size_t i = 0; i < vertex_count; ++i) + { + unsigned int index = unsigned(i); + unsigned int* entry = hashLookup(vertex_table, vertex_table_size, vertex_hasher, index, ~0u); + + if (*entry == ~0u) + *entry = index; + + remap[index] = *entry; + } +} + } // namespace meshopt size_t meshopt_generateVertexRemap(unsigned int* destination, const unsigned int* indices, size_t index_count, const void* vertices, size_t vertex_count, size_t vertex_size) @@ -345,3 +406,146 @@ void meshopt_generateShadowIndexBufferMulti(unsigned int* destination, const uns destination[i] = remap[index]; } } + +void meshopt_generateAdjacencyIndexBuffer(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride) +{ + using namespace meshopt; + + assert(index_count % 3 == 0); + assert(vertex_positions_stride > 0 && vertex_positions_stride <= 256); + assert(vertex_positions_stride % sizeof(float) == 0); + + meshopt_Allocator allocator; + + static const int next[4] = {1, 2, 0, 1}; + + // build position remap: for each vertex, which other (canonical) vertex does it map to? + unsigned int* remap = allocator.allocate<unsigned int>(vertex_count); + buildPositionRemap(remap, vertex_positions, vertex_count, vertex_positions_stride, allocator); + + // build edge set; this stores all triangle edges but we can look these up by any other wedge + EdgeHasher edge_hasher = {remap}; + + size_t edge_table_size = hashBuckets(index_count); + unsigned long long* edge_table = allocator.allocate<unsigned long long>(edge_table_size); + unsigned int* edge_vertex_table = allocator.allocate<unsigned int>(edge_table_size); + + memset(edge_table, -1, edge_table_size * sizeof(unsigned long long)); + memset(edge_vertex_table, -1, edge_table_size * sizeof(unsigned int)); + + for (size_t i = 0; i < index_count; i += 3) + { + for (int e = 0; e < 3; ++e) + { + unsigned int i0 = indices[i + e]; + unsigned int i1 = indices[i + next[e]]; + unsigned int i2 = indices[i + next[e + 1]]; + assert(i0 < vertex_count && i1 < vertex_count && i2 < vertex_count); + + unsigned long long edge = ((unsigned long long)i0 << 32) | i1; + unsigned long long* entry = hashLookup(edge_table, edge_table_size, edge_hasher, edge, ~0ull); + + if (*entry == ~0ull) + { + *entry = edge; + + // store vertex opposite to the edge + edge_vertex_table[entry - edge_table] = i2; + } + } + } + + // build resulting index buffer: 6 indices for each input triangle + for (size_t i = 0; i < index_count; i += 3) + { + unsigned int patch[6]; + + for (int e = 0; e < 3; ++e) + { + unsigned int i0 = indices[i + e]; + unsigned int i1 = indices[i + next[e]]; + assert(i0 < vertex_count && i1 < vertex_count); + + // note: this refers to the opposite edge! + unsigned long long edge = ((unsigned long long)i1 << 32) | i0; + unsigned long long* oppe = hashLookup(edge_table, edge_table_size, edge_hasher, edge, ~0ull); + + patch[e * 2 + 0] = i0; + patch[e * 2 + 1] = (*oppe == ~0ull) ? i0 : edge_vertex_table[oppe - edge_table]; + } + + memcpy(destination + i * 2, patch, sizeof(patch)); + } +} + +void meshopt_generateTessellationIndexBuffer(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride) +{ + using namespace meshopt; + + assert(index_count % 3 == 0); + assert(vertex_positions_stride > 0 && vertex_positions_stride <= 256); + assert(vertex_positions_stride % sizeof(float) == 0); + + meshopt_Allocator allocator; + + static const int next[3] = {1, 2, 0}; + + // build position remap: for each vertex, which other (canonical) vertex does it map to? + unsigned int* remap = allocator.allocate<unsigned int>(vertex_count); + buildPositionRemap(remap, vertex_positions, vertex_count, vertex_positions_stride, allocator); + + // build edge set; this stores all triangle edges but we can look these up by any other wedge + EdgeHasher edge_hasher = {remap}; + + size_t edge_table_size = hashBuckets(index_count); + unsigned long long* edge_table = allocator.allocate<unsigned long long>(edge_table_size); + memset(edge_table, -1, edge_table_size * sizeof(unsigned long long)); + + for (size_t i = 0; i < index_count; i += 3) + { + for (int e = 0; e < 3; ++e) + { + unsigned int i0 = indices[i + e]; + unsigned int i1 = indices[i + next[e]]; + assert(i0 < vertex_count && i1 < vertex_count); + + unsigned long long edge = ((unsigned long long)i0 << 32) | i1; + unsigned long long* entry = hashLookup(edge_table, edge_table_size, edge_hasher, edge, ~0ull); + + if (*entry == ~0ull) + *entry = edge; + } + } + + // build resulting index buffer: 12 indices for each input triangle + for (size_t i = 0; i < index_count; i += 3) + { + unsigned int patch[12]; + + for (int e = 0; e < 3; ++e) + { + unsigned int i0 = indices[i + e]; + unsigned int i1 = indices[i + next[e]]; + assert(i0 < vertex_count && i1 < vertex_count); + + // note: this refers to the opposite edge! + unsigned long long edge = ((unsigned long long)i1 << 32) | i0; + unsigned long long oppe = *hashLookup(edge_table, edge_table_size, edge_hasher, edge, ~0ull); + + // use the same edge if opposite edge doesn't exist (border) + oppe = (oppe == ~0ull) ? edge : oppe; + + // triangle index (0, 1, 2) + patch[e] = i0; + + // opposite edge (3, 4; 5, 6; 7, 8) + patch[3 + e * 2 + 0] = unsigned(oppe); + patch[3 + e * 2 + 1] = unsigned(oppe >> 32); + + // dominant vertex (9, 10, 11) + patch[9 + e] = remap[i0]; + } + + memcpy(destination + i * 4, patch, sizeof(patch)); + } +} diff --git a/thirdparty/meshoptimizer/meshoptimizer.h b/thirdparty/meshoptimizer/meshoptimizer.h index 1714000384..fe8d349731 100644 --- a/thirdparty/meshoptimizer/meshoptimizer.h +++ b/thirdparty/meshoptimizer/meshoptimizer.h @@ -1,7 +1,7 @@ /** - * meshoptimizer - version 0.15 + * meshoptimizer - version 0.16 * - * Copyright (C) 2016-2020, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) + * Copyright (C) 2016-2021, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) * Report bugs and download new versions at https://github.com/zeux/meshoptimizer * * This library is distributed under the MIT License. See notice at the end of this file. @@ -12,7 +12,7 @@ #include <stddef.h> /* Version macro; major * 1000 + minor * 10 + patch */ -#define MESHOPTIMIZER_VERSION 150 /* 0.15 */ +#define MESHOPTIMIZER_VERSION 160 /* 0.16 */ /* If no API is defined, assume default */ #ifndef MESHOPTIMIZER_API @@ -98,6 +98,35 @@ MESHOPTIMIZER_API void meshopt_generateShadowIndexBuffer(unsigned int* destinati MESHOPTIMIZER_API void meshopt_generateShadowIndexBufferMulti(unsigned int* destination, const unsigned int* indices, size_t index_count, size_t vertex_count, const struct meshopt_Stream* streams, size_t stream_count); /** + * Generate index buffer that can be used as a geometry shader input with triangle adjacency topology + * Each triangle is converted into a 6-vertex patch with the following layout: + * - 0, 2, 4: original triangle vertices + * - 1, 3, 5: vertices adjacent to edges 02, 24 and 40 + * The resulting patch can be rendered with geometry shaders using e.g. VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY. + * This can be used to implement algorithms like silhouette detection/expansion and other forms of GS-driven rendering. + * + * destination must contain enough space for the resulting index buffer (index_count*2 elements) + * vertex_positions should have float3 position in the first 12 bytes of each vertex - similar to glVertexPointer + */ +MESHOPTIMIZER_EXPERIMENTAL void meshopt_generateAdjacencyIndexBuffer(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride); + +/** + * Generate index buffer that can be used for PN-AEN tessellation with crack-free displacement + * Each triangle is converted into a 12-vertex patch with the following layout: + * - 0, 1, 2: original triangle vertices + * - 3, 4: opposing edge for edge 0, 1 + * - 5, 6: opposing edge for edge 1, 2 + * - 7, 8: opposing edge for edge 2, 0 + * - 9, 10, 11: dominant vertices for corners 0, 1, 2 + * The resulting patch can be rendered with hardware tessellation using PN-AEN and displacement mapping. + * See "Tessellation on Any Budget" (John McDonald, GDC 2011) for implementation details. + * + * destination must contain enough space for the resulting index buffer (index_count*4 elements) + * vertex_positions should have float3 position in the first 12 bytes of each vertex - similar to glVertexPointer + */ +MESHOPTIMIZER_EXPERIMENTAL void meshopt_generateTessellationIndexBuffer(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride); + +/** * Vertex transform cache optimizer * Reorders indices to reduce the number of GPU vertex shader invocations * If index buffer contains multiple ranges for multiple draw calls, this functions needs to be called on each range individually. @@ -373,22 +402,31 @@ MESHOPTIMIZER_API struct meshopt_VertexFetchStatistics meshopt_analyzeVertexFetc struct meshopt_Meshlet { - unsigned int vertices[64]; - unsigned char indices[126][3]; - unsigned char triangle_count; - unsigned char vertex_count; + /* offsets within meshlet_vertices and meshlet_triangles arrays with meshlet data */ + unsigned int vertex_offset; + unsigned int triangle_offset; + + /* number of vertices and triangles used in the meshlet; data is stored in consecutive range defined by offset and count */ + unsigned int vertex_count; + unsigned int triangle_count; }; /** * Experimental: Meshlet builder * Splits the mesh into a set of meshlets where each meshlet has a micro index buffer indexing into meshlet vertices that refer to the original vertex buffer * The resulting data can be used to render meshes using NVidia programmable mesh shading pipeline, or in other cluster-based renderers. - * For maximum efficiency the index buffer being converted has to be optimized for vertex cache first. + * When using buildMeshlets, vertex positions need to be provided to minimize the size of the resulting clusters. + * When using buildMeshletsScan, for maximum efficiency the index buffer being converted has to be optimized for vertex cache first. * - * destination must contain enough space for all meshlets, worst case size can be computed with meshopt_buildMeshletsBound - * max_vertices and max_triangles can't exceed limits statically declared in meshopt_Meshlet (max_vertices <= 64, max_triangles <= 126) + * meshlets must contain enough space for all meshlets, worst case size can be computed with meshopt_buildMeshletsBound + * meshlet_vertices must contain enough space for all meshlets, worst case size is equal to max_meshlets * max_vertices + * meshlet_triangles must contain enough space for all meshlets, worst case size is equal to max_meshlets * max_triangles * 3 + * vertex_positions should have float3 position in the first 12 bytes of each vertex - similar to glVertexPointer + * max_vertices and max_triangles must not exceed implementation limits (max_vertices <= 255 - not 256!, max_triangles <= 512) + * cone_weight should be set to 0 when cone culling is not used, and a value between 0 and 1 otherwise to balance between cluster size and cone culling efficiency */ -MESHOPTIMIZER_EXPERIMENTAL size_t meshopt_buildMeshlets(struct meshopt_Meshlet* destination, const unsigned int* indices, size_t index_count, size_t vertex_count, size_t max_vertices, size_t max_triangles); +MESHOPTIMIZER_EXPERIMENTAL size_t meshopt_buildMeshlets(struct meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t max_vertices, size_t max_triangles, float cone_weight); +MESHOPTIMIZER_EXPERIMENTAL size_t meshopt_buildMeshletsScan(struct meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const unsigned int* indices, size_t index_count, size_t vertex_count, size_t max_vertices, size_t max_triangles); MESHOPTIMIZER_EXPERIMENTAL size_t meshopt_buildMeshletsBound(size_t index_count, size_t max_vertices, size_t max_triangles); struct meshopt_Bounds @@ -426,10 +464,10 @@ struct meshopt_Bounds * to do frustum/occlusion culling, the formula that doesn't use the apex may be preferable. * * vertex_positions should have float3 position in the first 12 bytes of each vertex - similar to glVertexPointer - * index_count should be less than or equal to 256*3 (the function assumes clusters of limited size) + * index_count/3 should be less than or equal to 512 (the function assumes clusters of limited size) */ MESHOPTIMIZER_EXPERIMENTAL struct meshopt_Bounds meshopt_computeClusterBounds(const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride); -MESHOPTIMIZER_EXPERIMENTAL struct meshopt_Bounds meshopt_computeMeshletBounds(const struct meshopt_Meshlet* meshlet, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride); +MESHOPTIMIZER_EXPERIMENTAL struct meshopt_Bounds meshopt_computeMeshletBounds(const unsigned int* meshlet_vertices, const unsigned char* meshlet_triangles, size_t triangle_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride); /** * Experimental: Spatial sorter @@ -513,6 +551,10 @@ inline void meshopt_generateShadowIndexBuffer(T* destination, const T* indices, template <typename T> inline void meshopt_generateShadowIndexBufferMulti(T* destination, const T* indices, size_t index_count, size_t vertex_count, const meshopt_Stream* streams, size_t stream_count); template <typename T> +inline void meshopt_generateAdjacencyIndexBuffer(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride); +template <typename T> +inline void meshopt_generateTessellationIndexBuffer(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride); +template <typename T> inline void meshopt_optimizeVertexCache(T* destination, const T* indices, size_t index_count, size_t vertex_count); template <typename T> inline void meshopt_optimizeVertexCacheStrip(T* destination, const T* indices, size_t index_count, size_t vertex_count); @@ -547,7 +589,9 @@ inline meshopt_OverdrawStatistics meshopt_analyzeOverdraw(const T* indices, size template <typename T> inline meshopt_VertexFetchStatistics meshopt_analyzeVertexFetch(const T* indices, size_t index_count, size_t vertex_count, size_t vertex_size); template <typename T> -inline size_t meshopt_buildMeshlets(meshopt_Meshlet* destination, const T* indices, size_t index_count, size_t vertex_count, size_t max_vertices, size_t max_triangles); +inline size_t meshopt_buildMeshlets(meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t max_vertices, size_t max_triangles, float cone_weight); +template <typename T> +inline size_t meshopt_buildMeshletsScan(meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const T* indices, size_t index_count, size_t vertex_count, size_t max_vertices, size_t max_triangles); template <typename T> inline meshopt_Bounds meshopt_computeClusterBounds(const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride); template <typename T> @@ -762,6 +806,24 @@ inline void meshopt_generateShadowIndexBufferMulti(T* destination, const T* indi } template <typename T> +inline void meshopt_generateAdjacencyIndexBuffer(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride) +{ + meshopt_IndexAdapter<T> in(0, indices, index_count); + meshopt_IndexAdapter<T> out(destination, 0, index_count * 2); + + meshopt_generateAdjacencyIndexBuffer(out.data, in.data, index_count, vertex_positions, vertex_count, vertex_positions_stride); +} + +template <typename T> +inline void meshopt_generateTessellationIndexBuffer(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride) +{ + meshopt_IndexAdapter<T> in(0, indices, index_count); + meshopt_IndexAdapter<T> out(destination, 0, index_count * 4); + + meshopt_generateTessellationIndexBuffer(out.data, in.data, index_count, vertex_positions, vertex_count, vertex_positions_stride); +} + +template <typename T> inline void meshopt_optimizeVertexCache(T* destination, const T* indices, size_t index_count, size_t vertex_count) { meshopt_IndexAdapter<T> in(0, indices, index_count); @@ -908,11 +970,19 @@ inline meshopt_VertexFetchStatistics meshopt_analyzeVertexFetch(const T* indices } template <typename T> -inline size_t meshopt_buildMeshlets(meshopt_Meshlet* destination, const T* indices, size_t index_count, size_t vertex_count, size_t max_vertices, size_t max_triangles) +inline size_t meshopt_buildMeshlets(meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t max_vertices, size_t max_triangles, float cone_weight) +{ + meshopt_IndexAdapter<T> in(0, indices, index_count); + + return meshopt_buildMeshlets(meshlets, meshlet_vertices, meshlet_triangles, in.data, index_count, vertex_positions, vertex_count, vertex_positions_stride, max_vertices, max_triangles, cone_weight); +} + +template <typename T> +inline size_t meshopt_buildMeshletsScan(meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const T* indices, size_t index_count, size_t vertex_count, size_t max_vertices, size_t max_triangles) { meshopt_IndexAdapter<T> in(0, indices, index_count); - return meshopt_buildMeshlets(destination, in.data, index_count, vertex_count, max_vertices, max_triangles); + return meshopt_buildMeshletsScan(meshlets, meshlet_vertices, meshlet_triangles, in.data, index_count, vertex_count, max_vertices, max_triangles); } template <typename T> @@ -934,7 +1004,7 @@ inline void meshopt_spatialSortTriangles(T* destination, const T* indices, size_ #endif /** - * Copyright (c) 2016-2020 Arseny Kapoulkine + * Copyright (c) 2016-2021 Arseny Kapoulkine * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation diff --git a/thirdparty/meshoptimizer/simplifier.cpp b/thirdparty/meshoptimizer/simplifier.cpp index 942db14461..b2cb589462 100644 --- a/thirdparty/meshoptimizer/simplifier.cpp +++ b/thirdparty/meshoptimizer/simplifier.cpp @@ -131,7 +131,7 @@ struct PositionHasher static size_t hashBuckets2(size_t count) { size_t buckets = 1; - while (buckets < count) + while (buckets < count + count / 4) buckets *= 2; return buckets; diff --git a/thirdparty/meshoptimizer/vertexcodec.cpp b/thirdparty/meshoptimizer/vertexcodec.cpp index 2cbfaac367..5f3ec204ab 100644 --- a/thirdparty/meshoptimizer/vertexcodec.cpp +++ b/thirdparty/meshoptimizer/vertexcodec.cpp @@ -710,18 +710,12 @@ static v128_t decodeShuffleMask(unsigned char mask0, unsigned char mask1) SIMD_TARGET static void wasmMoveMask(v128_t mask, unsigned char& mask0, unsigned char& mask1) { - v128_t mask_0 = wasm_v32x4_shuffle(mask, mask, 0, 2, 1, 3); - - uint64_t mask_1a = wasm_i64x2_extract_lane(mask_0, 0) & 0x0804020108040201ull; - uint64_t mask_1b = wasm_i64x2_extract_lane(mask_0, 1) & 0x8040201080402010ull; + // magic constant found using z3 SMT assuming mask has 8 groups of 0xff or 0x00 + const uint64_t magic = 0x000103070f1f3f80ull; // TODO: This can use v8x16_bitmask in the future - uint64_t mask_2 = mask_1a | mask_1b; - uint64_t mask_4 = mask_2 | (mask_2 >> 16); - uint64_t mask_8 = mask_4 | (mask_4 >> 8); - - mask0 = uint8_t(mask_8); - mask1 = uint8_t(mask_8 >> 32); + mask0 = uint8_t((wasm_i64x2_extract_lane(mask, 0) * magic) >> 56); + mask1 = uint8_t((wasm_i64x2_extract_lane(mask, 1) * magic) >> 56); } SIMD_TARGET |