Blender V4.3
node_geo_set_material.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
6
7#include "UI_resources.hh"
8
9#include "DNA_curves_types.h"
11#include "DNA_volume_types.h"
12
13#include "BKE_curves.hh"
14#include "BKE_grease_pencil.hh"
15#include "BKE_material.h"
16#include "BKE_mesh.hh"
17
19
21{
22 b.add_input<decl::Geometry>("Geometry")
23 .supported_type({GeometryComponent::Type::Mesh,
28 b.add_input<decl::Bool>("Selection").default_value(true).hide_value().field_on_all();
29 b.add_input<decl::Material>("Material").hide_label();
30 b.add_output<decl::Geometry>("Geometry").propagate_all();
31}
32
34 const fn::FieldContext &field_context,
35 const Field<bool> &selection_field,
36 MutableAttributeAccessor &attributes,
37 const AttrDomain domain,
38 Material *material)
39{
40 const int domain_size = attributes.domain_size(domain);
41 fn::FieldEvaluator selection_evaluator{field_context, domain_size};
42 selection_evaluator.set_selection(selection_field);
43 selection_evaluator.evaluate();
44 const IndexMask selection = selection_evaluator.get_evaluated_selection_as_mask();
45
46 if (selection.size() != attributes.domain_size(domain)) {
47 /* If the entire geometry isn't selected, and there is no material slot yet, add an empty
48 * slot so that the faces that aren't selected can still refer to the default material. */
50 }
51
52 int new_index = -1;
53 const int orig_materials_num = *BKE_id_material_len_p(id);
54 if (Material **materials = *BKE_id_material_array_p(id)) {
55 new_index = Span(materials, orig_materials_num).first_index_try(material);
56 }
57
58 if (new_index == -1) {
59 /* Append a new material index. */
60 new_index = orig_materials_num;
61 BKE_id_material_eval_assign(id, new_index + 1, material);
62 }
63
64 SpanAttributeWriter<int> indices = attributes.lookup_or_add_for_write_span<int>("material_index",
65 domain);
66 index_mask::masked_fill(indices.span, new_index, selection);
67 indices.finish();
68}
69
71{
72 Material *material = params.extract_input<Material *>("Material");
73 const Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
74
75 GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
76
77 /* Only add the warnings once, even if there are many unique instances. */
78 bool no_faces_warning = false;
79 bool point_selection_warning = false;
80 bool volume_selection_warning = false;
81 bool curves_selection_warning = false;
82
83 geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
84 if (Mesh *mesh = geometry_set.get_mesh_for_write()) {
85 if (mesh->faces_num == 0) {
86 if (mesh->verts_num > 0) {
87 no_faces_warning = true;
88 }
89 }
90 else {
91 const bke::MeshFieldContext field_context{*mesh, AttrDomain::Face};
92 MutableAttributeAccessor attributes = mesh->attributes_for_write();
94 &mesh->id, field_context, selection_field, attributes, AttrDomain::Face, material);
95 }
96 }
97 if (Volume *volume = geometry_set.get_volume_for_write()) {
98 BKE_id_material_eval_assign(&volume->id, 1, material);
99 if (selection_field.node().depends_on_input()) {
100 volume_selection_warning = true;
101 }
102 }
103 if (PointCloud *pointcloud = geometry_set.get_pointcloud_for_write()) {
104 BKE_id_material_eval_assign(&pointcloud->id, 1, material);
105 if (selection_field.node().depends_on_input()) {
106 point_selection_warning = true;
107 }
108 }
109 if (Curves *curves = geometry_set.get_curves_for_write()) {
110 BKE_id_material_eval_assign(&curves->id, 1, material);
111 if (selection_field.node().depends_on_input()) {
112 curves_selection_warning = true;
113 }
114 }
115 if (GreasePencil *grease_pencil = geometry_set.get_grease_pencil_for_write()) {
116 using namespace blender::bke::greasepencil;
117 Vector<Mesh *> mesh_by_layer(grease_pencil->layers().size(), nullptr);
118 for (const int layer_index : grease_pencil->layers().index_range()) {
119 Drawing *drawing = grease_pencil->get_eval_drawing(grease_pencil->layer(layer_index));
120 if (drawing == nullptr) {
121 continue;
122 }
123 bke::CurvesGeometry &curves = drawing->strokes_for_write();
124 if (curves.curves_num() == 0) {
125 continue;
126 }
127
128 const bke::GreasePencilLayerFieldContext field_context{
129 *grease_pencil, AttrDomain::Curve, layer_index};
130 MutableAttributeAccessor attributes = curves.attributes_for_write();
131 assign_material_to_id_geometry(&grease_pencil->id,
132 field_context,
133 selection_field,
134 attributes,
135 AttrDomain::Curve,
136 material);
137 }
138 }
139 });
140
141 if (no_faces_warning) {
142 params.error_message_add(NodeWarningType::Info,
143 TIP_("Mesh has no faces for material assignment"));
144 }
145 if (volume_selection_warning) {
146 params.error_message_add(
147 NodeWarningType::Info,
148 TIP_("Volumes only support a single material; selection input cannot be a field"));
149 }
150 if (point_selection_warning) {
151 params.error_message_add(
152 NodeWarningType::Info,
153 TIP_("Point clouds only support a single material; selection input cannot be a field"));
154 }
155 if (curves_selection_warning) {
156 params.error_message_add(
157 NodeWarningType::Info,
158 TIP_("Curves only support a single material; selection input cannot be a field"));
159 }
160
161 params.set_output("Geometry", std::move(geometry_set));
162}
163
164static void node_register()
165{
166 static blender::bke::bNodeType ntype;
167
168 geo_node_type_base(&ntype, GEO_NODE_SET_MATERIAL, "Set Material", NODE_CLASS_GEOMETRY);
169 ntype.declare = node_declare;
172}
174
175} // namespace blender::nodes::node_geo_set_material_cc
Low-level operations for curves.
Low-level operations for grease pencil.
General operations, lookup, etc. for materials.
struct Material *** BKE_id_material_array_p(struct ID *id)
short * BKE_id_material_len_p(struct ID *id)
void BKE_id_material_eval_ensure_default_slot(struct ID *id)
void BKE_id_material_eval_assign(struct ID *id, int slot, struct Material *material)
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:418
#define TIP_(msgid)
#define NOD_REGISTER_NODE(REGISTER_FUNC)
constexpr int64_t first_index_try(const T &search_value) const
Definition BLI_span.hh:388
bke::CurvesGeometry & strokes_for_write()
local_group_size(16, 16) .push_constant(Type b
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void node_register_type(bNodeType *ntype)
Definition node.cc:1708
void masked_fill(MutableSpan< T > data, const T &value, const IndexMask &mask)
static void assign_material_to_id_geometry(ID *id, const fn::FieldContext &field_context, const Field< bool > &selection_field, MutableAttributeAccessor &attributes, const AttrDomain domain, Material *material)
static void node_geo_exec(GeoNodeExecParams params)
static void node_declare(NodeDeclarationBuilder &b)
void geo_node_type_base(blender::bke::bNodeType *ntype, int type, const char *name, short nclass)
Definition DNA_ID.h:413
Defines a node type.
Definition BKE_node.hh:218
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:339
NodeDeclareFunction declare
Definition BKE_node.hh:347