Blender V4.3
node_geo_curves_to_grease_pencil.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "BKE_curves.hh"
7#include "BKE_instances.hh"
8
10
12
14{
15 b.add_input<decl::Geometry>("Curves").description("Either plain curves or curve instances");
16 b.add_input<decl::Bool>("Selection")
17 .default_value(true)
18 .hide_value()
19 .field_on_all()
20 .description("Either a curve or instance selection");
21 b.add_input<decl::Bool>("Instances as Layers")
22 .default_value(true)
23 .description("Create a separate layer for each instance");
24 b.add_output<decl::Geometry>("Grease Pencil").propagate_all();
25}
26
28 const Curves &curves_id,
29 const Field<bool> &selection_field,
30 const StringRefNull layer_name,
31 const AttributeFilter &attribute_filter)
32{
33 bke::CurvesGeometry curves = curves_id.geometry.wrap();
34
35 const bke::CurvesFieldContext field_context{curves_id, AttrDomain::Curve};
36 FieldEvaluator evaluator{field_context, curves.curves_num()};
37 evaluator.set_selection(selection_field);
38 evaluator.evaluate();
39 const IndexMask curves_selection = evaluator.get_evaluated_selection_as_mask();
40 IndexMaskMemory memory;
41 const IndexMask curves_to_delete = curves_selection.complement(curves.curves_range(), memory);
42 curves.remove_curves(curves_to_delete, attribute_filter);
43
45 grease_pencil->add_layers_with_empty_drawings_for_eval(1);
46 bke::greasepencil::Layer &layer = grease_pencil->layer(0);
47 layer.set_name(layer_name);
48 bke::greasepencil::Drawing &drawing = *grease_pencil->get_eval_drawing(layer);
49 drawing.strokes_for_write() = std::move(curves);
50
51 /* Transfer materials. */
52 const int materials_num = curves_id.totcol;
53 grease_pencil->material_array_num = materials_num;
54 grease_pencil->material_array = MEM_cnew_array<Material *>(materials_num, __func__);
55 initialized_copy_n(curves_id.mat, materials_num, grease_pencil->material_array);
56
57 return grease_pencil;
58}
59
61 const bke::Instances &instances,
62 const Field<bool> &selection_field,
63 const AttributeFilter &attribute_filter)
64{
65 const Span<int> reference_handles = instances.reference_handles();
66 const Span<bke::InstanceReference> references = instances.references();
67 const Span<float4x4> transforms = instances.transforms();
68
69 const int instances_num = instances.instances_num();
70 if (instances_num == 0) {
71 return nullptr;
72 }
73
74 const bke::InstancesFieldContext field_context{instances};
75 FieldEvaluator evaluator{field_context, instances_num};
76 evaluator.set_selection(selection_field);
77 evaluator.evaluate();
78 const IndexMask instance_selection = evaluator.get_evaluated_selection_as_mask();
79
80 const int layer_num = instance_selection.size();
81 if (layer_num == 0) {
82 return nullptr;
83 }
84
86
87 VectorSet<Material *> all_materials;
88 grease_pencil->add_layers_with_empty_drawings_for_eval(layer_num);
89 instance_selection.foreach_index([&](const int instance_i) {
90 const bke::InstanceReference &reference = references[reference_handles[instance_i]];
91
92 bke::greasepencil::Layer &layer = grease_pencil->layer(instance_i);
93 bke::greasepencil::Drawing &drawing = *grease_pencil->get_eval_drawing(layer);
94 layer.set_name(reference.name());
95 layer.set_local_transform(transforms[instance_i]);
96
97 GeometrySet instance_geometry;
98 reference.to_geometry_set(instance_geometry);
99 const Curves *instance_curves = instance_geometry.get_curves();
100 if (!instance_curves) {
101 return;
102 }
103
104 bke::CurvesGeometry &strokes = drawing.strokes_for_write();
105 strokes = instance_curves->geometry.wrap();
106
107 Vector<int> new_material_indices;
108 for (Material *material : Span{instance_curves->mat, instance_curves->totcol}) {
109 new_material_indices.append(all_materials.index_of_or_add(material));
110 }
111
112 /* Remap material indices. */
113 bke::SpanAttributeWriter<int> material_indices =
114 strokes.attributes_for_write().lookup_or_add_for_write_span<int>("material_index",
116 for (int &material_index : material_indices.span) {
117 if (material_index >= 0 && material_index < new_material_indices.size()) {
118 material_index = new_material_indices[material_index];
119 }
120 }
121 material_indices.finish();
122 });
123
124 grease_pencil->material_array_num = all_materials.size();
125 grease_pencil->material_array = MEM_cnew_array<Material *>(all_materials.size(), __func__);
126 initialized_copy_n(all_materials.data(), all_materials.size(), grease_pencil->material_array);
127
128 const bke::AttributeAccessor instances_attributes = instances.attributes();
129 bke::MutableAttributeAccessor grease_pencil_attributes = grease_pencil->attributes_for_write();
130 instances_attributes.foreach_attribute([&](const AttributeIter &iter) {
131 if (iter.is_builtin && !grease_pencil_attributes.is_builtin(iter.name)) {
132 return;
133 }
134 if (iter.data_type == CD_PROP_STRING) {
135 return;
136 }
137 if (ELEM(iter.name, "opacity")) {
138 return;
139 }
140 if (attribute_filter.allow_skip(iter.name)) {
141 return;
142 }
143 const GAttributeReader src_attribute = iter.get();
144 if (instance_selection.size() == instances_num && src_attribute.varray.is_span() &&
145 src_attribute.sharing_info)
146 {
147 /* Try reusing existing attribute array. */
148 grease_pencil_attributes.add(
149 iter.name,
150 AttrDomain::Layer,
151 iter.data_type,
152 bke::AttributeInitShared{src_attribute.varray.get_internal_span().data(),
153 *src_attribute.sharing_info});
154 return;
155 }
156 if (!grease_pencil_attributes.add(
157 iter.name, AttrDomain::Layer, iter.data_type, bke::AttributeInitConstruct()))
158 {
159 return;
160 }
161 bke::GSpanAttributeWriter dst_attribute = grease_pencil_attributes.lookup_for_write_span(
162 iter.name);
163 array_utils::gather(src_attribute.varray, instance_selection, dst_attribute.span);
164 dst_attribute.finish();
165 });
166
167 {
168 /* Manually propagate "opacity" data, because it's not a layer attribute on grease pencil
169 * yet. Default to a full opacity of 1. */
170 const VArray<float> opacities = *instances_attributes.lookup_or_default<float>(
171 "opacity", AttrDomain::Instance, 1.0f);
172 instance_selection.foreach_index([&](const int instance_i, const int layer_i) {
173 grease_pencil->layer(layer_i).opacity = opacities[instance_i];
174 });
175 }
176
177 return grease_pencil;
178}
179
181{
182 GeometrySet curves_geometry = params.extract_input<GeometrySet>("Curves");
183 const Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
184 const bool instances_as_layers = params.extract_input<bool>("Instances as Layers");
185 const NodeAttributeFilter &attribute_filter = params.get_attribute_filter("Grease Pencil");
186
187 GreasePencil *grease_pencil = nullptr;
188 if (instances_as_layers) {
189 if (curves_geometry.has_curves()) {
190 params.error_message_add(NodeWarningType::Info, TIP_("Non-instance curves are ignored"));
191 }
192 const bke::Instances *instances = curves_geometry.get_instances();
193 if (!instances) {
194 params.set_default_remaining_outputs();
195 return;
196 }
198 *instances, selection_field, attribute_filter);
199 }
200 else {
201 if (curves_geometry.has_instances()) {
202 params.error_message_add(NodeWarningType::Info, TIP_("Instances are ignored"));
203 }
204 const Curves *curves_id = curves_geometry.get_curves();
205 if (!curves_id) {
206 params.set_default_remaining_outputs();
207 return;
208 }
210 *curves_id, selection_field, curves_geometry.name, attribute_filter);
211 }
212
213 GeometrySet grease_pencil_geometry = GeometrySet::from_grease_pencil(grease_pencil);
214 grease_pencil_geometry.name = std::move(curves_geometry.name);
215 params.set_output("Grease Pencil", std::move(grease_pencil_geometry));
216}
217
218static void node_register()
219{
220 static bke::bNodeType ntype;
222 &ntype, GEO_NODE_CURVES_TO_GREASE_PENCIL, "Curves to Grease Pencil", NODE_CLASS_GEOMETRY);
224 ntype.declare = node_declare;
225 bke::node_type_size(&ntype, 160, 100, 320);
226
228}
230
231} // namespace blender::nodes::node_geo_curves_to_grease_pencil_cc
Low-level operations for curves.
Low-level operations for grease pencil.
GreasePencil * BKE_grease_pencil_new_nomain()
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:418
#define ELEM(...)
#define TIP_(msgid)
@ CD_PROP_STRING
#define NOD_REGISTER_NODE(REGISTER_FUNC)
int64_t size() const
const Key * data() const
int64_t index_of_or_add(const Key &key)
int64_t size() const
void append(const T &value)
bool is_builtin(const StringRef attribute_id) const
void foreach_attribute(const FunctionRef< void(const AttributeIter &)> fn) const
GAttributeReader lookup_or_default(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type, const void *default_value=nullptr) const
GAttributeReader get() const
MutableAttributeAccessor attributes_for_write()
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
bool add(const StringRef attribute_id, const AttrDomain domain, const eCustomDataType data_type, const AttributeInit &initializer)
GSpanAttributeWriter lookup_for_write_span(StringRef attribute_id)
bke::CurvesGeometry & strokes_for_write()
void set_selection(Field< bool > selection)
Definition FN_field.hh:385
IndexMask complement(const IndexMask &universe, IndexMaskMemory &memory) const
void foreach_index(Fn &&fn) const
local_group_size(16, 16) .push_constant(Type b
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void gather(const GVArray &src, const IndexMask &indices, GMutableSpan dst, int64_t grain_size=4096)
void node_type_size(bNodeType *ntype, int width, int minwidth, int maxwidth)
Definition node.cc:4602
void node_register_type(bNodeType *ntype)
Definition node.cc:1708
static GreasePencil * curve_instances_to_grease_pencil_layers(const bke::Instances &instances, const Field< bool > &selection_field, const AttributeFilter &attribute_filter)
static GreasePencil * curves_to_grease_pencil_with_one_layer(const Curves &curves_id, const Field< bool > &selection_field, const StringRefNull layer_name, const AttributeFilter &attribute_filter)
void initialized_copy_n(const T *src, int64_t n, T *dst)
void geo_node_type_base(blender::bke::bNodeType *ntype, int type, const char *name, short nclass)
CurvesGeometry geometry
struct Material ** mat
struct Material ** material_array
bool allow_skip(const StringRef name) const
static GeometrySet from_grease_pencil(GreasePencil *grease_pencil, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
const Curves * get_curves() const
const Instances * get_instances() const
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