Spreadsheet: support showing data of nested instances (alternative 1) #124186

Closed
Jacques Lucke wants to merge 29 commits from JacquesLucke/blender:spreadsheet-instances into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
8 changed files with 247 additions and 31 deletions

View File

@ -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,

View File

@ -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. */

View File

@ -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<uiButViewItem *>(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);

View File

@ -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);
}

View File

@ -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<SpreadsheetInstanceID *>(
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);
}

View File

@ -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<SpreadsheetInstanceID> 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<bke::InstanceReference> 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<DataSource> 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<DataSource> data_source_from_geometry(const bContext *C, Object
if (component_type == bke::GeometryComponent::Type::Volume) {
return std::make_unique<VolumeDataSource>(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<GeometryDataSource>(
object_orig, std::move(geometry_set), component_type, domain, active_layer_index);
}

View File

@ -117,6 +117,8 @@ class DataSetViewItem : public ui::AbstractTreeViewItem {
public:
GeometryDataSetTreeView &get_tree() const;
void get_parent_instance_ids(Vector<SpreadsheetInstanceID> &r_instance_ids) const;
void on_activate(bContext &C) override;
std::optional<bool> 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<InstancesViewItem>(instances);
auto &instances_view = parent.add_tree_item<InstancesViewItem>(instances);
if (!instances) {
return;
}
const Span<bke::InstanceReference> references = instances->references();
for (const int reference_i : references.index_range()) {
auto &reference_item = instances_view.add_tree_item<InstanceReferenceViewItem>(*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<CollectionChildViewItem>(
*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<CollectionObjectViewItem>(
*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<GeometryDataSetTreeView &>(this->get_tree_view());
}
void DataSetViewItem::get_parent_instance_ids(Vector<SpreadsheetInstanceID> &r_instance_ids) const
{
this->foreach_parent([&](const ui::AbstractTreeViewItem &item) {
if (auto *reference_item = dynamic_cast<const InstanceReferenceViewItem *>(&item)) {
r_instance_ids.append({reference_item->reference_index()});
}
else if (auto *collection_object_item = dynamic_cast<const CollectionObjectViewItem *>(&item))
{
r_instance_ids.append({collection_object_item->child_index()});
}
else if (auto *collection_child_item = dynamic_cast<const CollectionChildViewItem *>(&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<GeometryDataIdentifier> 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<const DataSetViewItem *>(&item)) {
data_id = data_set_view_item->get_geometry_data_id();
}
});
if (!data_id) {
return;
}
return;
}
Vector<SpreadsheetInstanceID> 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<SpreadsheetInstanceID>(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<bool> DataSetViewItem::should_be_active() const
return false;
}
}
Vector<SpreadsheetInstanceID> 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;
}

View File

@ -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;