Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacques Lucke <jacques@blender.org>2021-10-26 12:25:32 +0300
committerJacques Lucke <jacques@blender.org>2021-10-26 12:25:32 +0300
commit5bfe09df2244cb9de0b6554a378eecef77b1e75d (patch)
treee4315292b81a99f08abe83c02e550ae79e41a8d6 /source/blender/editors/space_spreadsheet
parentfee2cedb33ccf33f72d0370892dd25faa884968b (diff)
Geometry Nodes: support viewing field values in spreadsheet
The viewer node has been expanded to have a field input next to the geometry input. When both are connected (by ctrl+shift clicking on a node) the spreadsheet will show the evaluated field on the geometry. The operator to link to the viewer has become a bit smarter. It automatically detects if it should link to the geometry or field input. In the future some more smartness could be added, such as automatically relinking the "right" geometry when viewing a field. Internally, there are two major changes: * Refactor of what happens when ctrl+shift clicking on a node to link to a viewer. The behavior of the geometry nodes viewer is a bit more complex than that of the compositor viewers. The behavior in compositing nodes should not have changed. Any change should be reported as a bug (and then we can decide if it's worse than before or if it needs fixing). * Evaluation, display and caching of fields in the spreadsheet editor. Differential Revision: https://developer.blender.org/D12938
Diffstat (limited to 'source/blender/editors/space_spreadsheet')
-rw-r--r--source/blender/editors/space_spreadsheet/CMakeLists.txt2
-rw-r--r--source/blender/editors/space_spreadsheet/space_spreadsheet.cc66
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_cache.cc79
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_cache.hh78
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh5
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh6
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc297
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh38
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_intern.hh22
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_layout.cc4
10 files changed, 506 insertions, 91 deletions
diff --git a/source/blender/editors/space_spreadsheet/CMakeLists.txt b/source/blender/editors/space_spreadsheet/CMakeLists.txt
index e903feeec1b..91fe1bc01b7 100644
--- a/source/blender/editors/space_spreadsheet/CMakeLists.txt
+++ b/source/blender/editors/space_spreadsheet/CMakeLists.txt
@@ -35,6 +35,7 @@ set(INC
set(SRC
space_spreadsheet.cc
+ spreadsheet_cache.cc
spreadsheet_column.cc
spreadsheet_context.cc
spreadsheet_data_source.cc
@@ -47,6 +48,7 @@ set(SRC
spreadsheet_row_filter.cc
spreadsheet_row_filter_ui.cc
+ spreadsheet_cache.hh
spreadsheet_cell_value.hh
spreadsheet_column.hh
spreadsheet_column_values.hh
diff --git a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
index a82648aeee0..73e0be76466 100644
--- a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
+++ b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
@@ -112,7 +112,7 @@ static void spreadsheet_free(SpaceLink *sl)
{
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl;
- MEM_SAFE_FREE(sspreadsheet->runtime);
+ delete sspreadsheet->runtime;
LISTBASE_FOREACH_MUTABLE (SpreadsheetRowFilter *, row_filter, &sspreadsheet->row_filters) {
spreadsheet_row_filter_free(row_filter);
@@ -129,8 +129,7 @@ static void spreadsheet_init(wmWindowManager *UNUSED(wm), ScrArea *area)
{
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)area->spacedata.first;
if (sspreadsheet->runtime == nullptr) {
- sspreadsheet->runtime = (SpaceSpreadsheet_Runtime *)MEM_callocN(
- sizeof(SpaceSpreadsheet_Runtime), __func__);
+ sspreadsheet->runtime = new SpaceSpreadsheet_Runtime();
}
}
@@ -138,7 +137,7 @@ static SpaceLink *spreadsheet_duplicate(SpaceLink *sl)
{
const SpaceSpreadsheet *sspreadsheet_old = (SpaceSpreadsheet *)sl;
SpaceSpreadsheet *sspreadsheet_new = (SpaceSpreadsheet *)MEM_dupallocN(sspreadsheet_old);
- sspreadsheet_new->runtime = (SpaceSpreadsheet_Runtime *)MEM_dupallocN(sspreadsheet_old->runtime);
+ sspreadsheet_new->runtime = new SpaceSpreadsheet_Runtime(*sspreadsheet_old->runtime);
BLI_listbase_clear(&sspreadsheet_new->row_filters);
LISTBASE_FOREACH (const SpreadsheetRowFilter *, src_filter, &sspreadsheet_old->row_filters) {
@@ -294,16 +293,39 @@ static std::unique_ptr<DataSource> get_data_source(const bContext *C)
return {};
}
-static float get_column_width(const ColumnValues &values)
+static float get_default_column_width(const ColumnValues &values)
{
- if (values.default_width > 0) {
+ if (values.default_width > 0.0f) {
return values.default_width;
}
+ static const float float_width = 3;
+ switch (values.type()) {
+ case SPREADSHEET_VALUE_TYPE_BOOL:
+ return 2.0f;
+ case SPREADSHEET_VALUE_TYPE_INT32:
+ return float_width;
+ case SPREADSHEET_VALUE_TYPE_FLOAT:
+ return float_width;
+ case SPREADSHEET_VALUE_TYPE_FLOAT2:
+ return 2.0f * float_width;
+ case SPREADSHEET_VALUE_TYPE_FLOAT3:
+ return 3.0f * float_width;
+ case SPREADSHEET_VALUE_TYPE_COLOR:
+ return 4.0f * float_width;
+ case SPREADSHEET_VALUE_TYPE_INSTANCES:
+ return 8.0f;
+ }
+ return float_width;
+}
+
+static float get_column_width(const ColumnValues &values)
+{
+ float data_width = get_default_column_width(values);
const int fontid = UI_style_get()->widget.uifont_id;
BLF_size(fontid, UI_DEFAULT_TEXT_POINTS, U.dpi);
const StringRefNull name = values.name();
const float name_width = BLF_width(fontid, name.data(), name.size());
- return std::max<float>(name_width / UI_UNIT_X + 1.0f, 3.0f);
+ return std::max<float>(name_width / UI_UNIT_X + 1.0f, data_width);
}
static float get_column_width_in_pixels(const ColumnValues &values)
@@ -339,21 +361,28 @@ static void update_visible_columns(ListBase &columns, DataSource &data_source)
}
}
- data_source.foreach_default_column_ids([&](const SpreadsheetColumnID &column_id) {
- std::unique_ptr<ColumnValues> values = data_source.get_column_values(column_id);
- if (values) {
- if (used_ids.add(column_id)) {
- SpreadsheetColumnID *new_id = spreadsheet_column_id_copy(&column_id);
- SpreadsheetColumn *new_column = spreadsheet_column_new(new_id);
- BLI_addtail(&columns, new_column);
- }
- }
- });
+ data_source.foreach_default_column_ids(
+ [&](const SpreadsheetColumnID &column_id, const bool is_extra) {
+ std::unique_ptr<ColumnValues> values = data_source.get_column_values(column_id);
+ if (values) {
+ if (used_ids.add(column_id)) {
+ SpreadsheetColumnID *new_id = spreadsheet_column_id_copy(&column_id);
+ SpreadsheetColumn *new_column = spreadsheet_column_new(new_id);
+ if (is_extra) {
+ BLI_addhead(&columns, new_column);
+ }
+ else {
+ BLI_addtail(&columns, new_column);
+ }
+ }
+ }
+ });
}
static void spreadsheet_main_region_draw(const bContext *C, ARegion *region)
{
SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
+ sspreadsheet->runtime->cache.set_all_unused();
spreadsheet_update_context_path(C);
std::unique_ptr<DataSource> data_source = get_data_source(C);
@@ -394,6 +423,9 @@ static void spreadsheet_main_region_draw(const bContext *C, ARegion *region)
ED_region_tag_redraw(footer);
ARegion *sidebar = BKE_area_find_region_type(CTX_wm_area(C), RGN_TYPE_UI);
ED_region_tag_redraw(sidebar);
+
+ /* Free all cache items that have not been used. */
+ sspreadsheet->runtime->cache.remove_all_unused();
}
static void spreadsheet_main_region_listener(const wmRegionListenerParams *params)
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_cache.cc b/source/blender/editors/space_spreadsheet/spreadsheet_cache.cc
new file mode 100644
index 00000000000..2a399e018b6
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_cache.cc
@@ -0,0 +1,79 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "spreadsheet_cache.hh"
+
+namespace blender::ed::spreadsheet {
+
+void SpreadsheetCache::add(std::unique_ptr<Key> key, std::unique_ptr<Value> value)
+{
+ key->is_used = true;
+ cache_map_.add_overwrite(*key, std::move(value));
+ keys_.append(std::move(key));
+}
+
+SpreadsheetCache::Value *SpreadsheetCache::lookup(const Key &key)
+{
+ std::unique_ptr<Value> *value = cache_map_.lookup_ptr(key);
+ if (value == nullptr) {
+ return nullptr;
+ }
+ const Key &stored_cache_key = cache_map_.lookup_key(key);
+ stored_cache_key.is_used = true;
+ return value->get();
+}
+
+SpreadsheetCache::Value &SpreadsheetCache::lookup_or_add(
+ std::unique_ptr<Key> key, FunctionRef<std::unique_ptr<Value>()> create_value)
+{
+ Value *value = this->lookup(*key);
+ if (value != nullptr) {
+ return *value;
+ }
+ std::unique_ptr<Value> new_value = create_value();
+ value = new_value.get();
+ this->add(std::move(key), std::move(new_value));
+ return *value;
+}
+
+void SpreadsheetCache::set_all_unused()
+{
+ for (std::unique_ptr<Key> &key : keys_) {
+ key->is_used = false;
+ }
+}
+
+void SpreadsheetCache::remove_all_unused()
+{
+ /* First remove the keys from the map and free the values. */
+ for (auto it = cache_map_.keys().begin(); it != cache_map_.keys().end(); ++it) {
+ const Key &key = *it;
+ if (!key.is_used) {
+ cache_map_.remove(it);
+ }
+ }
+ /* Then free the keys. */
+ for (int i = 0; i < keys_.size();) {
+ if (keys_[i]->is_used) {
+ i++;
+ }
+ else {
+ keys_.remove_and_reorder(i);
+ }
+ }
+}
+
+} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_cache.hh b/source/blender/editors/space_spreadsheet/spreadsheet_cache.hh
new file mode 100644
index 00000000000..d370bdab5c1
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_cache.hh
@@ -0,0 +1,78 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include <atomic>
+
+#include "BLI_function_ref.hh"
+#include "BLI_map.hh"
+#include "BLI_vector.hh"
+
+namespace blender::ed::spreadsheet {
+
+/**
+ * A generic cache for the spreadsheet. Different data sources can cache custom data using custom
+ * keys.
+ *
+ * Elements are removed from the cache when they are not used during a redraw.
+ */
+class SpreadsheetCache {
+ public:
+ class Key {
+ public:
+ virtual ~Key() = default;
+
+ mutable bool is_used = false;
+
+ virtual uint64_t hash() const = 0;
+
+ friend bool operator==(const Key &a, const Key &b)
+ {
+ return a.is_equal_to(b);
+ }
+
+ private:
+ virtual bool is_equal_to(const Key &other) const = 0;
+ };
+
+ class Value {
+ public:
+ virtual ~Value() = default;
+ };
+
+ private:
+ Vector<std::unique_ptr<Key>> keys_;
+ Map<std::reference_wrapper<const Key>, std::unique_ptr<Value>> cache_map_;
+
+ public:
+ /* Adding or looking up a key tags it as being used, so that it won't be removed. */
+ void add(std::unique_ptr<Key> key, std::unique_ptr<Value> value);
+ Value *lookup(const Key &key);
+ Value &lookup_or_add(std::unique_ptr<Key> key,
+ FunctionRef<std::unique_ptr<Value>()> create_value);
+
+ void set_all_unused();
+ void remove_all_unused();
+
+ template<typename T> T &lookup_or_add(std::unique_ptr<Key> key)
+ {
+ return dynamic_cast<T &>(
+ this->lookup_or_add(std::move(key), []() { return std::make_unique<T>(); }));
+ }
+};
+
+} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh b/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh
index 68370cf6a44..877651d6530 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh
@@ -97,9 +97,4 @@ std::unique_ptr<ColumnValues> column_values_from_function(const eSpreadsheetColu
return column_values;
}
-static constexpr float default_float_column_width = 3;
-static constexpr float default_float2_column_width = 2 * default_float_column_width;
-static constexpr float default_float3_column_width = 3 * default_float_column_width;
-static constexpr float default_color_column_width = 4 * default_float_column_width;
-
} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh b/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh
index 2ea7fb5809f..873735c81e5 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh
@@ -36,8 +36,12 @@ class DataSource {
* Calls the callback with all the column ids that should be displayed as long as the user does
* not manually add or remove columns. The column id can be stack allocated. Therefore, the
* callback should not keep a reference to it (and copy it instead).
+ *
+ * The `is_extra` argument indicates that this column is special and should be drawn as the first
+ * column. (This can be made a bit more generic in the future when necessary.)
*/
- virtual void foreach_default_column_ids(FunctionRef<void(const SpreadsheetColumnID &)> fn) const
+ virtual void foreach_default_column_ids(
+ FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const
{
UNUSED_VARS(fn);
}
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 78d9f61d8d5..136dc244d16 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
@@ -33,18 +33,99 @@
#include "NOD_geometry_nodes_eval_log.hh"
+#include "FN_field_cpp_type.hh"
+
#include "bmesh.h"
#include "spreadsheet_data_source_geometry.hh"
#include "spreadsheet_intern.hh"
namespace geo_log = blender::nodes::geometry_nodes_eval_log;
+using blender::fn::GField;
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(
- FunctionRef<void(const SpreadsheetColumnID &)> fn) const
+ FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const
{
+ if (component_->attribute_domain_size(domain_) == 0) {
+ return;
+ }
+
+ extra_columns_.foreach_default_column_ids(fn);
component_->attribute_foreach(
[&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
if (meta_data.domain != domain_) {
@@ -55,7 +136,7 @@ void GeometryDataSource::foreach_default_column_ids(
}
SpreadsheetColumnID column_id;
column_id.name = (char *)attribute_id.name().data();
- fn(column_id);
+ fn(column_id, false);
return true;
});
}
@@ -63,8 +144,17 @@ void GeometryDataSource::foreach_default_column_ids(
std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
const SpreadsheetColumnID &column_id) const
{
+ if (component_->attribute_domain_size(domain_) == 0) {
+ return {};
+ }
+
std::lock_guard lock{mutex_};
+ std::unique_ptr<ColumnValues> extra_column_values = extra_columns_.get_column_values(column_id);
+ if (extra_column_values) {
+ return extra_column_values;
+ }
+
bke::ReadAttributeLookup attribute = component_->attribute_try_get_for_read(column_id.name);
if (!attribute) {
return {};
@@ -104,40 +194,34 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
r_cell_value.value_bool = value;
});
case CD_PROP_FLOAT2: {
- return column_values_from_function(
- SPREADSHEET_VALUE_TYPE_FLOAT2,
- column_id.name,
- domain_size,
- [varray](int index, CellValue &r_cell_value) {
- float2 value;
- varray->get(index, &value);
- r_cell_value.value_float2 = value;
- },
- default_float2_column_width);
+ return column_values_from_function(SPREADSHEET_VALUE_TYPE_FLOAT2,
+ column_id.name,
+ domain_size,
+ [varray](int index, CellValue &r_cell_value) {
+ float2 value;
+ varray->get(index, &value);
+ r_cell_value.value_float2 = value;
+ });
}
case CD_PROP_FLOAT3: {
- return column_values_from_function(
- SPREADSHEET_VALUE_TYPE_FLOAT3,
- column_id.name,
- domain_size,
- [varray](int index, CellValue &r_cell_value) {
- float3 value;
- varray->get(index, &value);
- r_cell_value.value_float3 = value;
- },
- default_float3_column_width);
+ return column_values_from_function(SPREADSHEET_VALUE_TYPE_FLOAT3,
+ column_id.name,
+ domain_size,
+ [varray](int index, CellValue &r_cell_value) {
+ float3 value;
+ varray->get(index, &value);
+ r_cell_value.value_float3 = value;
+ });
}
case CD_PROP_COLOR: {
- return column_values_from_function(
- SPREADSHEET_VALUE_TYPE_COLOR,
- column_id.name,
- domain_size,
- [varray](int index, CellValue &r_cell_value) {
- ColorGeometry4f value;
- varray->get(index, &value);
- r_cell_value.value_color = value;
- },
- default_color_column_width);
+ return column_values_from_function(SPREADSHEET_VALUE_TYPE_COLOR,
+ column_id.name,
+ domain_size,
+ [varray](int index, CellValue &r_cell_value) {
+ ColorGeometry4f value;
+ varray->get(index, &value);
+ r_cell_value.value_color = value;
+ });
}
default:
break;
@@ -293,18 +377,20 @@ void GeometryDataSource::apply_selection_filter(MutableSpan<bool> rows_included)
}
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) {
return;
}
+ extra_columns_.foreach_default_column_ids(fn);
+
SpreadsheetColumnID column_id;
column_id.name = (char *)"Name";
- fn(column_id);
+ fn(column_id, false);
for (const char *name : {"Position", "Rotation", "Scale", "ID"}) {
column_id.name = (char *)name;
- fn(column_id);
+ fn(column_id, false);
}
}
@@ -315,6 +401,11 @@ std::unique_ptr<ColumnValues> InstancesDataSource::get_column_values(
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();
if (STREQ(column_id.name, "Name")) {
Span<int> reference_handles = component_->instance_reference_handles();
@@ -346,7 +437,6 @@ std::unique_ptr<ColumnValues> InstancesDataSource::get_column_values(
}
}
});
- values->default_width = 8.0f;
return values;
}
Span<float4x4> transforms = component_->instance_transforms();
@@ -357,28 +447,23 @@ std::unique_ptr<ColumnValues> InstancesDataSource::get_column_values(
size,
[transforms](int index, CellValue &r_cell_value) {
r_cell_value.value_float3 = transforms[index].translation();
- },
- default_float3_column_width);
+ });
}
if (STREQ(column_id.name, "Rotation")) {
- return column_values_from_function(
- SPREADSHEET_VALUE_TYPE_FLOAT3,
- column_id.name,
- size,
- [transforms](int index, CellValue &r_cell_value) {
- r_cell_value.value_float3 = transforms[index].to_euler();
- },
- default_float3_column_width);
+ return column_values_from_function(SPREADSHEET_VALUE_TYPE_FLOAT3,
+ column_id.name,
+ size,
+ [transforms](int index, CellValue &r_cell_value) {
+ r_cell_value.value_float3 = transforms[index].to_euler();
+ });
}
if (STREQ(column_id.name, "Scale")) {
- return column_values_from_function(
- SPREADSHEET_VALUE_TYPE_FLOAT3,
- column_id.name,
- size,
- [transforms](int index, CellValue &r_cell_value) {
- r_cell_value.value_float3 = transforms[index].scale();
- },
- default_float3_column_width);
+ return column_values_from_function(SPREADSHEET_VALUE_TYPE_FLOAT3,
+ column_id.name,
+ size,
+ [transforms](int index, CellValue &r_cell_value) {
+ r_cell_value.value_float3 = transforms[index].scale();
+ });
}
Span<int> ids = component_->instance_ids();
if (STREQ(column_id.name, "ID")) {
@@ -469,6 +554,38 @@ GeometrySet spreadsheet_get_display_geometry_set(const SpaceSpreadsheet *sspread
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)
{
SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
@@ -481,6 +598,69 @@ static GeometryComponentType get_display_component_type(const bContext *C, Objec
return GEO_COMPONENT_TYPE_MESH;
}
+class GeometryComponentCacheKey : public SpreadsheetCache::Key {
+ public:
+ /* Use the pointer to the geometry component as a key to detect when the geometry changed. */
+ const GeometryComponent *component;
+
+ GeometryComponentCacheKey(const GeometryComponent &component) : component(&component)
+ {
+ }
+
+ uint64_t hash() const override
+ {
+ return get_default_hash(this->component);
+ }
+
+ bool is_equal_to(const Key &other) const override
+ {
+ if (const GeometryComponentCacheKey *other_geo =
+ dynamic_cast<const GeometryComponentCacheKey *>(&other)) {
+ return this->component == other_geo->component;
+ }
+ return false;
+ }
+};
+
+class GeometryComponentCacheValue : public SpreadsheetCache::Value {
+ public:
+ /* Stores the result of fields evaluated on a geometry component. Without this, fields would have
+ * to be reevaluated on every redraw. */
+ Map<std::pair<AttributeDomain, GField>, fn::GArray<>> arrays;
+};
+
+static void add_fields_as_extra_columns(SpaceSpreadsheet *sspreadsheet,
+ const GeometryComponent &component,
+ ExtraColumns &r_extra_columns)
+{
+ Map<std::string, GField> fields_to_show;
+ find_fields_to_evaluate(sspreadsheet, fields_to_show);
+
+ GeometryComponentCacheValue &cache =
+ sspreadsheet->runtime->cache.lookup_or_add<GeometryComponentCacheValue>(
+ std::make_unique<GeometryComponentCacheKey>(component));
+
+ const AttributeDomain domain = (AttributeDomain)sspreadsheet->attribute_domain;
+ const int domain_size = component.attribute_domain_size(domain);
+ for (const auto &item : fields_to_show.items()) {
+ StringRef name = item.key;
+ const GField &field = item.value;
+
+ /* Use the cached evaluated array if it exists, otherwise evaluate the field now. */
+ fn::GArray<> &evaluated_array = cache.arrays.lookup_or_add_cb({domain, field}, [&]() {
+ fn::GArray<> evaluated_array(field.cpp_type(), domain_size);
+
+ bke::GeometryComponentFieldContext field_context{component, domain};
+ fn::FieldEvaluator field_evaluator{field_context, domain_size};
+ field_evaluator.add_with_destination(field, evaluated_array);
+ field_evaluator.evaluate();
+ return evaluated_array;
+ });
+
+ r_extra_columns.add(std::move(name), evaluated_array.as_span());
+ }
+}
+
std::unique_ptr<DataSource> data_source_from_geometry(const bContext *C, Object *object_eval)
{
SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
@@ -493,10 +673,15 @@ std::unique_ptr<DataSource> data_source_from_geometry(const bContext *C, Object
return {};
}
+ const GeometryComponent &component = *geometry_set.get_component_for_read(component_type);
+ ExtraColumns extra_columns;
+ add_fields_as_extra_columns(sspreadsheet, component, extra_columns);
+
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
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 d1b5dc6845e..6c88a94f585 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh
@@ -28,12 +28,34 @@ struct bContext;
namespace blender::ed::spreadsheet {
+/**
+ * Contains additional named columns that should be displayed that are not stored on the geometry
+ * directly. This is used for displaying the evaluated fields connected to a viewer node.
+ */
+class ExtraColumns {
+ private:
+ /** Maps column names to their data. The data is actually stored in the spreadsheet cache. */
+ Map<std::string, fn::GSpan> columns_;
+
+ public:
+ void add(std::string name, fn::GSpan data)
+ {
+ columns_.add(std::move(name), data);
+ }
+
+ void foreach_default_column_ids(
+ FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const;
+
+ std::unique_ptr<ColumnValues> get_column_values(const SpreadsheetColumnID &column_id) const;
+};
+
class GeometryDataSource : public DataSource {
private:
Object *object_eval_;
const GeometrySet geometry_set_;
const GeometryComponent *component_;
AttributeDomain domain_;
+ ExtraColumns extra_columns_;
/* Some data is computed on the fly only when it is requested. Computing it does not change the
* logical state of this data source. Therefore, the corresponding methods are const and need to
@@ -45,11 +67,13 @@ class GeometryDataSource : public DataSource {
GeometryDataSource(Object *object_eval,
GeometrySet geometry_set,
const GeometryComponentType component_type,
- const AttributeDomain domain)
+ const AttributeDomain domain,
+ ExtraColumns extra_columns)
: object_eval_(object_eval),
geometry_set_(std::move(geometry_set)),
component_(geometry_set_.get_component_for_read(component_type)),
- domain_(domain)
+ domain_(domain),
+ extra_columns_(std::move(extra_columns))
{
}
@@ -62,7 +86,7 @@ class GeometryDataSource : public DataSource {
void apply_selection_filter(MutableSpan<bool> rows_included) const;
void foreach_default_column_ids(
- FunctionRef<void(const SpreadsheetColumnID &)> fn) const override;
+ FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const override;
std::unique_ptr<ColumnValues> get_column_values(
const SpreadsheetColumnID &column_id) const override;
@@ -73,16 +97,18 @@ class GeometryDataSource : public DataSource {
class InstancesDataSource : public DataSource {
const GeometrySet geometry_set_;
const InstancesComponent *component_;
+ ExtraColumns extra_columns_;
public:
- InstancesDataSource(GeometrySet geometry_set)
+ InstancesDataSource(GeometrySet geometry_set, ExtraColumns extra_columns)
: geometry_set_(std::move(geometry_set)),
- component_(geometry_set_.get_component_for_read<InstancesComponent>())
+ component_(geometry_set_.get_component_for_read<InstancesComponent>()),
+ extra_columns_(std::move(extra_columns))
{
}
void foreach_default_column_ids(
- FunctionRef<void(const SpreadsheetColumnID &)> fn) const override;
+ FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const override;
std::unique_ptr<ColumnValues> get_column_values(
const SpreadsheetColumnID &column_id) const override;
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_intern.hh b/source/blender/editors/space_spreadsheet/spreadsheet_intern.hh
index 8be5283fd63..8b050c2e69b 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_intern.hh
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_intern.hh
@@ -17,12 +17,24 @@
#pragma once
#include "BKE_geometry_set.hh"
+#include "spreadsheet_cache.hh"
-typedef struct SpaceSpreadsheet_Runtime {
- int visible_rows;
- int tot_rows;
- int tot_columns;
-} SpaceSpreadsheet_Runtime;
+struct SpaceSpreadsheet_Runtime {
+ public:
+ int visible_rows = 0;
+ int tot_rows = 0;
+ int tot_columns = 0;
+
+ blender::ed::spreadsheet::SpreadsheetCache cache;
+
+ SpaceSpreadsheet_Runtime() = default;
+
+ /* The cache is not copied currently. */
+ SpaceSpreadsheet_Runtime(const SpaceSpreadsheet_Runtime &other)
+ : visible_rows(other.visible_rows), tot_rows(other.tot_rows), tot_columns(other.tot_columns)
+ {
+ }
+};
struct bContext;
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc
index 1a5eac53306..355899be279 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc
@@ -93,7 +93,9 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer {
const int real_index = spreadsheet_layout_.row_indices[row_index];
const ColumnValues &column = *spreadsheet_layout_.columns[column_index].values;
CellValue cell_value;
- column.get_value(real_index, cell_value);
+ if (real_index < column.size()) {
+ column.get_value(real_index, cell_value);
+ }
if (cell_value.value_int.has_value()) {
const int value = *cell_value.value_int;