Blender V5.0
node_geo_grease_pencil_to_curves.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 "BLI_array_utils.hh"
6
7#include "BKE_curves.hh"
9#include "BKE_instances.hh"
10
12
13#include "node_geometry_util.hh"
14
16
18{
19 b.add_input<decl::Geometry>("Grease Pencil")
21 .description("Grease Pencil data to convert to curves");
22 b.add_input<decl::Bool>("Selection")
23 .default_value(true)
24 .hide_value()
25 .field_on_all()
26 .description("Select the layers to convert");
27 b.add_input<decl::Bool>("Layers as Instances")
28 .default_value(true)
29 .description("Create a separate curve instance for every layer");
30 b.add_output<decl::Geometry>("Curves").propagate_all();
31}
32
34{
35 GeometrySet grease_pencil_geometry = params.extract_input<GeometrySet>("Grease Pencil");
36 const GreasePencil *grease_pencil = grease_pencil_geometry.get_grease_pencil();
37 if (!grease_pencil) {
38 params.set_default_remaining_outputs();
39 return;
40 }
41
42 const Span<const bke::greasepencil::Layer *> layers = grease_pencil->layers();
43 const int layers_num = layers.size();
44
45 const bke::GreasePencilFieldContext field_context{*grease_pencil};
46 const Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
47 FieldEvaluator evaluator{field_context, layers_num};
48 evaluator.set_selection(selection_field);
49 evaluator.evaluate();
50 const IndexMask layer_selection = evaluator.get_evaluated_selection_as_mask();
51
52 const int instances_num = layer_selection.size();
53 if (instances_num == 0) {
54 params.set_default_remaining_outputs();
55 return;
56 }
57
58 bke::Instances *instances = new bke::Instances();
59 std::optional<int> empty_geometry_handle;
60
61 layer_selection.foreach_index([&](const int layer_i) {
62 const bke::greasepencil::Layer &layer = *layers[layer_i];
63 const bke::greasepencil::Drawing *drawing = grease_pencil->get_eval_drawing(layer);
64 const float4x4 transform = layer.local_transform();
65 if (!drawing) {
66 if (!empty_geometry_handle.has_value()) {
67 empty_geometry_handle = instances->add_reference(bke::InstanceReference());
68 }
69 instances->add_instance(*empty_geometry_handle, transform);
70 return;
71 }
72 const bke::CurvesGeometry &layer_strokes = drawing->strokes();
73 Curves *curves_id = bke::curves_new_nomain(layer_strokes);
74 curves_id->mat = static_cast<Material **>(MEM_dupallocN(grease_pencil->material_array));
75 curves_id->totcol = grease_pencil->material_array_num;
76 GeometrySet curves_geometry = GeometrySet::from_curves(curves_id);
77 curves_geometry.name = layer.name();
78 const int handle = instances->add_reference(std::move(curves_geometry));
79 instances->add_instance(handle, transform);
80 });
81
82 const bke::AttributeAccessor grease_pencil_attributes = grease_pencil->attributes();
83 bke::MutableAttributeAccessor instances_attributes = instances->attributes_for_write();
84 grease_pencil_attributes.foreach_attribute([&](const bke::AttributeIter &iter) {
85 if (ELEM(iter.name, "opacity")) {
86 return;
87 }
88 if (iter.data_type == bke::AttrType::String) {
89 return;
90 }
91 const GAttributeReader src_attribute = iter.get();
92 if (!src_attribute) {
93 return;
94 }
95 if (src_attribute.varray.is_span() && src_attribute.sharing_info) {
96 /* Try reusing existing attribute array. */
97 instances_attributes.add(
98 iter.name,
99 AttrDomain::Instance,
100 iter.data_type,
101 bke::AttributeInitShared{src_attribute.varray.get_internal_span().data(),
102 *src_attribute.sharing_info});
103 return;
104 }
105 if (!instances_attributes.add(
106 iter.name, AttrDomain::Instance, iter.data_type, bke::AttributeInitConstruct()))
107 {
108 return;
109 }
110 bke::GSpanAttributeWriter dst_attribute = instances_attributes.lookup_for_write_span(
111 iter.name);
112 array_utils::gather(src_attribute.varray, layer_selection, dst_attribute.span);
113 dst_attribute.finish();
114 });
115
116 {
117 /* Manually propagate "opacity" data, because it's not a layer attribute on grease pencil
118 * yet. */
119 if (SpanAttributeWriter<float> opacity_attribute =
120 instances_attributes.lookup_or_add_for_write_only_span<float>("opacity",
121 AttrDomain::Instance))
122 {
123 layer_selection.foreach_index([&](const int layer_i, const int instance_i) {
124 opacity_attribute.span[instance_i] = grease_pencil->layer(layer_i).opacity;
125 });
126 opacity_attribute.finish();
127 }
128 }
129
130 GeometrySet curves_geometry = GeometrySet::from_instances(instances);
131 curves_geometry.name = std::move(grease_pencil_geometry.name);
132
133 const bool layers_as_instances = params.extract_input<bool>("Layers as Instances");
134 if (!layers_as_instances) {
136 const NodeAttributeFilter attribute_filter = params.get_attribute_filter("Curves");
137 options.attribute_filter = attribute_filter;
138 curves_geometry = geometry::realize_instances(curves_geometry, options).geometry;
139 }
140
141 params.set_output("Curves", std::move(curves_geometry));
142}
143
144static void node_register()
145{
146 static bke::bNodeType ntype;
147 geo_node_type_base(&ntype, "GeometryNodeGreasePencilToCurves", GEO_NODE_GREASE_PENCIL_TO_CURVES);
148 ntype.ui_name = "Grease Pencil to Curves";
149 ntype.ui_description = "Convert Grease Pencil layers into curve instances";
150 ntype.enum_name_legacy = "GREASE_PENCIL_TO_CURVES";
153 ntype.declare = node_declare;
154 bke::node_type_size(ntype, 160, 100, 320);
155
157}
159
160} // namespace blender::nodes::node_geo_grease_pencil_to_curves_cc
Low-level operations for curves.
Low-level operations for grease pencil.
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:461
#define GEO_NODE_GREASE_PENCIL_TO_CURVES
#define ELEM(...)
#define NOD_REGISTER_NODE(REGISTER_FUNC)
SIMD_FORCE_INLINE btVector3 transform(const btVector3 &point) const
constexpr int64_t size() const
Definition BLI_span.hh:252
void foreach_attribute(const FunctionRef< void(const AttributeIter &)> fn) const
GAttributeReader get() const
int add_reference(const InstanceReference &reference)
Definition instances.cc:261
void add_instance(int instance_handle, const float4x4 &transform)
Definition instances.cc:204
bke::MutableAttributeAccessor attributes_for_write()
Definition instances.cc:69
bool add(const StringRef attribute_id, const AttrDomain domain, const AttrType data_type, const AttributeInit &initializer)
GSpanAttributeWriter lookup_or_add_for_write_only_span(StringRef attribute_id, AttrDomain domain, AttrType data_type)
GSpanAttributeWriter lookup_for_write_span(StringRef attribute_id)
const bke::CurvesGeometry & strokes() const
void foreach_index(Fn &&fn) const
CCL_NAMESPACE_BEGIN struct Options options
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void * MEM_dupallocN(const void *vmemh)
Definition mallocn.cc:143
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:5384
void node_register_type(bNodeType &ntype)
Definition node.cc:2416
Curves * curves_new_nomain(int points_num, int curves_num)
RealizeInstancesResult realize_instances(bke::GeometrySet geometry_set, const RealizeInstancesOptions &options)
MatBase< float, 4, 4 > float4x4
void geo_node_type_base(blender::bke::bNodeType *ntype, std::string idname, const std::optional< int16_t > legacy_type)
struct Material ** mat
const ImplicitSharingInfo * sharing_info
const GreasePencil * get_grease_pencil() const
Defines a node type.
Definition BKE_node.hh:238
std::string ui_description
Definition BKE_node.hh:244
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:354
const char * enum_name_legacy
Definition BKE_node.hh:247
NodeDeclareFunction declare
Definition BKE_node.hh:362
static GeometrySet from_instances(Instances *instances, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
static GeometrySet from_curves(Curves *curves, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)