diff --git a/doc/classes/MeshLibrary.xml b/doc/classes/MeshLibrary.xml
index 0f7de0eb6a..a05a3358b5 100644
--- a/doc/classes/MeshLibrary.xml
+++ b/doc/classes/MeshLibrary.xml
@@ -32,6 +32,13 @@
Returns the first item with the given name, or [code]-1[/code] if no item is found.
+
+
+
+
+ Returns the category for a specific item [param id]. See also [method set_item_category].
+
+
@@ -121,6 +128,14 @@
Removes the item.
+
+
+
+
+
+ Sets the [param category] for a specific item [param id] for organization in the [GridMap] editor. A category can include [code]/[/code] as a delimiter to create sub categories.
+
+
diff --git a/editor/scene/3d/mesh_library_editor_plugin.cpp b/editor/scene/3d/mesh_library_editor_plugin.cpp
index c50408d570..853ca24e85 100644
--- a/editor/scene/3d/mesh_library_editor_plugin.cpp
+++ b/editor/scene/3d/mesh_library_editor_plugin.cpp
@@ -64,6 +64,8 @@ bool MeshLibraryEditor::MeshLibraryItem::_set(const StringName &p_name, const Va
if (p_name == "name") {
mesh_library->set_item_name(mesh_id, p_value);
+ } else if (p_name == "category") {
+ mesh_library->set_item_category(mesh_id, p_value);
} else if (p_name == "mesh") {
mesh_library->set_item_mesh(mesh_id, p_value);
} else if (p_name == "mesh_transform") {
@@ -112,6 +114,8 @@ bool MeshLibraryEditor::MeshLibraryItem::_get(const StringName &p_name, Variant
if (p_name == "name") {
r_ret = mesh_library->get_item_name(mesh_id);
+ } else if (p_name == "category") {
+ r_ret = mesh_library->get_item_category(mesh_id);
} else if (p_name == "mesh") {
r_ret = mesh_library->get_item_mesh(mesh_id);
} else if (p_name == "mesh_transform") {
@@ -143,6 +147,7 @@ void MeshLibraryEditor::MeshLibraryItem::_get_property_list(List *
}
p_list->push_back(PropertyInfo(Variant::STRING, PNAME("name")));
+ p_list->push_back(PropertyInfo(Variant::STRING, PNAME("category")));
p_list->push_back(PropertyInfo(Variant::OBJECT, PNAME("mesh"), PROPERTY_HINT_RESOURCE_TYPE, Mesh::get_class_static()));
p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, PNAME("mesh_transform"), PROPERTY_HINT_NONE, "suffix:m"));
p_list->push_back(PropertyInfo(Variant::INT, PNAME("mesh_cast_shadow"), PROPERTY_HINT_ENUM, "Off,On,Double-Sided,Shadows Only"));
@@ -739,6 +744,7 @@ MeshLibraryEditor::MeshLibraryEditor() {
item_split->add_child(inspector);
inspector->add_custom_property_description("MeshLibraryItem", "name", TTRC("The item's name, shown in the editor. It can also be used to look up the item later using [method MeshLibrary.find_item_by_name]."));
+ inspector->add_custom_property_description("MeshLibraryItem", "category", TTRC("The item's category, used to organize meshes in the [GridMap] editor. A category can include [code]/[/code] as a delimiter to create sub categories."));
inspector->add_custom_property_description("MeshLibraryItem", "mesh", TTRC("The item's mesh. Used by other parts of the engine (e.g. [GridMap], which displays them in a 3D tile)."));
inspector->add_custom_property_description("MeshLibraryItem", "mesh_transform", TTRC("The transform to apply to the item's mesh."));
inspector->add_custom_property_description("MeshLibraryItem", "mesh_cast_shadow", TTRC("The shadow casting mode used by the item's mesh. See [enum RenderingServer.ShadowCastingSetting]."));
diff --git a/modules/gridmap/editor/grid_map_editor_plugin.cpp b/modules/gridmap/editor/grid_map_editor_plugin.cpp
index b5a337ef7a..1e9cc1b241 100644
--- a/modules/gridmap/editor/grid_map_editor_plugin.cpp
+++ b/modules/gridmap/editor/grid_map_editor_plugin.cpp
@@ -57,6 +57,9 @@
#include "scene/gui/menu_button.h"
#include "scene/gui/separator.h"
#include "scene/gui/spin_box.h"
+#include "scene/gui/split_container.h"
+#include "scene/gui/tree.h"
+#include "scene/main/node.h"
#include "scene/main/scene_tree.h"
#include "scene/main/window.h"
#include "servers/rendering/rendering_server.h"
@@ -1099,55 +1102,195 @@ void GridMapEditor::update_palette() {
search_box->set_editable(true);
info_message->hide();
- Vector ids;
- ids = mesh_library->get_item_list();
+ TreeItem *ti_selected = categories->get_selected();
- List<_CGMEItemSort> il;
- for (int i = 0; i < ids.size(); i++) {
- _CGMEItemSort is;
- is.id = ids[i];
- is.name = mesh_library->get_item_name(ids[i]);
- il.push_back(is);
+ Vector item_ids;
+ if (ti_selected) {
+ item_ids = ti_selected->get_metadata(0);
+ } else {
+ item_ids = mesh_library->get_item_list();
}
- il.sort();
- String filter = search_box->get_text().strip_edges();
+ const String searched_string = search_box->get_text().strip_edges().to_lower();
- int item = 0;
+ // Sort alphabetically by item name.
+ List<_CGMEItemSort> items_sorted;
+ for (int item_id : item_ids) {
+ _CGMEItemSort is;
+ is.id = item_id;
+ is.name = mesh_library->get_item_name(item_id);
+ items_sorted.push_back(is);
+ }
+ items_sorted.sort();
- for (_CGMEItemSort &E : il) {
- int id = E.id;
- String name = mesh_library->get_item_name(id);
- Ref preview = mesh_library->get_item_preview(id);
+ for (_CGMEItemSort &E : items_sorted) {
+ int item_id = E.id;
- if (name.is_empty()) {
- name = "#" + itos(id);
- }
+ String item_name = mesh_library->get_item_name(item_id);
+ const StringName item_category = mesh_library->get_item_category(item_id);
- if (!filter.is_empty() && !filter.is_subsequence_ofn(name)) {
- continue;
- }
+ if (!searched_string.is_empty()) {
+ bool item_matches_search_string = false;
- mesh_library_palette->add_item("");
- if (preview.is_valid()) {
- mesh_library_palette->set_item_icon(item, preview);
- mesh_library_palette->set_item_tooltip(item, name);
- } else {
- Ref mesh = mesh_library->get_item_mesh(id);
- if (mesh.is_valid()) {
- // Fallback to the item's mesh preview.
- EditorResourcePreview::get_singleton()->queue_edited_resource_preview(mesh, callable_mp(this, &GridMapEditor::_update_resource_preview).bind(item));
+ if (item_name.to_lower().contains(searched_string)) {
+ item_matches_search_string = true;
+ }
+ if (!item_matches_search_string && String(item_category).contains(searched_string)) {
+ item_matches_search_string = true;
+ }
+
+ if (!item_matches_search_string) {
+ continue;
}
}
- mesh_library_palette->set_item_text(item, name);
- mesh_library_palette->set_item_metadata(item, id);
+ const int list_id = mesh_library_palette->add_item("");
- if (selected_palette == id) {
- mesh_library_palette->select(item);
+ if (item_name.is_empty()) {
+ item_name = "#" + itos(item_id);
+ mesh_library_palette->set_item_tooltip(list_id, item_name);
+ }
+ mesh_library_palette->set_item_text(list_id, item_name);
+
+ const Ref &preview = mesh_library->get_item_preview(item_id);
+ if (preview.is_valid()) {
+ mesh_library_palette->set_item_icon(list_id, preview);
+ } else {
+ Ref mesh = mesh_library->get_item_mesh(item_id);
+ if (mesh.is_valid()) {
+ // Fallback to the item's mesh preview.
+ EditorResourcePreview::get_singleton()->queue_edited_resource_preview(mesh, callable_mp(this, &GridMapEditor::_update_resource_preview).bind(list_id));
+ }
}
- item++;
+ mesh_library_palette->set_item_text(list_id, item_name);
+ mesh_library_palette->set_item_metadata(list_id, item_id);
+ }
+}
+
+void GridMapEditor::_on_categories_item_activated() {
+ TreeItem *ti_selected = categories->get_selected();
+ if (ti_selected) {
+ bool collapsed = ti_selected->is_collapsed();
+ ti_selected->set_collapsed(!collapsed);
+ }
+}
+
+void GridMapEditor::_rebuild_categories() {
+ ERR_FAIL_NULL(categories);
+
+ categories->clear();
+
+ if (mesh_library.is_null()) {
+ categories->hide();
+ item_palette_mc->set_theme_type_variation("NoBorderBottomPanel");
+ mesh_library_palette->set_scroll_hint_mode(ItemList::SCROLL_HINT_MODE_BOTH);
+ mesh_library_palette->set_theme_type_variation("");
+
+ return;
+ }
+
+ const Vector &item_ids = mesh_library->get_item_list();
+
+ categories->create_item();
+ TreeItem *tree_root = categories->get_root();
+ tree_root->set_text(0, TTRC("All"));
+ tree_root->set_text(1, vformat("(%d)", item_ids.size()));
+ tree_root->set_metadata(0, item_ids);
+
+ LocalVector root_categories;
+ AHashMap> category_to_items;
+ AHashMap> category_to_category_children;
+
+ for (const int item_id : item_ids) {
+ const StringName item_category = mesh_library->get_item_category(item_id);
+ Vector item_categories = String(item_category).split("/", false);
+
+ String item_category_path;
+ for (int i = 0; i < item_categories.size(); i++) {
+ const String category = item_categories[i];
+
+ if (i == 0 && !root_categories.has(category)) {
+ root_categories.push_back(category);
+ }
+
+ String parent_item_category_path = item_category_path;
+
+ item_category_path = item_category_path.path_join(category);
+
+ {
+ AHashMap>::Iterator existing = category_to_items.find(item_category_path);
+ if (!existing) {
+ existing = category_to_items.insert(item_category_path, HashSet());
+ }
+ existing->value.insert(item_id);
+ }
+
+ {
+ AHashMap>::Iterator existing = category_to_category_children.find(parent_item_category_path);
+ if (!existing) {
+ existing = category_to_category_children.insert(parent_item_category_path, HashSet());
+ }
+ existing->value.insert(item_category_path);
+ }
+ {
+ AHashMap>::Iterator existing = category_to_category_children.find(item_category_path);
+ if (!existing) {
+ category_to_category_children.insert(item_category_path, HashSet());
+ }
+ }
+ }
+ }
+
+ ItemCategoryMapping item_mapping;
+ item_mapping.category_to_category_children = category_to_category_children;
+ item_mapping.category_to_items = category_to_items;
+
+ for (const StringName &root_category : root_categories) {
+ _add_child_categories_recursive(
+ categories,
+ tree_root,
+ root_category,
+ item_mapping);
+ }
+
+ bool has_categories = !root_categories.is_empty();
+ categories->set_visible(has_categories); // Only show the category tree if there are categories present.
+ item_palette_mc->set_theme_type_variation(has_categories ? "" : "NoBorderBottomPanel");
+ mesh_library_palette->set_scroll_hint_mode(has_categories ? ItemList::SCROLL_HINT_MODE_DISABLED : ItemList::SCROLL_HINT_MODE_BOTH);
+ mesh_library_palette->set_theme_type_variation(has_categories ? "ItemListSecondary" : "");
+
+ update_palette();
+}
+
+void GridMapEditor::_add_child_categories_recursive(Tree *p_item_tree, TreeItem *p_ti_parent, const StringName &p_category, const GridMapEditor::ItemCategoryMapping &p_mapping) {
+ int category_user_count = 0;
+ Vector category_items_meta;
+
+ const AHashMap>::ConstIterator category_items_kv = p_mapping.category_to_items.find(p_category);
+ if (category_items_kv) {
+ const HashSet &category_items = category_items_kv->value;
+ category_user_count = category_items.size();
+
+ for (int item_id : category_items) {
+ category_items_meta.push_back(item_id);
+ }
+ }
+
+ Vector subcategories = String(p_category).split("/", false);
+ TreeItem *ti_category = p_item_tree->create_item(p_ti_parent);
+ ti_category->set_collapsed(true);
+ ti_category->set_text(0, vformat("%s", subcategories[subcategories.size() - 1]));
+ ti_category->set_metadata(0, category_items_meta);
+ ti_category->set_text(1, vformat("(%d)", category_user_count));
+
+ const AHashMap>::ConstIterator category_to_category_children_kv = p_mapping.category_to_category_children.find(p_category);
+ if (category_to_category_children_kv) {
+ const HashSet &category_children = category_to_category_children_kv->value;
+
+ for (const StringName &category_child : category_children) {
+ _add_child_categories_recursive(p_item_tree, ti_category, category_child, p_mapping);
+ }
}
}
@@ -1164,6 +1307,7 @@ void GridMapEditor::_update_mesh_library() {
if (new_mesh_library != mesh_library) {
if (mesh_library.is_valid()) {
mesh_library->disconnect_changed(callable_mp(this, &GridMapEditor::update_palette));
+ mesh_library->disconnect_changed(callable_mp(this, &GridMapEditor::_rebuild_categories));
}
mesh_library = new_mesh_library;
} else {
@@ -1173,6 +1317,7 @@ void GridMapEditor::_update_mesh_library() {
MeshLibraryEditorPlugin *mesh_library_editor_plugin = MeshLibraryEditorPlugin::get_singleton();
if (mesh_library.is_valid()) {
mesh_library->connect_changed(callable_mp(this, &GridMapEditor::update_palette));
+ mesh_library->connect(CoreStringName(changed), callable_mp(this, &GridMapEditor::_rebuild_categories));
if (mesh_library_editor_plugin) {
mesh_library_editor_plugin->edit(*mesh_library);
@@ -1198,8 +1343,10 @@ void GridMapEditor::edit(GridMap *p_gridmap) {
if (node) {
node->disconnect(SNAME("cell_size_changed"), callable_mp(this, &GridMapEditor::_draw_grids));
node->disconnect(CoreStringName(changed), callable_mp(this, &GridMapEditor::_update_mesh_library));
+ node->disconnect(CoreStringName(changed), callable_mp(this, &GridMapEditor::_rebuild_categories));
if (mesh_library.is_valid()) {
mesh_library->disconnect_changed(callable_mp(this, &GridMapEditor::update_palette));
+ mesh_library->disconnect_changed(callable_mp(this, &GridMapEditor::_rebuild_categories));
mesh_library = Ref();
}
}
@@ -1238,7 +1385,10 @@ void GridMapEditor::edit(GridMap *p_gridmap) {
node->connect(SNAME("cell_size_changed"), callable_mp(this, &GridMapEditor::_draw_grids));
node->connect(CoreStringName(changed), callable_mp(this, &GridMapEditor::_update_mesh_library));
+ node->connect(CoreStringName(changed), callable_mp(this, &GridMapEditor::_rebuild_categories));
+
_update_mesh_library();
+ _rebuild_categories();
}
void GridMapEditor::update_grid() {
@@ -1379,6 +1529,11 @@ void GridMapEditor::_notification(int p_what) {
vformat(TTR("Change Grid Floor:\nPrevious Plane (%s)\nNext Plane (%s)"),
ED_GET_SHORTCUT("grid_map/previous_floor")->get_as_text(),
ED_GET_SHORTCUT("grid_map/next_floor")->get_as_text()));
+
+ TreeItem *tree_root = categories->get_root();
+ if (tree_root) {
+ tree_root->set_text(0, TTRC("All"));
+ }
} break;
case NOTIFICATION_APPLICATION_FOCUS_OUT: {
@@ -1493,7 +1648,6 @@ GridMapEditor::GridMapEditor() {
set_global(false);
set_transient(true);
set_custom_minimum_size(Size2(0, 200 * EDSCALE));
- set_theme_type_variation("NoBorderBottomPanel");
ED_SHORTCUT("grid_map/previous_floor", TTRC("Previous Floor"), Key::KEY_1, true);
ED_SHORTCUT("grid_map/next_floor", TTRC("Next Floor"), Key::KEY_3, true);
@@ -1503,25 +1657,11 @@ GridMapEditor::GridMapEditor() {
ED_SHORTCUT("grid_map/keep_selected", TTRC("Keep Selection"));
ED_SHORTCUT("grid_map/clear_rotation", TTRC("Clear Rotation"), KeyModifierMask::ALT | Key::G, true);
- options = memnew(MenuButton);
- options->set_theme_type_variation(SceneStringName(FlatButton));
- options->get_popup()->add_radio_check_shortcut(ED_GET_SHORTCUT("grid_map/edit_x_axis"), MENU_OPTION_X_AXIS);
- options->get_popup()->add_radio_check_shortcut(ED_GET_SHORTCUT("grid_map/edit_y_axis"), MENU_OPTION_Y_AXIS);
- options->get_popup()->add_radio_check_shortcut(ED_GET_SHORTCUT("grid_map/edit_z_axis"), MENU_OPTION_Z_AXIS);
- options->get_popup()->set_item_checked(options->get_popup()->get_item_index(MENU_OPTION_Y_AXIS), true);
- options->get_popup()->add_separator();
- // TRANSLATORS: This is a toggle to select after pasting the new content.
- options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/clear_rotation"), MENU_OPTION_CURSOR_CLEAR_ROTATION);
- options->get_popup()->add_check_shortcut(ED_GET_SHORTCUT("grid_map/keep_selected"), MENU_OPTION_PASTE_SELECTS);
- options->get_popup()->set_item_checked(options->get_popup()->get_item_index(MENU_OPTION_PASTE_SELECTS), true);
- options->get_popup()->add_separator();
- options->get_popup()->add_item(TTRC("Settings..."), MENU_OPTION_GRIDMAP_SETTINGS);
-
settings_dialog = memnew(ConfirmationDialog);
settings_dialog->set_title(TTRC("GridMap Settings"));
add_child(settings_dialog);
settings_vbc = memnew(VBoxContainer);
- settings_vbc->set_custom_minimum_size(Size2(200, 0) * EDSCALE);
+ settings_vbc->set_custom_minimum_size(Size2(200 * EDSCALE, 0));
settings_dialog->add_child(settings_vbc);
settings_pick_distance = memnew(SpinBox);
@@ -1532,8 +1672,6 @@ GridMapEditor::GridMapEditor() {
settings_pick_distance->set_accessibility_name(TTRC("Pick Distance:"));
settings_vbc->add_margin_child(TTRC("Pick Distance:"), settings_pick_distance);
- options->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &GridMapEditor::_menu_option));
-
VBoxContainer *main_vb = memnew(VBoxContainer);
add_child(main_vb);
@@ -1742,14 +1880,52 @@ GridMapEditor::GridMapEditor() {
toolbar->add_child(mode_list);
mode_list->connect(SceneStringName(pressed), callable_mp(this, &GridMapEditor::_set_display_mode).bind(DISPLAY_LIST));
+ options = memnew(MenuButton);
+ options->set_theme_type_variation(SceneStringName(FlatButton));
+ options->get_popup()->add_radio_check_shortcut(ED_GET_SHORTCUT("grid_map/edit_x_axis"), MENU_OPTION_X_AXIS);
+ options->get_popup()->add_radio_check_shortcut(ED_GET_SHORTCUT("grid_map/edit_y_axis"), MENU_OPTION_Y_AXIS);
+ options->get_popup()->add_radio_check_shortcut(ED_GET_SHORTCUT("grid_map/edit_z_axis"), MENU_OPTION_Z_AXIS);
+ options->get_popup()->set_item_checked(options->get_popup()->get_item_index(MENU_OPTION_Y_AXIS), true);
+ options->get_popup()->add_separator();
+ // TRANSLATORS: This is a toggle to select after pasting the new content.
+ options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/clear_rotation"), MENU_OPTION_CURSOR_CLEAR_ROTATION);
+ options->get_popup()->add_check_shortcut(ED_GET_SHORTCUT("grid_map/keep_selected"), MENU_OPTION_PASTE_SELECTS);
+ options->get_popup()->set_item_checked(options->get_popup()->get_item_index(MENU_OPTION_PASTE_SELECTS), true);
+ options->get_popup()->add_separator();
+ options->get_popup()->add_item(TTRC("Settings..."), MENU_OPTION_GRIDMAP_SETTINGS);
+ options->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &GridMapEditor::_menu_option));
toolbar->add_child(options);
+ HSplitContainer *hsplit = memnew(HSplitContainer);
+ hsplit->set_split_offset(256 * EDSCALE);
+ hsplit->set_h_size_flags(SIZE_EXPAND_FILL);
+ hsplit->set_v_size_flags(SIZE_EXPAND_FILL);
+ main_vb->add_child(hsplit);
+
+ categories = memnew(Tree);
+ categories->set_select_mode(Tree::SelectMode::SELECT_ROW);
+ categories->set_columns(2);
+ categories->set_column_expand(0, true);
+ categories->set_column_expand(1, false);
+ categories->set_column_custom_minimum_width(1, 64 * EDSCALE);
+ categories->set_hide_root(false);
+ categories->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
+ categories->set_theme_type_variation("TreeSecondary");
+ categories->hide();
+ hsplit->add_child(categories);
+ categories->connect(SNAME("item_activated"), callable_mp(this, &GridMapEditor::_on_categories_item_activated));
+ categories->connect(SceneStringName(item_selected), callable_mp(this, &GridMapEditor::update_palette));
+
+ item_palette_mc = memnew(MarginContainer);
+ item_palette_mc->set_theme_type_variation("NoBorderHorizontal");
+ hsplit->add_child(item_palette_mc);
+
mesh_library_palette = memnew(ItemList);
- search_box->set_forward_control(mesh_library_palette);
mesh_library_palette->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
mesh_library_palette->set_scroll_hint_mode(ItemList::SCROLL_HINT_MODE_BOTH);
- mesh_library_palette->set_v_size_flags(SIZE_EXPAND_FILL);
- main_vb->add_child(mesh_library_palette);
+ mesh_library_palette->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
+ item_palette_mc->add_child(mesh_library_palette);
+ search_box->set_forward_control(mesh_library_palette);
mesh_library_palette->connect(SceneStringName(gui_input), callable_mp(this, &GridMapEditor::_mesh_library_palette_input));
mesh_library_palette->connect(SceneStringName(item_selected), callable_mp(this, &GridMapEditor::_item_selected_cbk));
diff --git a/modules/gridmap/editor/grid_map_editor_plugin.h b/modules/gridmap/editor/grid_map_editor_plugin.h
index d04aeca070..2f54b17fd8 100644
--- a/modules/gridmap/editor/grid_map_editor_plugin.h
+++ b/modules/gridmap/editor/grid_map_editor_plugin.h
@@ -47,6 +47,8 @@ class ButtonGroup;
class EditorZoomWidget;
class BaseButton;
class SpinBox;
+class Tree;
+class TreeItem;
class GridMapEditor : public EditorDock {
GDCLASS(GridMapEditor, EditorDock);
@@ -216,6 +218,8 @@ class GridMapEditor : public EditorDock {
RID instance;
};
+ Tree *categories = nullptr;
+ MarginContainer *item_palette_mc = nullptr;
ItemList *mesh_library_palette = nullptr;
Label *info_message = nullptr;
@@ -265,6 +269,16 @@ class GridMapEditor : public EditorDock {
friend class GridMapEditorPlugin;
+ struct ItemCategoryMapping {
+ AHashMap> category_to_category_children;
+ AHashMap> category_to_items;
+ };
+
+ void _on_categories_item_activated();
+
+ void _rebuild_categories();
+ void _add_child_categories_recursive(Tree *p_categories, TreeItem *p_ti_parent, const StringName &p_category, const ItemCategoryMapping &p_mapping);
+
protected:
void _notification(int p_what);
static void _bind_methods();
diff --git a/scene/resources/3d/mesh_library.cpp b/scene/resources/3d/mesh_library.cpp
index 2076b91896..edd042b008 100644
--- a/scene/resources/3d/mesh_library.cpp
+++ b/scene/resources/3d/mesh_library.cpp
@@ -40,6 +40,7 @@
bool MeshLibrary::_set(const StringName &p_name, const Variant &p_value) {
String prop_name = p_name;
+
if (prop_name.begins_with("item/")) {
int idx = prop_name.get_slicec('/', 1).to_int();
String what = prop_name.get_slicec('/', 2);
@@ -49,6 +50,8 @@ bool MeshLibrary::_set(const StringName &p_name, const Variant &p_value) {
if (what == "name") {
set_item_name(idx, p_value);
+ } else if (what == "category") {
+ set_item_category(idx, p_value);
} else if (what == "mesh") {
set_item_mesh(idx, p_value);
} else if (what == "mesh_transform") {
@@ -98,45 +101,52 @@ bool MeshLibrary::_set(const StringName &p_name, const Variant &p_value) {
} else {
return false;
}
-
- return true;
+ } else {
+ return false;
}
- return false;
+ return true;
}
bool MeshLibrary::_get(const StringName &p_name, Variant &r_ret) const {
String prop_name = p_name;
- int idx = prop_name.get_slicec('/', 1).to_int();
- ERR_FAIL_COND_V(!item_map.has(idx), false);
- String what = prop_name.get_slicec('/', 2);
- if (what == "name") {
- r_ret = get_item_name(idx);
- } else if (what == "mesh") {
- r_ret = get_item_mesh(idx);
- } else if (what == "mesh_transform") {
- r_ret = get_item_mesh_transform(idx);
- } else if (what == "mesh_cast_shadow") {
- r_ret = (int)get_item_mesh_cast_shadow(idx);
+ if (prop_name.begins_with("item/")) {
+ int idx = prop_name.get_slicec('/', 1).to_int();
+ ERR_FAIL_COND_V(!item_map.has(idx), false);
+ String what = prop_name.get_slicec('/', 2);
+
+ if (what == "name") {
+ r_ret = get_item_name(idx);
+ } else if (what == "category") {
+ r_ret = get_item_category(idx);
+ } else if (what == "mesh") {
+ r_ret = get_item_mesh(idx);
+ } else if (what == "mesh_transform") {
+ r_ret = get_item_mesh_transform(idx);
+ } else if (what == "mesh_cast_shadow") {
+ r_ret = (int)get_item_mesh_cast_shadow(idx);
#ifndef PHYSICS_3D_DISABLED
- } else if (what == "shapes") {
- r_ret = _get_item_shapes(idx);
+ } else if (what == "shapes") {
+ r_ret = _get_item_shapes(idx);
#endif // PHYSICS_3D_DISABLED
- } else if (what == "navigation_mesh") {
- r_ret = get_item_navigation_mesh(idx);
- } else if (what == "navigation_mesh_transform") {
- r_ret = get_item_navigation_mesh_transform(idx);
+ } else if (what == "navigation_mesh") {
+ r_ret = get_item_navigation_mesh(idx);
+ } else if (what == "navigation_mesh_transform") {
+ r_ret = get_item_navigation_mesh_transform(idx);
#ifndef DISABLE_DEPRECATED
- } else if (what == "navmesh") { // Renamed in 4.0 beta 9.
- r_ret = get_item_navigation_mesh(idx);
- } else if (what == "navmesh_transform") { // Renamed in 4.0 beta 9.
- r_ret = get_item_navigation_mesh_transform(idx);
+ } else if (what == "navmesh") { // Renamed in 4.0 beta 9.
+ r_ret = get_item_navigation_mesh(idx);
+ } else if (what == "navmesh_transform") { // Renamed in 4.0 beta 9.
+ r_ret = get_item_navigation_mesh_transform(idx);
#endif // DISABLE_DEPRECATED
- } else if (what == "navigation_layers") {
- r_ret = get_item_navigation_layers(idx);
- } else if (what == "preview") {
- r_ret = get_item_preview(idx);
+ } else if (what == "navigation_layers") {
+ r_ret = get_item_navigation_layers(idx);
+ } else if (what == "preview") {
+ r_ret = get_item_preview(idx);
+ } else {
+ return false;
+ }
} else {
return false;
}
@@ -148,6 +158,7 @@ void MeshLibrary::_get_property_list(List *p_list) const {
for (const KeyValue &E : item_map) {
String prop_name = vformat("%s/%d/", PNAME("item"), E.key);
p_list->push_back(PropertyInfo(Variant::STRING, prop_name + PNAME("name"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::STRING_NAME, prop_name + PNAME("category"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
p_list->push_back(PropertyInfo(Variant::OBJECT, prop_name + PNAME("mesh"), PROPERTY_HINT_RESOURCE_TYPE, Mesh::get_class_static(), PROPERTY_USAGE_NO_EDITOR));
p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prop_name + PNAME("mesh_transform"), PROPERTY_HINT_NONE, "suffix:m", PROPERTY_USAGE_NO_EDITOR));
p_list->push_back(PropertyInfo(Variant::INT, prop_name + PNAME("mesh_cast_shadow"), PROPERTY_HINT_ENUM, "Off,On,Double-Sided,Shadows Only", PROPERTY_USAGE_NO_EDITOR));
@@ -173,6 +184,12 @@ void MeshLibrary::set_item_name(int p_item, const String &p_name) {
emit_changed();
}
+void MeshLibrary::set_item_category(int p_item, const StringName &p_category) {
+ ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
+ item_map[p_item].category = p_category;
+ emit_changed();
+}
+
void MeshLibrary::set_item_mesh(int p_item, const Ref &p_mesh) {
ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
item_map[p_item].mesh = p_mesh;
@@ -229,6 +246,11 @@ String MeshLibrary::get_item_name(int p_item) const {
return item_map[p_item].name;
}
+StringName MeshLibrary::get_item_category(int p_item) const {
+ ERR_FAIL_COND_V_MSG(!item_map.has(p_item), StringName(), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
+ return item_map[p_item].category;
+}
+
Ref MeshLibrary::get_item_mesh(int p_item) const {
ERR_FAIL_COND_V_MSG(!item_map.has(p_item), Ref(), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
return item_map[p_item].mesh;
@@ -371,9 +393,11 @@ Array MeshLibrary::_get_item_shapes(int p_item) const {
void MeshLibrary::reset_state() {
clear();
}
+
void MeshLibrary::_bind_methods() {
ClassDB::bind_method(D_METHOD("create_item", "id"), &MeshLibrary::create_item);
ClassDB::bind_method(D_METHOD("set_item_name", "id", "name"), &MeshLibrary::set_item_name);
+ ClassDB::bind_method(D_METHOD("set_item_category", "id", "category"), &MeshLibrary::set_item_category);
ClassDB::bind_method(D_METHOD("set_item_mesh", "id", "mesh"), &MeshLibrary::set_item_mesh);
ClassDB::bind_method(D_METHOD("set_item_mesh_transform", "id", "mesh_transform"), &MeshLibrary::set_item_mesh_transform);
ClassDB::bind_method(D_METHOD("set_item_mesh_cast_shadow", "id", "shadow_casting_setting"), &MeshLibrary::set_item_mesh_cast_shadow);
@@ -385,6 +409,7 @@ void MeshLibrary::_bind_methods() {
#endif // PHYSICS_3D_DISABLED
ClassDB::bind_method(D_METHOD("set_item_preview", "id", "texture"), &MeshLibrary::set_item_preview);
ClassDB::bind_method(D_METHOD("get_item_name", "id"), &MeshLibrary::get_item_name);
+ ClassDB::bind_method(D_METHOD("get_item_category", "id"), &MeshLibrary::get_item_category);
ClassDB::bind_method(D_METHOD("get_item_mesh", "id"), &MeshLibrary::get_item_mesh);
ClassDB::bind_method(D_METHOD("get_item_mesh_transform", "id"), &MeshLibrary::get_item_mesh_transform);
ClassDB::bind_method(D_METHOD("get_item_mesh_cast_shadow", "id"), &MeshLibrary::get_item_mesh_cast_shadow);
@@ -403,9 +428,3 @@ void MeshLibrary::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_item_count"), &MeshLibrary::get_item_count);
ClassDB::bind_method(D_METHOD("get_last_unused_item_id"), &MeshLibrary::get_last_unused_item_id);
}
-
-MeshLibrary::MeshLibrary() {
-}
-
-MeshLibrary::~MeshLibrary() {
-}
diff --git a/scene/resources/3d/mesh_library.h b/scene/resources/3d/mesh_library.h
index 0c5055134a..d322246447 100644
--- a/scene/resources/3d/mesh_library.h
+++ b/scene/resources/3d/mesh_library.h
@@ -51,8 +51,10 @@ public:
Transform3D local_transform;
};
#endif // PHYSICS_3D_DISABLED
+
struct Item {
String name;
+ StringName category;
Ref mesh;
Transform3D mesh_transform;
RSE::ShadowCastingSetting mesh_cast_shadow = RSE::SHADOW_CASTING_SETTING_ON;
@@ -83,6 +85,7 @@ protected:
public:
void create_item(int p_item);
void set_item_name(int p_item, const String &p_name);
+ void set_item_category(int p_item, const StringName &p_category);
void set_item_mesh(int p_item, const Ref &p_mesh);
void set_item_mesh_transform(int p_item, const Transform3D &p_transform);
void set_item_mesh_cast_shadow(int p_item, RSE::ShadowCastingSetting p_shadow_casting_setting);
@@ -94,6 +97,7 @@ public:
#endif // PHYSICS_3D_DISABLED
void set_item_preview(int p_item, const Ref &p_preview);
String get_item_name(int p_item) const;
+ StringName get_item_category(int p_item) const;
Ref get_item_mesh(int p_item) const;
Transform3D get_item_mesh_transform(int p_item) const;
RSE::ShadowCastingSetting get_item_mesh_cast_shadow(int p_item) const;
@@ -115,7 +119,4 @@ public:
Vector get_item_list() const;
int get_item_count() const { return item_map.size(); }
int get_last_unused_item_id() const;
-
- MeshLibrary();
- ~MeshLibrary();
};