34 b.use_custom_socket_order();
35 b.allow_any_socket_order();
36 b.add_default_layout();
37 const bNode *node =
b.node_or_null();
39 if (node !=
nullptr) {
41 b.add_input(data_type,
"Value").supports_field().hide_value().is_default_link_socket();
42 b.add_output(data_type,
"Value").field_source_reference_all().align_with_previous();
47 .
description(
"How many times to blur the values for all elements");
54 .description(
"Relative mix weight of neighboring elements");
77 if (!new_node_type.has_value()) {
98 params.update_and_connect_available_socket(node,
"Value");
110 for (
const int vert : range) {
127 edges, verts_num, vert_to_edge_offset_data, vert_to_edge_indices);
132 for (const int edge_i : range) {
133 const int2 edge = edges[edge_i];
134 r_offsets[edge_i] = vert_to_edge_offsets[edge[0]].size() - 1 +
135 vert_to_edge_offsets[edge[1]].size() - 1;
142 for (const int edge_i : range) {
143 const int2 edge = edges[edge_i];
144 MutableSpan<int> neighbors = r_indices.as_mutable_span().slice(offsets[edge_i]);
146 for (const Span<int> neighbor_edges : {vert_to_edge[edge[0]], vert_to_edge[edge[1]]}) {
147 for (const int neighbor_edge : neighbor_edges) {
148 if (neighbor_edge != edge_i) {
149 neighbors[count] = neighbor_edge;
167 faces, corner_edges, edges_num, edge_to_face_offset_data, edge_to_face_indices);
172 for (const int face_i : range) {
173 for (const int edge : corner_edges.slice(faces[face_i])) {
175 r_offsets[face_i] += edge_to_face_offsets[edge].size() - 1;
183 for (const int face_i : range) {
184 MutableSpan<int> neighbors = r_indices.as_mutable_span().slice(offsets[face_i]);
185 if (neighbors.is_empty()) {
189 for (const int edge : corner_edges.slice(faces[face_i])) {
190 for (const int neighbor : edge_to_face_map[edge]) {
191 if (neighbor != face_i) {
192 neighbors[count] = neighbor;
207 case AttrDomain::Point:
210 case AttrDomain::Edge:
213 case AttrDomain::Face:
215 mesh.faces(), mesh.corner_edges(), mesh.
edges_num, r_offsets, r_indices);
227 const int iterations,
240 for (const int64_t index : range) {
241 const Span<int> neighbors = neighbors_map[index];
242 const float neighbor_weight = neighbor_weights[index];
243 mixer.set(index, src[index], 1.0f);
244 for (const int neighbor : neighbors) {
245 mixer.mix_in(index, src[neighbor], neighbor_weight);
248 mixer.finalize(range);
258 using T =
typename decltype(type_tag)::type;
259 if constexpr (!std::is_same_v<T, void>) {
270 const int iterations,
278 mesh, domain, neighbor_offsets, neighbor_indices);
282 using T = decltype(dummy);
283 result_buffer = blur_on_mesh_exec<T>(
284 neighbor_weights, neighbors_map, iterations, buffer_a.typed<T>(), buffer_b.typed<T>());
286 return result_buffer;
292 const int iterations,
302 for ([[maybe_unused]]
const int iteration :
IndexRange(iterations)) {
306 for (const int curve_i : range) {
307 const IndexRange points = points_by_curve[curve_i];
308 if (points.size() == 1) {
310 const int point_i = points[0];
311 mixer.set(point_i, src[point_i], 1.0f);
315 for (const int point_i : points.drop_front(1).drop_back(1)) {
316 const float neighbor_weight = neighbor_weights[point_i];
317 mixer.set(point_i, src[point_i], 1.0f);
318 mixer.mix_in(point_i, src[point_i - 1], neighbor_weight);
319 mixer.mix_in(point_i, src[point_i + 1], neighbor_weight);
321 const int first_i = points[0];
322 const float first_neighbor_weight = neighbor_weights[first_i];
323 const int last_i = points.last();
324 const float last_neighbor_weight = neighbor_weights[last_i];
327 mixer.set(first_i, src[first_i], 1.0f);
328 mixer.mix_in(first_i, src[first_i + 1], first_neighbor_weight);
330 mixer.set(last_i, src[last_i], 1.0f);
331 mixer.mix_in(last_i, src[last_i - 1], last_neighbor_weight);
333 if (cyclic[curve_i]) {
335 mixer.mix_in(first_i, src[last_i], first_neighbor_weight);
337 mixer.mix_in(last_i, src[first_i], last_neighbor_weight);
340 mixer.finalize(points_by_curve[range]);
348 const int iterations,
355 using T = decltype(dummy);
356 result_buffer = blur_on_curve_exec<T>(
357 curves, neighbor_weights, iterations, buffer_a.typed<T>(), buffer_b.typed<T>());
359 return result_buffer;
365 const GField value_field_;
366 const int iterations_;
370 :
bke::GeometryFieldInput(value_field.
cpp_type(),
"Blur Attribute"),
371 weight_field_(std::move(weight_field)),
372 value_field_(std::move(value_field)),
373 iterations_(iterations)
380 const int64_t domain_size = context.attributes()->domain_size(context.domain());
387 evaluator.
add(weight_field_);
391 if (domain_size <= 1) {
395 if (iterations_ <= 0) {
403 switch (context.type()) {
404 case GeometryComponent::Type::Mesh:
405 if (
ELEM(context.domain(), AttrDomain::Point, AttrDomain::Edge, AttrDomain::Face)) {
406 if (
const Mesh *mesh = context.mesh()) {
408 *mesh, context.domain(), iterations_, neighbor_weights, buffer_a, buffer_b);
412 case GeometryComponent::Type::Curve:
413 case GeometryComponent::Type::GreasePencil:
414 if (context.domain() == AttrDomain::Point) {
417 *curves, iterations_, neighbor_weights, buffer_a, buffer_b);
426 if (result_buffer.
data() == buffer_a.
data()) {
434 weight_field_.node().for_each_field_input_recursive(
fn);
435 value_field_.node().for_each_field_input_recursive(
fn);
448 return weight_field_ == other_blur->weight_field_ &&
449 value_field_ == other_blur->value_field_ && iterations_ == other_blur->iterations_;
457 if (domain.has_value() && *domain == AttrDomain::Corner) {
458 return AttrDomain::Point;
466 const int iterations =
params.extract_input<
int>(
"Iterations");
470 GField output_field{std::make_shared<BlurAttributeFieldInput>(
471 std::move(weight_field), std::move(value_field), iterations)};
472 params.set_output<
GField>(
"Value", std::move(output_field));
497 ntype.
ui_name =
"Blur Attribute";
498 ntype.
ui_description =
"Mix attribute values of neighboring elements";
Low-level operations for curves.
#define NODE_CLASS_ATTRIBUTE
#define GEO_NODE_BLUR_ATTRIBUTE
#define BLI_assert_unreachable()
#define NOD_REGISTER_NODE(REGISTER_FUNC)
#define NOD_inline_enum_accessors(member)
unsigned long long int uint64_t
MutableSpan< T > as_mutable_span()
void to_static_type_tag(const Fn &fn) const
GMutableSpan as_mutable_span()
const void * data() const
const CPPType & type() const
const void * data() const
static GVArray from_garray(GArray<> array)
constexpr IndexRange index_range() const
constexpr int64_t size() const
constexpr IndexRange index_range() const
OffsetIndices< int > points_by_curve() const
IndexRange curves_range() const
VArray< bool > cyclic() const
int add(GField field, GVArray *varray_ptr)
int add_with_destination(GField field, GVMutableArray dst)
const GVArray & get_evaluated(const int field_index) const
Vector< SocketDeclaration * > inputs
typename DefaultMixerStruct< T >::type DefaultMixer
int edge_other_vert(const int2 edge, const int vert)
GroupedSpan< int > build_edge_to_face_map(OffsetIndices< int > faces, Span< int > corner_edges, int edges_num, Array< int > &r_offsets, Array< int > &r_indices)
GroupedSpan< int > build_vert_to_edge_map(Span< int2 > edges, int verts_num, Array< int > &r_offsets, Array< int > &r_indices)
std::optional< AttrDomain > try_detect_field_domain(const GeometryComponent &component, const fn::GField &field)
void node_register_type(bNodeType &ntype)
std::optional< eCustomDataType > socket_type_to_custom_data_type(eNodeSocketDatatype type)
static void node_geo_exec(GeoNodeExecParams params)
static Span< T > blur_on_curve_exec(const bke::CurvesGeometry &curves, const Span< float > neighbor_weights, const int iterations, const MutableSpan< T > buffer_a, const MutableSpan< T > buffer_b)
static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms)
static void build_vert_to_vert_by_edge_map(const Span< int2 > edges, const int verts_num, Array< int > &r_offsets, Array< int > &r_indices)
static void to_static_type_for_blur(const CPPType &type, const Func &func)
static void node_rna(StructRNA *srna)
static GSpan blur_on_mesh(const Mesh &mesh, const AttrDomain domain, const int iterations, const Span< float > neighbor_weights, const GMutableSpan buffer_a, const GMutableSpan buffer_b)
static void node_register()
static GSpan blur_on_curves(const bke::CurvesGeometry &curves, const int iterations, const Span< float > neighbor_weights, const GMutableSpan buffer_a, const GMutableSpan buffer_b)
static GroupedSpan< int > create_mesh_map(const Mesh &mesh, const AttrDomain domain, Array< int > &r_offsets, Array< int > &r_indices)
static void node_layout(uiLayout *layout, bContext *, PointerRNA *ptr)
static void build_face_to_face_by_edge_map(const OffsetIndices< int > faces, const Span< int > corner_edges, const int edges_num, Array< int > &r_offsets, Array< int > &r_indices)
static void build_edge_to_edge_by_vert_map(const Span< int2 > edges, const int verts_num, Array< int > &r_offsets, Array< int > &r_indices)
static void node_init(bNodeTree *, bNode *node)
static void node_declare(NodeDeclarationBuilder &b)
static Span< T > blur_on_mesh_exec(const Span< float > neighbor_weights, const GroupedSpan< int > neighbors_map, const int iterations, const MutableSpan< T > buffer_a, const MutableSpan< T > buffer_b)
void search_link_ops_for_declarations(GatherLinkSearchOpParams ¶ms, Span< SocketDeclaration * > declarations)
PropertyRNA * RNA_def_node_enum(StructRNA *srna, const char *identifier, const char *ui_name, const char *ui_description, const EnumPropertyItem *static_items, const EnumRNAAccessors accessors, std::optional< int > default_value, const EnumPropertyItemFunc item_func, const bool allow_animation)
const EnumPropertyItem * enum_items_filter(const EnumPropertyItem *original_item_array, FunctionRef< bool(const EnumPropertyItem &item)> fn)
OffsetIndices< int > accumulate_counts_to_offsets(MutableSpan< int > counts_to_offsets, int start_offset=0)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
uint64_t get_default_hash(const T &v, const Args &...args)
ColorSceneLinear4f< eAlpha::Premultiplied > ColorGeometry4f
VecBase< float, 3 > float3
void geo_node_type_base(blender::bke::bNodeType *ntype, std::string idname, const std::optional< int16_t > legacy_type)
const EnumPropertyItem rna_enum_attribute_type_items[]
blender::nodes::NodeDeclaration * static_declaration
std::string ui_description
void(* initfunc)(bNodeTree *ntree, bNode *node)
NodeGeometryExecFunction geometry_node_execute
const char * enum_name_legacy
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
NodeGatherSocketLinkOperationsFunction gather_link_search_ops
NodeDeclareFunction declare
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)