Add categories to MeshLibrary

Co-authored-by: smix8 <52464204+smix8@users.noreply.github.com>
This commit is contained in:
Michael Alexsander
2026-05-07 16:01:33 -03:00
parent 720caf476d
commit ebcbd78fbf
6 changed files with 324 additions and 93 deletions
+15
View File
@@ -32,6 +32,13 @@
Returns the first item with the given name, or [code]-1[/code] if no item is found.
</description>
</method>
<method name="get_item_category" qualifiers="const">
<return type="StringName" />
<param index="0" name="id" type="int" />
<description>
Returns the category for a specific item [param id]. See also [method set_item_category].
</description>
</method>
<method name="get_item_count" qualifiers="const">
<return type="int" />
<description>
@@ -121,6 +128,14 @@
Removes the item.
</description>
</method>
<method name="set_item_category">
<return type="void" />
<param index="0" name="id" type="int" />
<param index="1" name="category" type="StringName" />
<description>
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.
</description>
</method>
<method name="set_item_mesh">
<return type="void" />
<param index="0" name="id" type="int" />
@@ -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<PropertyInfo> *
}
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]"));
+232 -56
View File
@@ -56,6 +56,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"
@@ -1106,55 +1109,195 @@ void GridMapEditor::update_palette() {
search_box->set_editable(true);
info_message->hide();
Vector<int> 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<int> 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<Texture2D> 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 = 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<Texture2D> &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 = 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<int> &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<StringName> root_categories;
AHashMap<StringName, HashSet<int>> category_to_items;
AHashMap<StringName, HashSet<StringName>> category_to_category_children;
for (const int item_id : item_ids) {
const StringName item_category = mesh_library->get_item_category(item_id);
Vector<String> 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<StringName, HashSet<int>>::Iterator existing = category_to_items.find(item_category_path);
if (!existing) {
existing = category_to_items.insert(item_category_path, HashSet<int>());
}
existing->value.insert(item_id);
}
{
AHashMap<StringName, HashSet<StringName>>::Iterator existing = category_to_category_children.find(parent_item_category_path);
if (!existing) {
existing = category_to_category_children.insert(parent_item_category_path, HashSet<StringName>());
}
existing->value.insert(item_category_path);
}
{
AHashMap<StringName, HashSet<StringName>>::Iterator existing = category_to_category_children.find(item_category_path);
if (!existing) {
category_to_category_children.insert(item_category_path, HashSet<StringName>());
}
}
}
}
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<int> category_items_meta;
const AHashMap<StringName, HashSet<int>>::ConstIterator category_items_kv = p_mapping.category_to_items.find(p_category);
if (category_items_kv) {
const HashSet<int> &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<String> 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<StringName, HashSet<StringName>>::ConstIterator category_to_category_children_kv = p_mapping.category_to_category_children.find(p_category);
if (category_to_category_children_kv) {
const HashSet<StringName> &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);
}
}
}
@@ -1171,6 +1314,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 {
@@ -1180,6 +1324,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);
@@ -1205,8 +1350,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<MeshLibrary>();
}
}
@@ -1245,7 +1392,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() {
@@ -1387,6 +1537,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: {
@@ -1502,7 +1657,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);
@@ -1512,25 +1666,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);
@@ -1541,8 +1681,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);
@@ -1751,14 +1889,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));
@@ -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;
@@ -266,6 +270,16 @@ class GridMapEditor : public EditorDock {
friend class GridMapEditorPlugin;
struct ItemCategoryMapping {
AHashMap<StringName, HashSet<StringName>> category_to_category_children;
AHashMap<StringName, HashSet<int>> 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();
+53 -34
View File
@@ -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<PropertyInfo> *p_list) const {
for (const KeyValue<int, Item> &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<Mesh> &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<Mesh> MeshLibrary::get_item_mesh(int p_item) const {
ERR_FAIL_COND_V_MSG(!item_map.has(p_item), Ref<Mesh>(), "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() {
}
+4 -3
View File
@@ -51,8 +51,10 @@ public:
Transform3D local_transform;
};
#endif // PHYSICS_3D_DISABLED
struct Item {
String name;
StringName category;
Ref<Mesh> 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<Mesh> &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<Texture2D> &p_preview);
String get_item_name(int p_item) const;
StringName get_item_category(int p_item) const;
Ref<Mesh> 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<int> get_item_list() const;
int get_item_count() const { return item_map.size(); }
int get_last_unused_item_id() const;
MeshLibrary();
~MeshLibrary();
};