diff --git a/source/blender/blenkernel/BKE_instances.hh b/source/blender/blenkernel/BKE_instances.hh index 1dc4a242b9d..2c63c0d022a 100644 --- a/source/blender/blenkernel/BKE_instances.hh +++ b/source/blender/blenkernel/BKE_instances.hh @@ -115,6 +115,11 @@ class Instances { CustomData attributes_; + /** + * Caches how often each reference is used. + */ + mutable SharedCache> reference_user_counts_; + /* These almost unique ids are generated based on the `id` attribute, which might not contain * unique ids at all. They are *almost* unique, because under certain very unlikely * circumstances, they are not unique. Code using these ids should not crash when they are not @@ -186,6 +191,11 @@ class Instances { */ Span almost_unique_ids() const; + /** + * Get cached user counts for every reference. + */ + Span reference_user_counts() const; + bke::AttributeAccessor attributes() const; bke::MutableAttributeAccessor attributes_for_write(); @@ -200,6 +210,7 @@ class Instances { void tag_reference_handles_changed() { + reference_user_counts_.tag_dirty(); almost_unique_ids_cache_.tag_dirty(); } }; diff --git a/source/blender/blenkernel/intern/instances.cc b/source/blender/blenkernel/intern/instances.cc index 162b8b8fd06..7f1155190f1 100644 --- a/source/blender/blenkernel/intern/instances.cc +++ b/source/blender/blenkernel/intern/instances.cc @@ -128,6 +128,7 @@ Instances::Instances(Instances &&other) : references_(std::move(other.references_)), instances_num_(other.instances_num_), attributes_(other.attributes_), + reference_user_counts_(std::move(other.reference_user_counts_)), almost_unique_ids_cache_(std::move(other.almost_unique_ids_cache_)) { CustomData_reset(&other.attributes_); @@ -136,6 +137,7 @@ Instances::Instances(Instances &&other) Instances::Instances(const Instances &other) : references_(other.references_), instances_num_(other.instances_num_), + reference_user_counts_(std::move(other.reference_user_counts_)), almost_unique_ids_cache_(other.almost_unique_ids_cache_) { CustomData_copy(&other.attributes_, &attributes_, CD_MASK_ALL, other.instances_num_); @@ -443,6 +445,23 @@ static Array generate_unique_instance_ids(Span original_ids) return unique_ids; } +Span Instances::reference_user_counts() const +{ + reference_user_counts_.ensure([&](Array &r_data) { + const int references_num = references_.size(); + r_data.reinitialize(references_num); + r_data.fill(0); + + const Span handles = this->reference_handles(); + for (const int handle : handles) { + if (handle >= 0 && handle < references_num) { + r_data[handle]++; + } + } + }); + return reference_user_counts_.data(); +} + Span Instances::almost_unique_ids() const { almost_unique_ids_cache_.ensure([&](Array &r_data) { diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh index c6900f676e1..2846dd9afa3 100644 --- a/source/blender/editors/include/UI_tree_view.hh +++ b/source/blender/editors/include/UI_tree_view.hh @@ -93,6 +93,7 @@ class TreeViewItemContainer { protected: void foreach_item_recursive(ItemIterFn iter_fn, IterOptions options = IterOptions::None) const; + void foreach_parent(ItemIterFn iter_fn) const; }; ENUM_OPERATORS(TreeViewItemContainer::IterOptions, @@ -395,7 +396,8 @@ class TreeViewBuilder { public: static void build_tree_view(AbstractTreeView &tree_view, uiLayout &layout, - std::optional search_string = {}); + std::optional search_string = {}, + bool add_box = true); private: static void ensure_min_rows_items(AbstractTreeView &tree_view); diff --git a/source/blender/editors/interface/views/tree_view.cc b/source/blender/editors/interface/views/tree_view.cc index 597fca3c793..f2186d1423f 100644 --- a/source/blender/editors/interface/views/tree_view.cc +++ b/source/blender/editors/interface/views/tree_view.cc @@ -83,6 +83,13 @@ void TreeViewItemContainer::foreach_item_recursive(ItemIterFn iter_fn, IterOptio } } +void TreeViewItemContainer::foreach_parent(ItemIterFn iter_fn) const +{ + for (ui::AbstractTreeViewItem *item = parent_; item; item = item->parent_) { + iter_fn(*item); + } +} + /* ---------------------------------------------------------------------- */ /* Implementation for the base class virtual function. More specialized iterators below. */ @@ -620,6 +627,7 @@ bool AbstractTreeViewItem::matches(const AbstractViewItem &other) const class TreeViewLayoutBuilder { uiBlock &block_; + bool add_box_ = true; friend TreeViewBuilder; @@ -643,8 +651,13 @@ void TreeViewLayoutBuilder::build_from_tree(const AbstractTreeView &tree_view) { uiLayout &parent_layout = this->current_layout(); - uiLayout *box = uiLayoutBox(&parent_layout); - uiLayoutColumn(box, true); + if (add_box_) { + uiLayout *box = uiLayoutBox(&parent_layout); + uiLayoutColumn(box, true); + } + else { + uiLayoutColumn(&parent_layout, true); + } tree_view.foreach_item([this](AbstractTreeViewItem &item) { build_row(item); }, AbstractTreeView::IterOptions::SkipCollapsed | @@ -727,7 +740,8 @@ void TreeViewBuilder::ensure_min_rows_items(AbstractTreeView &tree_view) void TreeViewBuilder::build_tree_view(AbstractTreeView &tree_view, uiLayout &layout, - std::optional search_string) + std::optional search_string, + const bool add_box) { uiBlock &block = *uiLayoutGetBlock(&layout); @@ -742,6 +756,7 @@ void TreeViewBuilder::build_tree_view(AbstractTreeView &tree_view, UI_block_layout_set_current(&block, &layout); TreeViewLayoutBuilder builder(layout); + builder.add_box_ = add_box; builder.build_from_tree(tree_view); } diff --git a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc index 64f69d9b95f..6f250619946 100644 --- a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc +++ b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc @@ -107,6 +107,7 @@ static void spreadsheet_free(SpaceLink *sl) LISTBASE_FOREACH_MUTABLE (SpreadsheetColumn *, column, &sspreadsheet->columns) { spreadsheet_column_free(column); } + MEM_SAFE_FREE(sspreadsheet->instance_ids); BKE_viewer_path_clear(&sspreadsheet->viewer_path); } @@ -141,6 +142,8 @@ static SpaceLink *spreadsheet_duplicate(SpaceLink *sl) BLI_addtail(&sspreadsheet_new->columns, new_column); } + sspreadsheet_new->instance_ids = static_cast( + MEM_dupallocN(sspreadsheet_old->instance_ids)); BKE_viewer_path_copy(&sspreadsheet_new->viewer_path, &sspreadsheet_old->viewer_path); return (SpaceLink *)sspreadsheet_new; @@ -688,6 +691,9 @@ static void spreadsheet_blend_read_data(BlendDataReader *reader, SpaceLink *sl) BLO_read_string(reader, &column->display_name); } + BLO_read_struct_array( + reader, SpreadsheetInstanceID, sspreadsheet->instance_ids_num, &sspreadsheet->instance_ids); + BKE_viewer_path_blend_read_data(reader, &sspreadsheet->viewer_path); } @@ -711,6 +717,8 @@ static void spreadsheet_blend_write(BlendWriter *writer, SpaceLink *sl) BLO_write_string(writer, column->display_name); } + BLO_write_struct_array( + writer, SpreadsheetInstanceID, sspreadsheet->instance_ids_num, sspreadsheet->instance_ids); BKE_viewer_path_blend_write(writer, &sspreadsheet->viewer_path); } diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc index 46368cddbae..2f44fa80cf7 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc @@ -12,6 +12,7 @@ #include "BKE_editmesh.hh" #include "BKE_geometry_fields.hh" #include "BKE_geometry_set.hh" +#include "BKE_geometry_set_instances.hh" #include "BKE_global.hh" #include "BKE_grease_pencil.hh" #include "BKE_instances.hh" @@ -560,7 +561,7 @@ int get_instance_reference_icon(const bke::InstanceReference &reference) return ICON_OUTLINER_COLLECTION; } case bke::InstanceReference::Type::GeometrySet: { - return ICON_EMPTY_AXIS; + return ICON_GEOMETRY_SET; } case bke::InstanceReference::Type::None: { break; @@ -632,13 +633,41 @@ bke::GeometrySet spreadsheet_get_display_geometry_set(const SpaceSpreadsheet *ss return geometry_set; } +bke::GeometrySet get_geometry_set_for_instance_ids(const bke::GeometrySet &root_geometry, + const Span instance_ids) +{ + bke::GeometrySet geometry = root_geometry; + for (const SpreadsheetInstanceID &instance_id : instance_ids) { + const bke::Instances *instances = geometry.get_instances(); + if (!instances) { + /* Return the best available geometry. */ + return geometry; + } + const Span references = instances->references(); + if (instance_id.reference_index < 0 || instance_id.reference_index >= references.size()) { + /* Return the best available geometry. */ + return geometry; + } + const bke::InstanceReference &reference = references[instance_id.reference_index]; + bke::GeometrySet reference_geometry; + reference.to_geometry_set(reference_geometry); + geometry = reference_geometry; + } + return geometry; +} + std::unique_ptr data_source_from_geometry(const bContext *C, Object *object_eval) { SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C); + + const bke::GeometrySet root_geometry_set = spreadsheet_get_display_geometry_set(sspreadsheet, + object_eval); + const bke::GeometrySet geometry_set = get_geometry_set_for_instance_ids( + root_geometry_set, Span{sspreadsheet->instance_ids, sspreadsheet->instance_ids_num}); + const bke::AttrDomain domain = (bke::AttrDomain)sspreadsheet->attribute_domain; const auto component_type = bke::GeometryComponent::Type(sspreadsheet->geometry_component_type); const int active_layer_index = sspreadsheet->active_layer_index; - bke::GeometrySet geometry_set = spreadsheet_get_display_geometry_set(sspreadsheet, object_eval); if (!geometry_set.has(component_type)) { return {}; } @@ -646,7 +675,9 @@ std::unique_ptr data_source_from_geometry(const bContext *C, Object if (component_type == bke::GeometryComponent::Type::Volume) { return std::make_unique(std::move(geometry_set)); } - Object *object_orig = DEG_get_original_object(object_eval); + Object *object_orig = sspreadsheet->instance_ids_num == 0 ? + DEG_get_original_object(object_eval) : + nullptr; return std::make_unique( object_orig, std::move(geometry_set), component_type, domain, active_layer_index); } diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh index 61470d996da..17ce9d2230d 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh @@ -113,4 +113,7 @@ int get_instance_reference_icon(const bke::InstanceReference &reference); std::unique_ptr data_source_from_geometry(const bContext *C, Object *object_eval); +bke::GeometrySet get_geometry_set_for_instance_ids(const bke::GeometrySet &root_geometry, + const Span instance_ids); + } // namespace blender::ed::spreadsheet diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc index b9221c0d4f3..a1b711868e3 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc @@ -2,6 +2,8 @@ * * SPDX-License-Identifier: GPL-2.0-or-later */ +#include + #include "BLI_string.h" #include "DNA_collection_types.h" @@ -24,6 +26,7 @@ #include "UI_interface.hh" #include "UI_tree_view.hh" +#include "WM_api.hh" #include "WM_types.hh" #include "BLT_translation.hh" @@ -37,6 +40,7 @@ namespace blender::ed::spreadsheet { class GeometryDataSetTreeView; +class GeometryInstancesTreeView; struct GeometryDataIdentifier { bke::GeometryComponent::Type component_type; @@ -44,13 +48,18 @@ struct GeometryDataIdentifier { std::optional domain; }; -static void draw_count(ui::AbstractTreeViewItem &view_item, const int count) +static void draw_row_suffix(ui::AbstractTreeViewItem &view_item, const StringRefNull str) { /* Using the tree row button instead of a separate right aligned button gives padding * to the right side of the number, which it didn't have with the button. */ + UI_but_hint_drawstr_set(reinterpret_cast(view_item.view_item_button()), str.c_str()); +} + +static void draw_count(ui::AbstractTreeViewItem &view_item, const int count) +{ char element_count[BLI_STR_FORMAT_INT32_DECIMAL_UNIT_SIZE]; BLI_str_format_decimal_unit(element_count, count); - UI_but_hint_drawstr_set(reinterpret_cast(view_item.view_item_button()), element_count); + draw_row_suffix(view_item, element_count); } static StringRefNull mesh_domain_to_label(const bke::AttrDomain domain) @@ -113,6 +122,102 @@ static BIFIconID curves_domain_to_icon(const bke::AttrDomain domain) } } +class InstancesTreeViewItem : public ui::AbstractTreeViewItem { + public: + GeometryInstancesTreeView &get_tree() const; + + void get_parent_instance_ids(Vector &r_instance_ids) const; + + void on_activate(bContext &C) override; + + std::optional should_be_active() const override; +}; + +class RootGeometryViewItem : public InstancesTreeViewItem { + public: + RootGeometryViewItem(const bke::GeometrySet &geometry) + { + label_ = geometry.name.empty() ? IFACE_("Geometry") : geometry.name; + } + + void build_row(uiLayout &row) override + { + uiItemL(&row, label_.c_str(), ICON_GEOMETRY_SET); + } +}; + +class InstanceReferenceViewItem : public InstancesTreeViewItem { + private: + const bke::InstanceReference &reference_; + int reference_index_; + int user_count_; + + public: + InstanceReferenceViewItem(const bke::Instances &instances, const int reference_index) + : reference_(instances.references()[reference_index]), reference_index_(reference_index) + { + label_ = std::to_string(reference_index); + user_count_ = instances.reference_user_counts()[reference_index]; + } + + void build_row(uiLayout &row) override + { + const int icon = get_instance_reference_icon(reference_); + StringRefNull name = reference_.name(); + if (name.is_empty()) { + name = IFACE_("Geometry"); + } + uiItemL(&row, name.c_str(), icon); + draw_count(*this, user_count_); + } + + int reference_index() const + { + return reference_index_; + } +}; + +class GeometryInstancesTreeView : public ui::AbstractTreeView { + private: + bke::GeometrySet root_geometry_set_; + SpaceSpreadsheet &sspreadsheet_; + bScreen &screen_; + + friend class InstancesTreeViewItem; + + public: + GeometryInstancesTreeView(bke::GeometrySet geometry_set, const bContext &C) + : root_geometry_set_(std::move(geometry_set)), + sspreadsheet_(*CTX_wm_space_spreadsheet(&C)), + screen_(*CTX_wm_screen(&C)) + { + } + + void build_tree() override + { + auto &root_item = this->add_tree_item(root_geometry_set_); + root_item.uncollapse_by_default(); + if (const bke::Instances *instances = root_geometry_set_.get_instances()) { + this->build_tree_for_instances(root_item, *instances); + } + } + + void build_tree_for_instances(ui::TreeViewItemContainer &parent, const bke::Instances &instances) + { + const Span references = instances.references(); + for (const int reference_i : references.index_range()) { + auto &reference_item = parent.add_tree_item(instances, + reference_i); + const bke::InstanceReference &reference = references[reference_i]; + bke::GeometrySet reference_geometry; + reference.to_geometry_set(reference_geometry); + if (const bke::Instances *child_instances = reference_geometry.get_instances()) { + this->build_tree_for_instances(reference_item, *child_instances); + } + } + } +}; + class DataSetViewItem : public ui::AbstractTreeViewItem { public: GeometryDataSetTreeView &get_tree() const; @@ -380,7 +485,7 @@ class InstancesViewItem : public DataSetViewItem { class GeometryDataSetTreeView : public ui::AbstractTreeView { private: - bke::GeometrySet root_geometry_set_; + bke::GeometrySet geometry_set_; SpaceSpreadsheet &sspreadsheet_; bScreen &screen_; @@ -388,7 +493,7 @@ class GeometryDataSetTreeView : public ui::AbstractTreeView { public: GeometryDataSetTreeView(bke::GeometrySet geometry_set, const bContext &C) - : root_geometry_set_(std::move(geometry_set)), + : geometry_set_(std::move(geometry_set)), sspreadsheet_(*CTX_wm_space_spreadsheet(&C)), screen_(*CTX_wm_screen(&C)) { @@ -396,7 +501,7 @@ class GeometryDataSetTreeView : public ui::AbstractTreeView { void build_tree() override { - this->build_tree_for_geometry(root_geometry_set_, *this); + this->build_tree_for_geometry(geometry_set_, *this); } void build_tree_for_geometry(const bke::GeometrySet &geometry, ui::TreeViewItemContainer &parent) @@ -482,6 +587,60 @@ GeometryDataSetTreeView &DataSetViewItem::get_tree() const return static_cast(this->get_tree_view()); } +GeometryInstancesTreeView &InstancesTreeViewItem::get_tree() const +{ + return static_cast(this->get_tree_view()); +} + +void InstancesTreeViewItem::get_parent_instance_ids( + Vector &r_instance_ids) const +{ + if (auto *reference_item = dynamic_cast(this)) { + r_instance_ids.append({reference_item->reference_index()}); + } + this->foreach_parent([&](const ui::AbstractTreeViewItem &item) { + if (auto *reference_item = dynamic_cast(&item)) { + r_instance_ids.append({reference_item->reference_index()}); + } + }); + std::reverse(r_instance_ids.begin(), r_instance_ids.end()); +} + +std::optional InstancesTreeViewItem::should_be_active() const +{ + GeometryInstancesTreeView &tree_view = this->get_tree(); + SpaceSpreadsheet &sspreadsheet = tree_view.sspreadsheet_; + + Vector instance_ids; + this->get_parent_instance_ids(instance_ids); + if (sspreadsheet.instance_ids_num != instance_ids.size()) { + return false; + } + for (const int i : instance_ids.index_range()) { + const SpreadsheetInstanceID &a = sspreadsheet.instance_ids[i]; + const SpreadsheetInstanceID &b = instance_ids[i]; + if (a.reference_index != b.reference_index) { + return false; + } + } + return true; +} + +void InstancesTreeViewItem::on_activate(bContext &C) +{ + Vector instance_ids; + this->get_parent_instance_ids(instance_ids); + + SpaceSpreadsheet &sspreadsheet = *CTX_wm_space_spreadsheet(&C); + + MEM_SAFE_FREE(sspreadsheet.instance_ids); + sspreadsheet.instance_ids = MEM_cnew_array(instance_ids.size(), __func__); + sspreadsheet.instance_ids_num = instance_ids.size(); + initialized_copy_n(instance_ids.data(), instance_ids.size(), sspreadsheet.instance_ids); + + WM_main_add_notifier(NC_SPACE | ND_SPACE_SPREADSHEET, nullptr); +} + void DataSetViewItem::on_activate(bContext &C) { std::optional data_id = this->get_geometry_data_id(); @@ -555,14 +714,29 @@ void spreadsheet_data_set_panel_draw(const bContext *C, Panel *panel) uiBlock *block = uiLayoutGetBlock(layout); UI_block_layout_set_current(block, layout); + const bke::GeometrySet root_geometry = spreadsheet_get_display_geometry_set(sspreadsheet, + object); - ui::AbstractTreeView *tree_view = UI_block_add_view( - *block, - "Data Set Tree View", - std::make_unique( - spreadsheet_get_display_geometry_set(sspreadsheet, object), *C)); - tree_view->set_context_menu_title("Spreadsheet"); - ui::TreeViewBuilder::build_tree_view(*tree_view, *layout); + if (uiLayout *panel = uiLayoutPanel(C, layout, "instance tree", false, IFACE_("Geometry"))) { + ui::AbstractTreeView *tree_view = UI_block_add_view( + *block, + "Instances Tree View", + std::make_unique(root_geometry, *C)); + tree_view->set_context_menu_title("Instance"); + ui::TreeViewBuilder::build_tree_view(*tree_view, *panel, {}, false); + } + if (uiLayout *panel = uiLayoutPanel( + C, layout, "geometry_domain_tree_view", false, IFACE_("Domain"))) + { + bke::GeometrySet instance_geometry = get_geometry_set_for_instance_ids( + root_geometry, {sspreadsheet->instance_ids, sspreadsheet->instance_ids_num}); + ui::AbstractTreeView *tree_view = UI_block_add_view( + *block, + "Data Set Tree View", + std::make_unique(std::move(instance_geometry), *C)); + tree_view->set_context_menu_title("Domain"); + ui::TreeViewBuilder::build_tree_view(*tree_view, *panel, {}, false); + } } } // namespace blender::ed::spreadsheet diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 30061447792..aec9148edfb 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -1953,6 +1953,10 @@ typedef struct SpreadsheetColumn { char *display_name; } SpreadsheetColumn; +typedef struct SpreadsheetInstanceID { + int reference_index; +} SpreadsheetInstanceID; + typedef struct SpaceSpreadsheet { SpaceLink *next, *prev; /** Storage of regions for inactive spaces. */ @@ -1975,6 +1979,13 @@ typedef struct SpaceSpreadsheet { */ ViewerPath viewer_path; + /** + * The "path" to the currently active instance reference. This is needed when viewing nested + * instances. + */ + SpreadsheetInstanceID *instance_ids; + int instance_ids_num; + /* eSpaceSpreadsheet_FilterFlag. */ uint8_t filter_flag; @@ -1989,7 +2000,6 @@ typedef struct SpaceSpreadsheet { /* eSpaceSpreadsheet_Flag. */ uint32_t flag; - char _pad1[4]; SpaceSpreadsheet_Runtime *runtime; } SpaceSpreadsheet;