2022-02-11 09:07:11 +11:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
2020-12-02 13:25:25 +01:00
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
2021-09-09 12:54:20 +02:00
|
|
|
#include "FN_field.hh"
|
2022-09-13 08:44:26 +02:00
|
|
|
#include "FN_lazy_function.hh"
|
2021-09-09 12:54:20 +02:00
|
|
|
#include "FN_multi_function_builder.hh"
|
2020-12-02 13:25:25 +01:00
|
|
|
|
2022-04-01 08:40:45 -05:00
|
|
|
#include "BKE_geometry_fields.hh"
|
2020-12-02 13:25:25 +01:00
|
|
|
#include "BKE_geometry_set.hh"
|
|
|
|
|
|
|
|
|
|
#include "DNA_node_types.h"
|
|
|
|
|
|
2021-02-16 13:06:18 -06:00
|
|
|
#include "NOD_derived_node_tree.hh"
|
2022-09-13 08:44:26 +02:00
|
|
|
#include "NOD_geometry_nodes_lazy_function.hh"
|
2021-02-16 13:06:18 -06:00
|
|
|
|
2021-02-16 17:15:08 -06:00
|
|
|
struct ModifierData;
|
2021-01-19 16:58:05 +01:00
|
|
|
|
2020-12-02 13:25:25 +01:00
|
|
|
namespace blender::nodes {
|
|
|
|
|
|
2021-09-22 19:45:47 +02:00
|
|
|
using bke::AnonymousAttributeFieldInput;
|
Geometry Nodes: new geometry attribute API
Currently, there are two attribute API. The first, defined in `BKE_attribute.h` is
accessible from RNA and C code. The second is implemented with `GeometryComponent`
and is only accessible in C++ code. The second is widely used, but only being
accessible through the `GeometrySet` API makes it awkward to use, and even impossible
for types that don't correspond directly to a geometry component like `CurvesGeometry`.
This patch adds a new attribute API, designed to replace the `GeometryComponent`
attribute API now, and to eventually replace or be the basis of the other one.
The basic idea is that there is an `AttributeAccessor` class that allows code to
interact with a set of attributes owned by some geometry. The accessor itself has
no ownership. `AttributeAccessor` is a simple type that can be passed around by
value. That makes it easy to return it from functions and to store it in containers.
For const-correctness, there is also a `MutableAttributeAccessor` that allows
changing individual and can add or remove attributes.
Currently, `AttributeAccessor` is composed of two pointers. The first is a pointer
to the owner of the attribute data. The second is a pointer to a struct with
function pointers, that is similar to a virtual function table. The functions
know how to access attributes on the owner.
The actual attribute access for geometries is still implemented with the `AttributeProvider`
pattern, which makes it easy to support different sources of attributes on a
geometry and simplifies dealing with built-in attributes.
There are different ways to get an attribute accessor for a geometry:
* `GeometryComponent.attributes()`
* `CurvesGeometry.attributes()`
* `bke::mesh_attributes(const Mesh &)`
* `bke::pointcloud_attributes(const PointCloud &)`
All of these also have a `_for_write` variant that returns a `MutabelAttributeAccessor`.
Differential Revision: https://developer.blender.org/D15280
2022-07-08 16:16:56 +02:00
|
|
|
using bke::AttributeAccessor;
|
2021-09-22 19:45:47 +02:00
|
|
|
using bke::AttributeFieldInput;
|
2021-09-09 12:54:20 +02:00
|
|
|
using bke::AttributeIDRef;
|
Geometry Nodes: new geometry attribute API
Currently, there are two attribute API. The first, defined in `BKE_attribute.h` is
accessible from RNA and C code. The second is implemented with `GeometryComponent`
and is only accessible in C++ code. The second is widely used, but only being
accessible through the `GeometrySet` API makes it awkward to use, and even impossible
for types that don't correspond directly to a geometry component like `CurvesGeometry`.
This patch adds a new attribute API, designed to replace the `GeometryComponent`
attribute API now, and to eventually replace or be the basis of the other one.
The basic idea is that there is an `AttributeAccessor` class that allows code to
interact with a set of attributes owned by some geometry. The accessor itself has
no ownership. `AttributeAccessor` is a simple type that can be passed around by
value. That makes it easy to return it from functions and to store it in containers.
For const-correctness, there is also a `MutableAttributeAccessor` that allows
changing individual and can add or remove attributes.
Currently, `AttributeAccessor` is composed of two pointers. The first is a pointer
to the owner of the attribute data. The second is a pointer to a struct with
function pointers, that is similar to a virtual function table. The functions
know how to access attributes on the owner.
The actual attribute access for geometries is still implemented with the `AttributeProvider`
pattern, which makes it easy to support different sources of attributes on a
geometry and simplifies dealing with built-in attributes.
There are different ways to get an attribute accessor for a geometry:
* `GeometryComponent.attributes()`
* `CurvesGeometry.attributes()`
* `bke::mesh_attributes(const Mesh &)`
* `bke::pointcloud_attributes(const PointCloud &)`
All of these also have a `_for_write` variant that returns a `MutabelAttributeAccessor`.
Differential Revision: https://developer.blender.org/D15280
2022-07-08 16:16:56 +02:00
|
|
|
using bke::AttributeKind;
|
|
|
|
|
using bke::AttributeMetaData;
|
|
|
|
|
using bke::AttributeReader;
|
|
|
|
|
using bke::AttributeWriter;
|
|
|
|
|
using bke::GAttributeReader;
|
|
|
|
|
using bke::GAttributeWriter;
|
|
|
|
|
using bke::GSpanAttributeWriter;
|
|
|
|
|
using bke::MutableAttributeAccessor;
|
|
|
|
|
using bke::SpanAttributeWriter;
|
2021-09-09 12:54:20 +02:00
|
|
|
using bke::StrongAnonymousAttributeID;
|
|
|
|
|
using bke::WeakAnonymousAttributeID;
|
|
|
|
|
using fn::Field;
|
2021-09-27 10:16:38 +02:00
|
|
|
using fn::FieldContext;
|
|
|
|
|
using fn::FieldEvaluator;
|
2021-09-09 12:54:20 +02:00
|
|
|
using fn::FieldInput;
|
|
|
|
|
using fn::FieldOperation;
|
|
|
|
|
using fn::GField;
|
2021-11-23 14:47:25 +01:00
|
|
|
using fn::ValueOrField;
|
2022-09-13 08:44:26 +02:00
|
|
|
using geo_eval_log::NamedAttributeUsage;
|
|
|
|
|
using geo_eval_log::NodeWarningType;
|
2021-04-27 13:03:40 +02:00
|
|
|
|
2020-12-02 13:25:25 +01:00
|
|
|
class GeoNodeExecParams {
|
|
|
|
|
private:
|
2022-09-13 08:44:26 +02:00
|
|
|
const bNode &node_;
|
|
|
|
|
lf::Params ¶ms_;
|
|
|
|
|
const lf::Context &lf_context_;
|
2020-12-02 13:25:25 +01:00
|
|
|
|
|
|
|
|
public:
|
2022-09-13 08:44:26 +02:00
|
|
|
GeoNodeExecParams(const bNode &node, lf::Params ¶ms, const lf::Context &lf_context)
|
|
|
|
|
: node_(node), params_(params), lf_context_(lf_context)
|
2020-12-02 13:25:25 +01:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-09 12:54:20 +02:00
|
|
|
template<typename T>
|
2021-12-27 16:08:11 +01:00
|
|
|
static inline constexpr bool is_field_base_type_v =
|
|
|
|
|
is_same_any_v<T, float, int, bool, ColorGeometry4f, float3, std::string>;
|
2021-09-09 12:54:20 +02:00
|
|
|
|
2020-12-02 13:25:25 +01:00
|
|
|
/**
|
|
|
|
|
* Get the input value for the input socket with the given identifier.
|
|
|
|
|
*
|
|
|
|
|
* This method can only be called once for each identifier.
|
|
|
|
|
*/
|
|
|
|
|
template<typename T> T extract_input(StringRef identifier)
|
|
|
|
|
{
|
2021-11-23 14:47:25 +01:00
|
|
|
if constexpr (is_field_base_type_v<T>) {
|
|
|
|
|
ValueOrField<T> value_or_field = this->extract_input<ValueOrField<T>>(identifier);
|
|
|
|
|
return value_or_field.as_value();
|
|
|
|
|
}
|
|
|
|
|
else if constexpr (fn::is_field_v<T>) {
|
|
|
|
|
using BaseType = typename T::base_type;
|
|
|
|
|
ValueOrField<BaseType> value_or_field = this->extract_input<ValueOrField<BaseType>>(
|
|
|
|
|
identifier);
|
|
|
|
|
return value_or_field.as_field();
|
2021-09-09 12:54:20 +02:00
|
|
|
}
|
|
|
|
|
else {
|
2020-12-02 13:25:25 +01:00
|
|
|
#ifdef DEBUG
|
2021-09-09 12:54:20 +02:00
|
|
|
this->check_input_access(identifier, &CPPType::get<T>());
|
2020-12-02 13:25:25 +01:00
|
|
|
#endif
|
2022-09-13 08:44:26 +02:00
|
|
|
const int index = this->get_input_index(identifier);
|
|
|
|
|
T value = params_.extract_input<T>(index);
|
2021-10-26 20:00:03 +02:00
|
|
|
if constexpr (std::is_same_v<T, GeometrySet>) {
|
|
|
|
|
this->check_input_geometry_set(identifier, value);
|
|
|
|
|
}
|
|
|
|
|
return value;
|
2021-09-09 12:54:20 +02:00
|
|
|
}
|
2020-12-02 13:25:25 +01:00
|
|
|
}
|
|
|
|
|
|
2021-10-26 20:00:03 +02:00
|
|
|
void check_input_geometry_set(StringRef identifier, const GeometrySet &geometry_set) const;
|
2022-07-22 15:39:41 +02:00
|
|
|
void check_output_geometry_set(const GeometrySet &geometry_set) const;
|
2021-10-26 20:00:03 +02:00
|
|
|
|
2020-12-02 13:25:25 +01:00
|
|
|
/**
|
|
|
|
|
* Get the input value for the input socket with the given identifier.
|
|
|
|
|
*/
|
2021-11-23 14:47:25 +01:00
|
|
|
template<typename T> T get_input(StringRef identifier) const
|
2020-12-02 13:25:25 +01:00
|
|
|
{
|
2021-11-23 14:47:25 +01:00
|
|
|
if constexpr (is_field_base_type_v<T>) {
|
|
|
|
|
ValueOrField<T> value_or_field = this->get_input<ValueOrField<T>>(identifier);
|
|
|
|
|
return value_or_field.as_value();
|
|
|
|
|
}
|
|
|
|
|
else if constexpr (fn::is_field_v<T>) {
|
|
|
|
|
using BaseType = typename T::base_type;
|
|
|
|
|
ValueOrField<BaseType> value_or_field = this->get_input<ValueOrField<BaseType>>(identifier);
|
|
|
|
|
return value_or_field.as_field();
|
2021-09-09 12:54:20 +02:00
|
|
|
}
|
|
|
|
|
else {
|
2020-12-02 13:25:25 +01:00
|
|
|
#ifdef DEBUG
|
2021-09-09 12:54:20 +02:00
|
|
|
this->check_input_access(identifier, &CPPType::get<T>());
|
2020-12-18 13:28:43 +01:00
|
|
|
#endif
|
2022-09-13 08:44:26 +02:00
|
|
|
const int index = this->get_input_index(identifier);
|
|
|
|
|
const T &value = params_.get_input<T>(index);
|
2021-10-26 20:00:03 +02:00
|
|
|
if constexpr (std::is_same_v<T, GeometrySet>) {
|
|
|
|
|
this->check_input_geometry_set(identifier, value);
|
|
|
|
|
}
|
|
|
|
|
return value;
|
2021-09-09 12:54:20 +02:00
|
|
|
}
|
2020-12-18 13:28:43 +01:00
|
|
|
}
|
|
|
|
|
|
2020-12-02 13:25:25 +01:00
|
|
|
/**
|
|
|
|
|
* Store the output value for the given socket identifier.
|
|
|
|
|
*/
|
|
|
|
|
template<typename T> void set_output(StringRef identifier, T &&value)
|
|
|
|
|
{
|
2021-04-27 13:03:40 +02:00
|
|
|
using StoredT = std::decay_t<T>;
|
2021-11-23 14:47:25 +01:00
|
|
|
if constexpr (is_field_base_type_v<StoredT>) {
|
|
|
|
|
this->set_output(identifier, ValueOrField<StoredT>(std::forward<T>(value)));
|
|
|
|
|
}
|
|
|
|
|
else if constexpr (fn::is_field_v<StoredT>) {
|
|
|
|
|
using BaseType = typename StoredT::base_type;
|
|
|
|
|
this->set_output(identifier, ValueOrField<BaseType>(std::forward<T>(value)));
|
2021-09-09 12:54:20 +02:00
|
|
|
}
|
|
|
|
|
else {
|
2020-12-02 13:25:25 +01:00
|
|
|
#ifdef DEBUG
|
2022-09-13 08:44:26 +02:00
|
|
|
const CPPType &type = CPPType::get<StoredT>();
|
2021-09-09 12:54:20 +02:00
|
|
|
this->check_output_access(identifier, type);
|
2020-12-02 13:25:25 +01:00
|
|
|
#endif
|
2022-07-22 15:39:41 +02:00
|
|
|
if constexpr (std::is_same_v<StoredT, GeometrySet>) {
|
|
|
|
|
this->check_output_geometry_set(value);
|
|
|
|
|
}
|
2022-09-13 08:44:26 +02:00
|
|
|
const int index = this->get_output_index(identifier);
|
|
|
|
|
params_.set_output(index, std::forward<T>(value));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
geo_eval_log::GeoTreeLogger *get_local_tree_logger() const
|
|
|
|
|
{
|
|
|
|
|
GeoNodesLFUserData *user_data = this->user_data();
|
|
|
|
|
BLI_assert(user_data != nullptr);
|
|
|
|
|
const ComputeContext *compute_context = user_data->compute_context;
|
|
|
|
|
BLI_assert(compute_context != nullptr);
|
|
|
|
|
if (user_data->modifier_data->eval_log == nullptr) {
|
|
|
|
|
return nullptr;
|
2021-09-09 12:54:20 +02:00
|
|
|
}
|
2022-09-13 08:44:26 +02:00
|
|
|
return &user_data->modifier_data->eval_log->get_local_tree_logger(*compute_context);
|
2021-05-20 11:34:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Tell the evaluator that a specific input won't be used anymore.
|
|
|
|
|
*/
|
|
|
|
|
void set_input_unused(StringRef identifier)
|
|
|
|
|
{
|
2022-09-13 08:44:26 +02:00
|
|
|
const int index = this->get_input_index(identifier);
|
|
|
|
|
params_.set_input_unused(index);
|
2021-05-20 11:34:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns true when the output has to be computed.
|
2021-05-25 18:25:55 +10:00
|
|
|
* Nodes that support laziness could use the #lazy_output_is_required variant to possibly avoid
|
2021-05-20 11:34:47 +02:00
|
|
|
* some computations.
|
|
|
|
|
*/
|
|
|
|
|
bool output_is_required(StringRef identifier) const
|
|
|
|
|
{
|
2022-09-13 08:44:26 +02:00
|
|
|
const int index = this->get_output_index(identifier);
|
|
|
|
|
return params_.get_output_usage(index) != lf::ValueUsage::Unused;
|
2021-05-20 11:34:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Tell the evaluator that a specific input is required.
|
|
|
|
|
* This returns true when the input will only be available in the next execution.
|
|
|
|
|
* False is returned if the input is available already.
|
2021-05-25 18:25:55 +10:00
|
|
|
* This can only be used when the node supports laziness.
|
2021-05-20 11:34:47 +02:00
|
|
|
*/
|
|
|
|
|
bool lazy_require_input(StringRef identifier)
|
|
|
|
|
{
|
2022-09-13 08:44:26 +02:00
|
|
|
const int index = this->get_input_index(identifier);
|
|
|
|
|
return params_.try_get_input_data_ptr_or_request(index) == nullptr;
|
2021-05-20 11:34:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Asks the evaluator if a specific output is required right now. If this returns false, the
|
|
|
|
|
* value might still need to be computed later.
|
2021-05-25 18:25:55 +10:00
|
|
|
* This can only be used when the node supports laziness.
|
2021-05-20 11:34:47 +02:00
|
|
|
*/
|
|
|
|
|
bool lazy_output_is_required(StringRef identifier)
|
|
|
|
|
{
|
2022-09-13 08:44:26 +02:00
|
|
|
const int index = this->get_output_index(identifier);
|
|
|
|
|
return params_.get_output_usage(index) == lf::ValueUsage::Used;
|
2020-12-02 13:25:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the node that is currently being executed.
|
|
|
|
|
*/
|
|
|
|
|
const bNode &node() const
|
|
|
|
|
{
|
2022-09-13 08:44:26 +02:00
|
|
|
return node_;
|
2020-12-02 13:25:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const Object *self_object() const
|
|
|
|
|
{
|
2022-09-13 08:44:26 +02:00
|
|
|
if (const auto *data = this->user_data()) {
|
|
|
|
|
if (data->modifier_data) {
|
|
|
|
|
return data->modifier_data->self_object;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
2020-12-02 13:25:25 +01:00
|
|
|
}
|
|
|
|
|
|
2021-01-19 16:58:05 +01:00
|
|
|
Depsgraph *depsgraph() const
|
|
|
|
|
{
|
2022-09-13 08:44:26 +02:00
|
|
|
if (const auto *data = this->user_data()) {
|
|
|
|
|
if (data->modifier_data) {
|
|
|
|
|
return data->modifier_data->depsgraph;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GeoNodesLFUserData *user_data() const
|
|
|
|
|
{
|
|
|
|
|
return dynamic_cast<GeoNodesLFUserData *>(lf_context_.user_data);
|
2021-01-19 16:58:05 +01:00
|
|
|
}
|
|
|
|
|
|
2021-02-16 17:15:08 -06:00
|
|
|
/**
|
|
|
|
|
* Add an error message displayed at the top of the node when displaying the node tree,
|
|
|
|
|
* and potentially elsewhere in Blender.
|
|
|
|
|
*/
|
2022-09-17 12:08:43 +02:00
|
|
|
void error_message_add(const NodeWarningType type, StringRef message) const;
|
2021-02-16 17:15:08 -06:00
|
|
|
|
2021-10-26 15:32:01 +02:00
|
|
|
std::string attribute_producer_name() const;
|
|
|
|
|
|
2021-11-26 18:00:52 +01:00
|
|
|
void set_default_remaining_outputs();
|
|
|
|
|
|
2022-09-17 12:08:43 +02:00
|
|
|
void used_named_attribute(StringRef attribute_name, NamedAttributeUsage usage);
|
2022-04-14 16:31:09 +02:00
|
|
|
|
2020-12-02 13:25:25 +01:00
|
|
|
private:
|
|
|
|
|
/* Utilities for detecting common errors at when using this class. */
|
2021-04-27 13:03:40 +02:00
|
|
|
void check_input_access(StringRef identifier, const CPPType *requested_type = nullptr) const;
|
|
|
|
|
void check_output_access(StringRef identifier, const CPPType &value_type) const;
|
2020-12-14 11:43:46 -06:00
|
|
|
|
2021-06-28 15:44:12 +10:00
|
|
|
/* Find the active socket with the input name (not the identifier). */
|
2020-12-14 11:43:46 -06:00
|
|
|
const bNodeSocket *find_available_socket(const StringRef name) const;
|
2022-09-13 08:44:26 +02:00
|
|
|
|
|
|
|
|
int get_input_index(const StringRef identifier) const
|
|
|
|
|
{
|
|
|
|
|
int counter = 0;
|
|
|
|
|
for (const bNodeSocket *socket : node_.input_sockets()) {
|
|
|
|
|
if (!socket->is_available()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (socket->identifier == identifier) {
|
|
|
|
|
return counter;
|
|
|
|
|
}
|
|
|
|
|
counter++;
|
|
|
|
|
}
|
|
|
|
|
BLI_assert_unreachable();
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int get_output_index(const StringRef identifier) const
|
|
|
|
|
{
|
|
|
|
|
int counter = 0;
|
|
|
|
|
for (const bNodeSocket *socket : node_.output_sockets()) {
|
|
|
|
|
if (!socket->is_available()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (socket->identifier == identifier) {
|
|
|
|
|
return counter;
|
|
|
|
|
}
|
|
|
|
|
counter++;
|
|
|
|
|
}
|
|
|
|
|
BLI_assert_unreachable();
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2020-12-02 13:25:25 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
} // namespace blender::nodes
|