Changeset View
Standalone View
source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
| Show All 27 Lines | |||||
| #include "DNA_userdef_types.h" | #include "DNA_userdef_types.h" | ||||
| #include "DEG_depsgraph_query.h" | #include "DEG_depsgraph_query.h" | ||||
| #include "ED_spreadsheet.h" | #include "ED_spreadsheet.h" | ||||
| #include "NOD_geometry_nodes_eval_log.hh" | #include "NOD_geometry_nodes_eval_log.hh" | ||||
| #include "FN_field_cpp_type.hh" | |||||
| #include "bmesh.h" | #include "bmesh.h" | ||||
| #include "spreadsheet_data_source_geometry.hh" | #include "spreadsheet_data_source_geometry.hh" | ||||
| #include "spreadsheet_intern.hh" | #include "spreadsheet_intern.hh" | ||||
| namespace geo_log = blender::nodes::geometry_nodes_eval_log; | namespace geo_log = blender::nodes::geometry_nodes_eval_log; | ||||
| using blender::fn::GField; | |||||
| namespace blender::ed::spreadsheet { | namespace blender::ed::spreadsheet { | ||||
| static std::optional<eSpreadsheetColumnValueType> cpp_type_to_column_value_type( | |||||
| const fn::CPPType &type) | |||||
| { | |||||
| if (type.is<bool>()) { | |||||
| return SPREADSHEET_VALUE_TYPE_BOOL; | |||||
| } | |||||
| if (type.is<int>()) { | |||||
| return SPREADSHEET_VALUE_TYPE_INT32; | |||||
| } | |||||
| if (type.is<float>()) { | |||||
| return SPREADSHEET_VALUE_TYPE_FLOAT; | |||||
| } | |||||
| if (type.is<float2>()) { | |||||
| return SPREADSHEET_VALUE_TYPE_FLOAT2; | |||||
| } | |||||
| if (type.is<float3>()) { | |||||
| return SPREADSHEET_VALUE_TYPE_FLOAT3; | |||||
| } | |||||
| if (type.is<ColorGeometry4f>()) { | |||||
| return SPREADSHEET_VALUE_TYPE_COLOR; | |||||
| } | |||||
| return std::nullopt; | |||||
| } | |||||
| void ExtraColumns::foreach_default_column_ids( | |||||
| FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const | |||||
| { | |||||
| for (const auto &item : columns_.items()) { | |||||
| SpreadsheetColumnID column_id; | |||||
| column_id.name = (char *)item.key.c_str(); | |||||
| fn(column_id, true); | |||||
| } | |||||
| } | |||||
| std::unique_ptr<ColumnValues> ExtraColumns::get_column_values( | |||||
| const SpreadsheetColumnID &column_id) const | |||||
| { | |||||
| const fn::GSpan *values = columns_.lookup_ptr(column_id.name); | |||||
| if (values == nullptr) { | |||||
| return {}; | |||||
| } | |||||
| eSpreadsheetColumnValueType column_type = *cpp_type_to_column_value_type(values->type()); | |||||
| return column_values_from_function(column_type, | |||||
| column_id.name, | |||||
| values->size(), | |||||
| [column_type, values](int index, CellValue &r_cell_value) { | |||||
| const void *value = (*values)[index]; | |||||
| switch (column_type) { | |||||
| case SPREADSHEET_VALUE_TYPE_BOOL: | |||||
| r_cell_value.value_bool = *(const bool *)value; | |||||
| break; | |||||
| case SPREADSHEET_VALUE_TYPE_INT32: | |||||
| r_cell_value.value_int = *(const int *)value; | |||||
| break; | |||||
| case SPREADSHEET_VALUE_TYPE_FLOAT: | |||||
| r_cell_value.value_float = *(const float *)value; | |||||
| break; | |||||
| case SPREADSHEET_VALUE_TYPE_FLOAT2: | |||||
| r_cell_value.value_float2 = *(const float2 *)value; | |||||
| break; | |||||
| case SPREADSHEET_VALUE_TYPE_FLOAT3: | |||||
| r_cell_value.value_float3 = *(const float3 *)value; | |||||
| break; | |||||
| case SPREADSHEET_VALUE_TYPE_COLOR: | |||||
| r_cell_value.value_color = *( | |||||
| const ColorGeometry4f *)value; | |||||
| break; | |||||
| case SPREADSHEET_VALUE_TYPE_INSTANCES: | |||||
| break; | |||||
| } | |||||
| }); | |||||
| } | |||||
| void GeometryDataSource::foreach_default_column_ids( | void GeometryDataSource::foreach_default_column_ids( | ||||
| FunctionRef<void(const SpreadsheetColumnID &)> fn) const | FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const | ||||
| { | { | ||||
| extra_columns_.foreach_default_column_ids(fn); | |||||
| component_->attribute_foreach( | component_->attribute_foreach( | ||||
| [&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { | [&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { | ||||
| if (meta_data.domain != domain_) { | if (meta_data.domain != domain_) { | ||||
| return true; | return true; | ||||
| } | } | ||||
| if (attribute_id.is_anonymous()) { | if (attribute_id.is_anonymous()) { | ||||
| return true; | return true; | ||||
| } | } | ||||
| SpreadsheetColumnID column_id; | SpreadsheetColumnID column_id; | ||||
| column_id.name = (char *)attribute_id.name().data(); | column_id.name = (char *)attribute_id.name().data(); | ||||
| fn(column_id); | fn(column_id, false); | ||||
| return true; | return true; | ||||
| }); | }); | ||||
| } | } | ||||
| std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( | std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( | ||||
| const SpreadsheetColumnID &column_id) const | const SpreadsheetColumnID &column_id) const | ||||
| { | { | ||||
| std::lock_guard lock{mutex_}; | std::lock_guard lock{mutex_}; | ||||
| std::unique_ptr<ColumnValues> extra_column_values = extra_columns_.get_column_values(column_id); | |||||
HooglyBoogly: I guess this means attributes with the name "Viewer" won't be visible in that mode of the… | |||||
Done Inline ActionsTrue.. Could be worked around in the future by adding more info to column_id. JacquesLucke: True.. Could be worked around in the future by adding more info to `column_id`. | |||||
| if (extra_column_values) { | |||||
| return extra_column_values; | |||||
| } | |||||
| bke::ReadAttributeLookup attribute = component_->attribute_try_get_for_read(column_id.name); | bke::ReadAttributeLookup attribute = component_->attribute_try_get_for_read(column_id.name); | ||||
| if (!attribute) { | if (!attribute) { | ||||
| return {}; | return {}; | ||||
| } | } | ||||
| const fn::GVArray *varray = scope_.add(std::move(attribute.varray)); | const fn::GVArray *varray = scope_.add(std::move(attribute.varray)); | ||||
| if (attribute.domain != domain_) { | if (attribute.domain != domain_) { | ||||
| return {}; | return {}; | ||||
| } | } | ||||
| Show All 23 Lines | case CD_PROP_BOOL: | ||||
| column_id.name, | column_id.name, | ||||
| domain_size, | domain_size, | ||||
| [varray](int index, CellValue &r_cell_value) { | [varray](int index, CellValue &r_cell_value) { | ||||
| bool value; | bool value; | ||||
| varray->get(index, &value); | varray->get(index, &value); | ||||
| r_cell_value.value_bool = value; | r_cell_value.value_bool = value; | ||||
| }); | }); | ||||
| case CD_PROP_FLOAT2: { | case CD_PROP_FLOAT2: { | ||||
| return column_values_from_function( | return column_values_from_function(SPREADSHEET_VALUE_TYPE_FLOAT2, | ||||
| SPREADSHEET_VALUE_TYPE_FLOAT2, | |||||
| column_id.name, | column_id.name, | ||||
| domain_size, | domain_size, | ||||
| [varray](int index, CellValue &r_cell_value) { | [varray](int index, CellValue &r_cell_value) { | ||||
| float2 value; | float2 value; | ||||
| varray->get(index, &value); | varray->get(index, &value); | ||||
| r_cell_value.value_float2 = value; | r_cell_value.value_float2 = value; | ||||
| }, | }); | ||||
| default_float2_column_width); | |||||
| } | } | ||||
| case CD_PROP_FLOAT3: { | case CD_PROP_FLOAT3: { | ||||
| return column_values_from_function( | return column_values_from_function(SPREADSHEET_VALUE_TYPE_FLOAT3, | ||||
| SPREADSHEET_VALUE_TYPE_FLOAT3, | |||||
| column_id.name, | column_id.name, | ||||
| domain_size, | domain_size, | ||||
| [varray](int index, CellValue &r_cell_value) { | [varray](int index, CellValue &r_cell_value) { | ||||
| float3 value; | float3 value; | ||||
| varray->get(index, &value); | varray->get(index, &value); | ||||
| r_cell_value.value_float3 = value; | r_cell_value.value_float3 = value; | ||||
| }, | }); | ||||
| default_float3_column_width); | |||||
Not Done Inline ActionsI haven't figured out what happened with the default column width handling, or why that's related to the patch, what am I missing? HooglyBoogly: I haven't figured out what happened with the default column width handling, or why that's… | |||||
Done Inline ActionsI moved this to a more central place in space_spreadsheet.cc. We didn't do it before because we didn't have SPREADSHEET_VALUE_TYPE_*. JacquesLucke: I moved this to a more central place in `space_spreadsheet.cc`. We didn't do it before because… | |||||
| } | } | ||||
| case CD_PROP_COLOR: { | case CD_PROP_COLOR: { | ||||
| return column_values_from_function( | return column_values_from_function(SPREADSHEET_VALUE_TYPE_COLOR, | ||||
| SPREADSHEET_VALUE_TYPE_COLOR, | |||||
| column_id.name, | column_id.name, | ||||
| domain_size, | domain_size, | ||||
| [varray](int index, CellValue &r_cell_value) { | [varray](int index, CellValue &r_cell_value) { | ||||
| ColorGeometry4f value; | ColorGeometry4f value; | ||||
| varray->get(index, &value); | varray->get(index, &value); | ||||
| r_cell_value.value_color = value; | r_cell_value.value_color = value; | ||||
| }, | }); | ||||
| default_color_column_width); | |||||
| } | } | ||||
| default: | default: | ||||
| break; | break; | ||||
| } | } | ||||
| return {}; | return {}; | ||||
| } | } | ||||
| int GeometryDataSource::tot_rows() const | int GeometryDataSource::tot_rows() const | ||||
| ▲ Show 20 Lines • Show All 139 Lines • ▼ Show 20 Lines | auto is_vertex_selected = [&](int vertex_index) -> bool { | ||||
| BMVert *vert = bm->vtable[vertex_index]; | BMVert *vert = bm->vtable[vertex_index]; | ||||
| return BM_elem_flag_test(vert, BM_ELEM_SELECT); | return BM_elem_flag_test(vert, BM_ELEM_SELECT); | ||||
| }; | }; | ||||
| get_selected_indices_on_domain(*mesh_eval, domain_, is_vertex_selected, rows_included); | get_selected_indices_on_domain(*mesh_eval, domain_, is_vertex_selected, rows_included); | ||||
| } | } | ||||
| } | } | ||||
| void InstancesDataSource::foreach_default_column_ids( | void InstancesDataSource::foreach_default_column_ids( | ||||
| FunctionRef<void(const SpreadsheetColumnID &)> fn) const | FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const | ||||
| { | { | ||||
| if (component_->instances_amount() == 0) { | if (component_->instances_amount() == 0) { | ||||
| return; | return; | ||||
| } | } | ||||
| extra_columns_.foreach_default_column_ids(fn); | |||||
| SpreadsheetColumnID column_id; | SpreadsheetColumnID column_id; | ||||
| column_id.name = (char *)"Name"; | column_id.name = (char *)"Name"; | ||||
| fn(column_id); | fn(column_id, false); | ||||
| for (const char *name : {"Position", "Rotation", "Scale", "ID"}) { | for (const char *name : {"Position", "Rotation", "Scale", "ID"}) { | ||||
| column_id.name = (char *)name; | column_id.name = (char *)name; | ||||
| fn(column_id); | fn(column_id, false); | ||||
| } | } | ||||
| } | } | ||||
| std::unique_ptr<ColumnValues> InstancesDataSource::get_column_values( | std::unique_ptr<ColumnValues> InstancesDataSource::get_column_values( | ||||
| const SpreadsheetColumnID &column_id) const | const SpreadsheetColumnID &column_id) const | ||||
| { | { | ||||
| if (component_->instances_amount() == 0) { | if (component_->instances_amount() == 0) { | ||||
| return {}; | return {}; | ||||
| } | } | ||||
| std::unique_ptr<ColumnValues> extra_column_values = extra_columns_.get_column_values(column_id); | |||||
| if (extra_column_values) { | |||||
| return extra_column_values; | |||||
| } | |||||
| const int size = this->tot_rows(); | const int size = this->tot_rows(); | ||||
| if (STREQ(column_id.name, "Name")) { | if (STREQ(column_id.name, "Name")) { | ||||
| Span<int> reference_handles = component_->instance_reference_handles(); | Span<int> reference_handles = component_->instance_reference_handles(); | ||||
| Span<InstanceReference> references = component_->references(); | Span<InstanceReference> references = component_->references(); | ||||
| std::unique_ptr<ColumnValues> values = column_values_from_function( | std::unique_ptr<ColumnValues> values = column_values_from_function( | ||||
| SPREADSHEET_VALUE_TYPE_INSTANCES, | SPREADSHEET_VALUE_TYPE_INSTANCES, | ||||
| "Name", | "Name", | ||||
| size, | size, | ||||
| Show All 15 Lines | std::unique_ptr<ColumnValues> values = column_values_from_function( | ||||
| r_cell_value.value_geometry_set = GeometrySetCellValue{&geometry_set}; | r_cell_value.value_geometry_set = GeometrySetCellValue{&geometry_set}; | ||||
| break; | break; | ||||
| } | } | ||||
| case InstanceReference::Type::None: { | case InstanceReference::Type::None: { | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| }); | }); | ||||
| values->default_width = 8.0f; | |||||
| return values; | return values; | ||||
| } | } | ||||
| Span<float4x4> transforms = component_->instance_transforms(); | Span<float4x4> transforms = component_->instance_transforms(); | ||||
| if (STREQ(column_id.name, "Position")) { | if (STREQ(column_id.name, "Position")) { | ||||
| return column_values_from_function( | return column_values_from_function( | ||||
| SPREADSHEET_VALUE_TYPE_FLOAT3, | SPREADSHEET_VALUE_TYPE_FLOAT3, | ||||
| column_id.name, | column_id.name, | ||||
| size, | size, | ||||
| [transforms](int index, CellValue &r_cell_value) { | [transforms](int index, CellValue &r_cell_value) { | ||||
| r_cell_value.value_float3 = transforms[index].translation(); | r_cell_value.value_float3 = transforms[index].translation(); | ||||
| }, | }); | ||||
| default_float3_column_width); | |||||
| } | } | ||||
| if (STREQ(column_id.name, "Rotation")) { | if (STREQ(column_id.name, "Rotation")) { | ||||
| return column_values_from_function( | return column_values_from_function(SPREADSHEET_VALUE_TYPE_FLOAT3, | ||||
| SPREADSHEET_VALUE_TYPE_FLOAT3, | |||||
| column_id.name, | column_id.name, | ||||
| size, | size, | ||||
| [transforms](int index, CellValue &r_cell_value) { | [transforms](int index, CellValue &r_cell_value) { | ||||
| r_cell_value.value_float3 = transforms[index].to_euler(); | r_cell_value.value_float3 = transforms[index].to_euler(); | ||||
| }, | }); | ||||
| default_float3_column_width); | |||||
| } | } | ||||
| if (STREQ(column_id.name, "Scale")) { | if (STREQ(column_id.name, "Scale")) { | ||||
| return column_values_from_function( | return column_values_from_function(SPREADSHEET_VALUE_TYPE_FLOAT3, | ||||
| SPREADSHEET_VALUE_TYPE_FLOAT3, | |||||
| column_id.name, | column_id.name, | ||||
| size, | size, | ||||
| [transforms](int index, CellValue &r_cell_value) { | [transforms](int index, CellValue &r_cell_value) { | ||||
| r_cell_value.value_float3 = transforms[index].scale(); | r_cell_value.value_float3 = transforms[index].scale(); | ||||
| }, | }); | ||||
| default_float3_column_width); | |||||
| } | } | ||||
| Span<int> ids = component_->instance_ids(); | Span<int> ids = component_->instance_ids(); | ||||
| if (STREQ(column_id.name, "ID")) { | if (STREQ(column_id.name, "ID")) { | ||||
| /* Make the column a bit wider by default, since the IDs tend to be large numbers. */ | /* Make the column a bit wider by default, since the IDs tend to be large numbers. */ | ||||
| return column_values_from_function( | return column_values_from_function( | ||||
| SPREADSHEET_VALUE_TYPE_INT32, | SPREADSHEET_VALUE_TYPE_INT32, | ||||
| column_id.name, | column_id.name, | ||||
| size, | size, | ||||
| ▲ Show 20 Lines • Show All 74 Lines • ▼ Show 20 Lines | else { | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| return geometry_set; | return geometry_set; | ||||
| } | } | ||||
| static void find_fields_to_evaluate(const SpaceSpreadsheet *sspreadsheet, | |||||
| Map<std::string, GField> &r_fields) | |||||
| { | |||||
| if (sspreadsheet->object_eval_state != SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE) { | |||||
| return; | |||||
| } | |||||
| if (BLI_listbase_count(&sspreadsheet->context_path) <= 1) { | |||||
| /* No viewer is currently referenced by the context path. */ | |||||
| return; | |||||
| } | |||||
| const geo_log::NodeLog *node_log = geo_log::ModifierLog::find_node_by_spreadsheet_editor_context( | |||||
| *sspreadsheet); | |||||
| if (node_log == nullptr) { | |||||
| return; | |||||
| } | |||||
| for (const geo_log::SocketLog &socket_log : node_log->input_logs()) { | |||||
| const geo_log::ValueLog *value_log = socket_log.value(); | |||||
| if (value_log == nullptr) { | |||||
| continue; | |||||
| } | |||||
| if (const geo_log::GenericValueLog *generic_value_log = | |||||
| dynamic_cast<const geo_log::GenericValueLog *>(value_log)) { | |||||
| const fn::GPointer value = generic_value_log->value(); | |||||
| if (!dynamic_cast<const fn::FieldCPPType *>(value.type())) { | |||||
| continue; | |||||
| } | |||||
| GField field = *(const GField *)value.get(); | |||||
| r_fields.add("Viewer", std::move(field)); | |||||
| } | |||||
| } | |||||
| } | |||||
| static GeometryComponentType get_display_component_type(const bContext *C, Object *object_eval) | static GeometryComponentType get_display_component_type(const bContext *C, Object *object_eval) | ||||
| { | { | ||||
| SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C); | SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C); | ||||
| if (sspreadsheet->object_eval_state != SPREADSHEET_OBJECT_EVAL_STATE_ORIGINAL) { | if (sspreadsheet->object_eval_state != SPREADSHEET_OBJECT_EVAL_STATE_ORIGINAL) { | ||||
| return (GeometryComponentType)sspreadsheet->geometry_component_type; | return (GeometryComponentType)sspreadsheet->geometry_component_type; | ||||
| } | } | ||||
| if (object_eval->type == OB_POINTCLOUD) { | if (object_eval->type == OB_POINTCLOUD) { | ||||
| return GEO_COMPONENT_TYPE_POINT_CLOUD; | return GEO_COMPONENT_TYPE_POINT_CLOUD; | ||||
| } | } | ||||
| return GEO_COMPONENT_TYPE_MESH; | return GEO_COMPONENT_TYPE_MESH; | ||||
| } | } | ||||
| std::unique_ptr<DataSource> data_source_from_geometry(const bContext *C, Object *object_eval) | std::unique_ptr<DataSource> data_source_from_geometry(const bContext *C, Object *object_eval) | ||||
| { | { | ||||
| SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C); | SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C); | ||||
| const AttributeDomain domain = (AttributeDomain)sspreadsheet->attribute_domain; | const AttributeDomain domain = (AttributeDomain)sspreadsheet->attribute_domain; | ||||
| const GeometryComponentType component_type = get_display_component_type(C, object_eval); | const GeometryComponentType component_type = get_display_component_type(C, object_eval); | ||||
| GeometrySet geometry_set = spreadsheet_get_display_geometry_set( | GeometrySet geometry_set = spreadsheet_get_display_geometry_set( | ||||
| sspreadsheet, object_eval, component_type); | sspreadsheet, object_eval, component_type); | ||||
| if (!geometry_set.has(component_type)) { | if (!geometry_set.has(component_type)) { | ||||
| return {}; | return {}; | ||||
| } | } | ||||
| Map<std::string, GField> fields_to_show; | |||||
| find_fields_to_evaluate(sspreadsheet, fields_to_show); | |||||
| ExtraColumns extra_columns; | |||||
| GeometryComponent &component = geometry_set.get_component_for_write(component_type); | |||||
| for (const auto &item : fields_to_show.items()) { | |||||
| StringRef name = item.key; | |||||
| const GField &field = item.value; | |||||
| const int domain_size = component.attribute_domain_size(domain); | |||||
| auto evaluated_array = std::make_unique<fn::GArray<>>(field.cpp_type(), domain_size); | |||||
| bke::GeometryComponentFieldContext field_context{component, domain}; | |||||
Not Done Inline ActionsThis stores evaluated fields, but is called GeometryComponentCache. I guess you thought of adding more to this? Or maybe just a comment like Stores the result of fields evaluated on a geometry component would clarify the purpose a bit. HooglyBoogly: This stores evaluated fields, but is called `GeometryComponentCache`. I guess you thought of… | |||||
| fn::FieldEvaluator field_evaluator{field_context, domain_size}; | |||||
| field_evaluator.add_with_destination(field, *evaluated_array); | |||||
Not Done Inline ActionsThis could use a comment with some explanation. And yay, GArray is helpful! HooglyBoogly: This could use a comment with some explanation. And yay, GArray is helpful! | |||||
| field_evaluator.evaluate(); | |||||
| extra_columns.add(std::move(name), std::move(evaluated_array)); | |||||
| } | |||||
| if (component_type == GEO_COMPONENT_TYPE_INSTANCES) { | if (component_type == GEO_COMPONENT_TYPE_INSTANCES) { | ||||
| return std::make_unique<InstancesDataSource>(geometry_set); | return std::make_unique<InstancesDataSource>(geometry_set, std::move(extra_columns)); | ||||
| } | } | ||||
| return std::make_unique<GeometryDataSource>(object_eval, geometry_set, component_type, domain); | return std::make_unique<GeometryDataSource>( | ||||
| object_eval, geometry_set, component_type, domain, std::move(extra_columns)); | |||||
| } | } | ||||
| } // namespace blender::ed::spreadsheet | } // namespace blender::ed::spreadsheet | ||||
I guess this means attributes with the name "Viewer" won't be visible in that mode of the spreadsheet. Not really a problem, just a bit funny to note.