diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh index c6900f676e1..a10b099f6e0 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, diff --git a/source/blender/editors/interface/interface_intern.hh b/source/blender/editors/interface/interface_intern.hh index 6096b320015..5fe30096464 100644 --- a/source/blender/editors/interface/interface_intern.hh +++ b/source/blender/editors/interface/interface_intern.hh @@ -421,6 +421,8 @@ struct uiButViewItem : public uiBut { * (e.g. so highlights are drawn smaller). */ int draw_width = 0; int draw_height = 0; + + bool activateable = true; }; /** Derived struct for #UI_BTYPE_HSVCUBE. */ diff --git a/source/blender/editors/interface/interface_widgets.cc b/source/blender/editors/interface/interface_widgets.cc index d20d39356d2..5665668c2b1 100644 --- a/source/blender/editors/interface/interface_widgets.cc +++ b/source/blender/editors/interface/interface_widgets.cc @@ -4205,8 +4205,10 @@ static void widget_list_itembut(uiBut *but, { rcti draw_rect = *rect; + bool activateable = true; if (but->type == UI_BTYPE_VIEW_ITEM) { uiButViewItem *item_but = static_cast(but); + activateable = item_but->activateable; if (item_but->draw_width > 0) { BLI_rcti_resize_x(&draw_rect, zoom * item_but->draw_width); } @@ -4223,9 +4225,11 @@ static void widget_list_itembut(uiBut *but, const float rad = widget_radius_from_zoom(zoom, wcol); round_box_edges(&wtb, UI_CNR_ALL, &draw_rect, rad); - if (state->but_flag & UI_HOVER && !(state->but_flag & UI_SELECT)) { - copy_v3_v3_uchar(wcol->inner, wcol->text); - wcol->inner[3] = 20; + if (activateable) { + if (state->but_flag & UI_HOVER && !(state->but_flag & UI_SELECT)) { + copy_v3_v3_uchar(wcol->inner, wcol->text); + wcol->inner[3] = 20; + } } widgetbase_draw(&wtb, wcol); diff --git a/source/blender/editors/interface/views/tree_view.cc b/source/blender/editors/interface/views/tree_view.cc index 597fca3c793..58ae43dde20 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. */ @@ -315,6 +322,7 @@ void AbstractTreeViewItem::add_treerow_button(uiBlock &block) view_item_but_->view_item = this; view_item_but_->draw_height = unpadded_item_height(); + view_item_but_->activateable = is_activatable_; UI_but_func_set(view_item_but_, tree_row_click_fn, view_item_but_, nullptr); } 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 a2851b41826..6cd334ee285 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" @@ -632,13 +633,39 @@ bke::GeometrySet spreadsheet_get_display_geometry_set(const SpaceSpreadsheet *ss return geometry_set; } +static 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 {}; + } + const Span references = instances->references(); + if (instance_id.reference_index < 0 || instance_id.reference_index >= references.size()) { + return {}; + } + 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 +673,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_dataset_draw.cc b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc index ea4029ec149..02b33901136 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc @@ -117,6 +117,8 @@ class DataSetViewItem : public ui::AbstractTreeViewItem { public: GeometryDataSetTreeView &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; @@ -133,6 +135,7 @@ class MeshViewItem : public DataSetViewItem { MeshViewItem() { label_ = IFACE_("Mesh"); + this->disable_activatable(); } void build_row(uiLayout &row) override @@ -172,6 +175,7 @@ class CurvesViewItem : public DataSetViewItem { CurvesViewItem() { label_ = IFACE_("Curve"); + this->disable_activatable(); } void build_row(uiLayout &row) override @@ -212,6 +216,7 @@ class GreasePencilViewItem : public DataSetViewItem { GreasePencilViewItem() { label_ = IFACE_("Grease Pencil"); + this->disable_activatable(); } void build_row(uiLayout &row) override @@ -254,6 +259,7 @@ class GreasePencilLayerViewItem : public DataSetViewItem { : layer_(layer), layer_index_(layer_index) { label_ = layer_.name(); + this->disable_activatable(); } void build_row(uiLayout &row) override @@ -300,6 +306,7 @@ class PointCloudViewItem : public DataSetViewItem { PointCloudViewItem() { label_ = IFACE_("Point Cloud"); + this->disable_activatable(); } void build_row(uiLayout &row) override @@ -380,6 +387,84 @@ class InstancesViewItem : public DataSetViewItem { } }; +class InstanceReferenceViewItem : public DataSetViewItem { + private: + const bke::InstanceReference &reference_; + int reference_index_; + + 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); + this->disable_activatable(); + } + + void build_row(uiLayout &row) override + { + const int icon = get_instance_reference_icon(reference_); + std::string name = reference_.name(); + if (name.empty()) { + name = IFACE_("Geometry"); + } + uiItemL(&row, name.c_str(), icon); + } + + int reference_index() const + { + return reference_index_; + } +}; + +class CollectionChildViewItem : public DataSetViewItem { + private: + const CollectionChild *collection_child_; + int child_index_; + + public: + CollectionChildViewItem(const CollectionChild &collection_child, const int child_index) + : collection_child_(&collection_child), child_index_(child_index) + { + label_ = std::to_string(child_index); + this->disable_activatable(); + } + + void build_row(uiLayout &row) override + { + uiItemL(&row, collection_child_->collection->id.name + 2, ICON_OUTLINER_COLLECTION); + } + + int child_index() const + { + return child_index_; + } +}; + +class CollectionObjectViewItem : public DataSetViewItem { + private: + const CollectionObject *collection_object_; + int child_index_; + + public: + CollectionObjectViewItem(const CollectionObject &collection_object, const int child_index) + : collection_object_(&collection_object), child_index_(child_index) + { + label_ = std::to_string(child_index); + this->disable_activatable(); + } + + void build_row(uiLayout &row) override + { + const int icon = ED_outliner_icon_from_id(collection_object_->ob->id); + uiItemL(&row, collection_object_->ob->id.name + 2, icon); + } + + int child_index() const + { + return child_index_; + } +}; + class GeometryDataSetTreeView : public ui::AbstractTreeView { private: bke::GeometrySet root_geometry_set_; @@ -398,28 +483,37 @@ class GeometryDataSetTreeView : public ui::AbstractTreeView { void build_tree() override { - this->build_tree_for_geometry(root_geometry_set_, *this); + this->build_tree_for_geometry(root_geometry_set_, *this, true); } - void build_tree_for_geometry(const bke::GeometrySet &geometry, ui::TreeViewItemContainer &parent) + void build_tree_for_geometry(const bke::GeometrySet &geometry, + ui::TreeViewItemContainer &parent, + const bool all_components) { const Mesh *mesh = geometry.get_mesh(); - this->build_tree_for_mesh(mesh, parent); - + if (mesh || all_components) { + this->build_tree_for_mesh(mesh, parent); + } const Curves *curves = geometry.get_curves(); - this->build_tree_for_curves(curves, parent); - + if (curves || all_components) { + this->build_tree_for_curves(curves, parent); + } const GreasePencil *grease_pencil = geometry.get_grease_pencil(); - this->build_tree_for_grease_pencil(grease_pencil, parent); - + if (grease_pencil || all_components) { + this->build_tree_for_grease_pencil(grease_pencil, parent); + } const PointCloud *pointcloud = geometry.get_pointcloud(); - this->build_tree_for_pointcloud(pointcloud, parent); - + if (pointcloud || all_components) { + this->build_tree_for_pointcloud(pointcloud, parent); + } const Volume *volume = geometry.get_volume(); - this->build_tree_for_volume(volume, parent); - + if (volume || all_components) { + this->build_tree_for_volume(volume, parent); + } const bke::Instances *instances = geometry.get_instances(); - this->build_tree_for_instances(instances, parent); + if (instances || all_components) { + this->build_tree_for_instances(instances, parent); + } } void build_tree_for_mesh(const Mesh *mesh, ui::TreeViewItemContainer &parent) @@ -475,7 +569,41 @@ class GeometryDataSetTreeView : public ui::AbstractTreeView { void build_tree_for_instances(const bke::Instances *instances, ui::TreeViewItemContainer &parent) { - parent.add_tree_item(instances); + auto &instances_view = parent.add_tree_item(instances); + if (!instances) { + return; + } + const Span references = instances->references(); + for (const int reference_i : references.index_range()) { + auto &reference_item = instances_view.add_tree_item(*instances, + reference_i); + const bke::InstanceReference &reference = references[reference_i]; + if (reference.type() == bke::InstanceReference::Type::Collection) { + this->build_tree_for_collection(reference.collection(), reference_item); + } + else { + bke::GeometrySet reference_geometry; + reference.to_geometry_set(reference_geometry); + this->build_tree_for_geometry(reference_geometry, reference_item, false); + } + } + } + + void build_tree_for_collection(const Collection &collection, ui::TreeViewItemContainer &parent) + { + int child_index = 0; + LISTBASE_FOREACH (CollectionChild *, collection_child, &collection.children) { + auto &collection_child_item = parent.add_tree_item( + *collection_child, child_index++); + this->build_tree_for_collection(*collection_child->collection, collection_child_item); + } + LISTBASE_FOREACH (CollectionObject *, collection_object, &collection.gobject) { + auto &collection_object_item = parent.add_tree_item( + *collection_object, child_index++); + const bke::GeometrySet geometry = bke::object_get_evaluated_geometry_set( + *collection_object->ob); + this->build_tree_for_geometry(geometry, collection_object_item, false); + } } }; @@ -484,26 +612,40 @@ GeometryDataSetTreeView &DataSetViewItem::get_tree() const return static_cast(this->get_tree_view()); } +void DataSetViewItem::get_parent_instance_ids(Vector &r_instance_ids) const +{ + this->foreach_parent([&](const ui::AbstractTreeViewItem &item) { + if (auto *reference_item = dynamic_cast(&item)) { + r_instance_ids.append({reference_item->reference_index()}); + } + else if (auto *collection_object_item = dynamic_cast(&item)) + { + r_instance_ids.append({collection_object_item->child_index()}); + } + else if (auto *collection_child_item = dynamic_cast(&item)) { + r_instance_ids.append({collection_child_item->child_index()}); + } + }); + std::reverse(r_instance_ids.begin(), r_instance_ids.end()); +} + void DataSetViewItem::on_activate(bContext &C) { std::optional data_id = this->get_geometry_data_id(); if (!data_id) { - this->foreach_item_recursive([&](const ui::AbstractTreeViewItem &item) { - if (data_id) { - return; - } - if (auto *data_set_view_item = dynamic_cast(&item)) { - data_id = data_set_view_item->get_geometry_data_id(); - } - }); - if (!data_id) { - return; - } + return; } + Vector instance_ids; + this->get_parent_instance_ids(instance_ids); bScreen &screen = *CTX_wm_screen(&C); 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); + sspreadsheet.geometry_component_type = uint8_t(data_id->component_type); if (data_id->domain) { sspreadsheet.attribute_domain = uint8_t(*data_id->domain); @@ -542,6 +684,18 @@ std::optional DataSetViewItem::should_be_active() const return false; } } + 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; } 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;