Blender V5.0
MOD_grease_pencil_armature.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
8
9#include "DNA_defaults.h"
10#include "DNA_meshdata_types.h"
11#include "DNA_modifier_types.h"
12#include "DNA_scene_types.h"
13
14#include "BKE_armature.hh"
15#include "BKE_curves.hh"
16#include "BKE_geometry_set.hh"
17#include "BKE_grease_pencil.hh"
18#include "BKE_lib_query.hh"
19#include "BKE_material.hh"
20#include "BKE_modifier.hh"
21#include "BKE_screen.hh"
22
23#include "BLO_read_write.hh"
24
26
28#include "UI_resources.hh"
29
30#include "BLT_translation.hh"
31
32#include "WM_types.hh"
33
34#include "RNA_access.hh"
35#include "RNA_enum_types.hh"
36#include "RNA_prototypes.hh"
37
39#include "MOD_modifiertypes.hh"
40#include "MOD_ui_common.hh"
41
42namespace blender {
43
45
55
56static void copy_data(const ModifierData *md, ModifierData *target, const int flag)
57{
58 const auto *amd = reinterpret_cast<const GreasePencilArmatureModifierData *>(md);
59 auto *tamd = reinterpret_cast<GreasePencilArmatureModifierData *>(target);
60
62
64 modifier::greasepencil::copy_influence_data(&amd->influence, &tamd->influence, flag);
65}
66
67static void free_data(ModifierData *md)
68{
69 auto *amd = reinterpret_cast<GreasePencilArmatureModifierData *>(md);
71}
72
73static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
74{
75 auto *amd = reinterpret_cast<GreasePencilArmatureModifierData *>(md);
76 modifier::greasepencil::foreach_influence_ID_link(&amd->influence, ob, walk, user_data);
77 walk(user_data, ob, (ID **)&amd->object, IDWALK_CB_NOP);
78}
79
80static bool is_disabled(const Scene * /*scene*/, ModifierData *md, bool /*use_render_params*/)
81{
82 auto *amd = reinterpret_cast<GreasePencilArmatureModifierData *>(md);
83
84 /* The object type check is only needed here in case we have a placeholder
85 * object assigned (because the library containing the armature is missing).
86 *
87 * In other cases it should be impossible to have a type mismatch. */
88 return !amd->object || amd->object->type != OB_ARMATURE;
89}
90
92{
93 auto *amd = reinterpret_cast<GreasePencilArmatureModifierData *>(md);
94 if (amd->object != nullptr) {
95 DEG_add_object_relation(ctx->node, amd->object, DEG_OB_COMP_EVAL_POSE, "Armature Modifier");
96 DEG_add_object_relation(ctx->node, amd->object, DEG_OB_COMP_TRANSFORM, "Armature Modifier");
97 }
98 DEG_add_object_relation(ctx->node, ctx->object, DEG_OB_COMP_TRANSFORM, "Armature Modifier");
99}
100
102{
103 if (attribute.sharing_info && attribute.varray.is_span()) {
104 const void *data = attribute.varray.get_internal_span().data();
105 attribute.sharing_info->add_user();
106 return {ImplicitSharingPtr(attribute.sharing_info), data};
107 }
108 auto *data = new ImplicitSharedValue<GArray<>>(attribute.varray.type(), attribute.varray.size());
109 attribute.varray.materialize(data->data.data());
110 return {ImplicitSharingPtr<>(data), data->data.data()};
111}
112
114 const ModifierEvalContext &ctx,
115 Drawing &drawing,
117{
118 const auto &amd = reinterpret_cast<GreasePencilArmatureModifierData &>(md);
119 /* The influence flag is where the "invert" flag is stored,
120 * but armature functions expect `deformflag` to have the flag set as well.
121 * Copy to `deformflag` here to keep old functions happy. */
122 const int deformflag = amd.deformflag |
123 (amd.influence.flag & GREASE_PENCIL_INFLUENCE_INVERT_VERTEX_GROUP ?
125 0);
126
127 if (drawing.strokes().deform_verts().is_empty()) {
128 return;
129 }
130
131 IndexMaskMemory mask_memory;
133 ctx.object, drawing.strokes(), amd.influence, mask_memory);
134
135 auto deform_curves = [&](MutableSpan<float3> positions,
136 std::optional<Span<float3>> old_positions,
137 std::optional<MutableSpan<float3x3>> deform_mats,
138 Span<MDeformVert> dverts,
139 const OffsetIndices<int> points_by_curve) {
140 curves_mask.foreach_index(blender::GrainSize(128), [&](const int curve_i) {
141 const IndexRange points = points_by_curve[curve_i];
142 std::optional<Span<float3>> old_positions_for_curve;
143 if (old_positions) {
144 old_positions_for_curve = old_positions->slice(points);
145 }
146 std::optional<MutableSpan<float3x3>> deform_mats_for_curve;
147 if (deform_mats) {
148 deform_mats_for_curve = deform_mats->slice(points);
149 }
151 *ctx.object,
152 &drawing.strokes().vertex_group_names,
153 positions.slice(points),
154 old_positions_for_curve,
155 deform_mats_for_curve,
156 dverts.slice(points),
157 deformflag,
158 amd.influence.vertex_group_name);
159 });
160 };
161
162 /* Cached position data for supporting the multi-modifier feature. This data is only valid as
163 * long as topology does not change, don't use this after converting Bezier curves! */
164 const ImplicitSharingPtrAndData old_positions_data = save_shared_attribute(
165 drawing.strokes().attributes().lookup("position", bke::AttrType::Float3));
166 const Span<blender::float3> old_positions = {
167 static_cast<const float3 *>(old_positions_data.data), drawing.strokes().points_num()};
168
169 const bool has_bezier_curves = drawing.strokes().has_curve_with_type(
170 CurveType::CURVE_TYPE_BEZIER);
171 if (has_bezier_curves) {
172 /* Update deformation data in edit hints related to original points.
173 * Do this before converting Bezier curves because that changes the topology.
174 * The multi-modifier feature is not supported in this case (no "old_positions" argument). */
175 if (edit_hints && edit_hints->positions()) {
176 const bke::CurvesGeometry &curves = drawing.strokes();
177 std::optional<MutableSpan<float3x3>> deform_mats =
178 edit_hints->deform_mats ?
179 edit_hints->deform_mats->as_mutable_span() :
180 edit_hints->deform_mats.emplace(curves.points_num(), float3x3::identity());
181 deform_curves(*edit_hints->positions_for_write(),
182 std::nullopt,
183 deform_mats,
184 curves.deform_verts(),
185 curves.points_by_curve());
186 }
187
188 /* Convert Bezier curves since these are not supported in armature deformation. */
190
191 /* Deform curve data without changes to edit hints. */
192 {
193 bke::CurvesGeometry &curves = drawing.strokes_for_write();
194 deform_curves(curves.positions_for_write(),
195 std::nullopt,
196 std::nullopt,
197 curves.deform_verts(),
198 curves.points_by_curve());
199 }
200 }
201 else {
202 /* Deform curve data and edit hints at the same time. */
203 bke::CurvesGeometry &curves = drawing.strokes_for_write();
204 std::optional<MutableSpan<float3x3>> deform_mats;
205 if (edit_hints) {
206 deform_mats = edit_hints->deform_mats ?
207 edit_hints->deform_mats->as_mutable_span() :
208 edit_hints->deform_mats.emplace(curves.points_num(), float3x3::identity());
209 }
210 deform_curves(curves.positions_for_write(),
211 old_positions,
212 deform_mats,
213 curves.deform_verts(),
214 curves.points_by_curve());
215 /* Copy deformed positions to edit hints if necessary. */
216 if (edit_hints && edit_hints->positions() &&
217 edit_hints->positions()->data() != curves.positions().data())
218 {
219 edit_hints->positions_for_write()->copy_from(curves.positions());
220 }
221 }
222
223 drawing.tag_positions_changed();
224}
225
227 const ModifierEvalContext *ctx,
228 bke::GeometrySet *geometry_set)
229{
230 using namespace modifier::greasepencil;
231
232 const auto *amd = reinterpret_cast<GreasePencilArmatureModifierData *>(md);
233
234 if (!geometry_set->has_grease_pencil()) {
235 return;
236 }
238
239 GreasePencil &grease_pencil = *geometry_set->get_grease_pencil_for_write();
240 const int frame = grease_pencil.runtime->eval_frame;
241
243 if (geometry_set->has_component<bke::GeometryComponentEditData>()) {
244 bke::GeometryComponentEditData &edit_component =
246 if (auto &hints = edit_component.grease_pencil_edit_hints_) {
247 if (!hints->drawing_hints) {
248 hints->drawing_hints.emplace(hints->grease_pencil_id_orig.layers().size());
249 }
250 edit_hints = *hints->drawing_hints;
251 }
252 }
253
254 IndexMaskMemory mask_memory;
255 const IndexMask layer_mask = get_filtered_layer_mask(grease_pencil, amd->influence, mask_memory);
256 const Vector<LayerDrawingInfo> drawings = get_drawing_infos_by_layer(
257 grease_pencil, layer_mask, frame);
258 threading::parallel_for_each(drawings, [&](const LayerDrawingInfo &info) {
259 if (edit_hints.is_empty()) {
260 modify_curves(*md, *ctx, *info.drawing, nullptr);
261 }
262 else {
263 modify_curves(*md, *ctx, *info.drawing, &edit_hints[info.layer_index]);
264 }
265 });
266}
267
268static void panel_draw(const bContext *C, Panel *panel)
269{
270 uiLayout *layout = panel->layout;
271
272 PointerRNA ob_ptr;
274
275 layout->use_property_split_set(true);
276
277 layout->prop(ptr, "object", UI_ITEM_NONE, std::nullopt, ICON_NONE);
279
280 uiLayout *col = &layout->column(true, IFACE_("Bind To"));
281 col->prop(ptr, "use_vertex_groups", UI_ITEM_NONE, IFACE_("Vertex Groups"), ICON_NONE);
282 col->prop(ptr, "use_bone_envelopes", UI_ITEM_NONE, IFACE_("Bone Envelopes"), ICON_NONE);
283
285}
286
291
292static void blend_write(BlendWriter *writer, const ID * /*id_owner*/, const ModifierData *md)
293{
294 const auto *amd = reinterpret_cast<const GreasePencilArmatureModifierData *>(md);
295
297 modifier::greasepencil::write_influence_data(writer, &amd->influence);
298}
299
300static void blend_read(BlendDataReader *reader, ModifierData *md)
301{
302 auto *amd = reinterpret_cast<GreasePencilArmatureModifierData *>(md);
303
304 modifier::greasepencil::read_influence_data(reader, &amd->influence);
305}
306
307} // namespace blender
308
310 /*idname*/ "GreasePencilArmature",
311 /*name*/ N_("Armature"),
312 /*struct_name*/ "GreasePencilArmatureModifierData",
313 /*struct_size*/ sizeof(GreasePencilArmatureModifierData),
314 /*srna*/ &RNA_GreasePencilArmatureModifier,
318 /*icon*/ ICON_MOD_ARMATURE,
319
320 /*copy_data*/ blender::copy_data,
321
322 /*deform_verts*/ nullptr,
323 /*deform_matrices*/ nullptr,
324 /*deform_verts_EM*/ nullptr,
325 /*deform_matrices_EM*/ nullptr,
326 /*modify_mesh*/ nullptr,
327 /*modify_geometry_set*/ blender::modify_geometry_set,
328
329 /*init_data*/ blender::init_data,
330 /*required_data_mask*/ nullptr,
331 /*free_data*/ blender::free_data,
332 /*is_disabled*/ blender::is_disabled,
333 /*update_depsgraph*/ blender::update_depsgraph,
334 /*depends_on_time*/ nullptr,
335 /*depends_on_normals*/ nullptr,
336 /*foreach_ID_link*/ blender::foreach_ID_link,
337 /*foreach_tex_link*/ nullptr,
338 /*free_runtime_data*/ nullptr,
339 /*panel_register*/ blender::panel_register,
340 /*blend_write*/ blender::blend_write,
341 /*blend_read*/ blender::blend_read,
342};
void BKE_armature_deform_coords_with_curves(const Object &ob_arm, const Object &ob_target, const ListBase *defbase, blender::MutableSpan< blender::float3 > vert_coords, std::optional< blender::Span< blender::float3 > > vert_coords_prev, std::optional< blender::MutableSpan< blender::float3x3 > > vert_deform_mats, blender::Span< MDeformVert > dverts, int deformflag, blender::StringRefNull defgrp_name)
Low-level operations for curves.
Low-level operations for grease pencil.
@ IDWALK_CB_NOP
General operations, lookup, etc. for materials.
void(*)(void *user_data, Object *ob, ID **idpoin, LibraryForeachIDCallbackFlag cb_flag) IDWalkFunc
void BKE_modifier_copydata_generic(const ModifierData *md, ModifierData *md_dst, int flag)
@ eModifierTypeFlag_SupportsMapping
@ eModifierTypeFlag_AcceptsGreasePencil
@ eModifierTypeFlag_EnableInEditmode
@ eModifierTypeFlag_SupportsEditmode
#define BLI_assert(a)
Definition BLI_assert.h:46
#define MEMCMP_STRUCT_AFTER_IS_ZERO(struct_var, member)
#define MEMCPY_STRUCT_AFTER(struct_dst, struct_src, member)
#define BLO_write_struct(writer, struct_name, data_ptr)
#define IFACE_(msgid)
void DEG_add_object_relation(DepsNodeHandle *node_handle, Object *object, eDepsObjectComponentType component, const char *description)
@ DEG_OB_COMP_EVAL_POSE
@ DEG_OB_COMP_TRANSFORM
@ ARM_DEF_INVERT_VGROUP
#define DNA_struct_default_get(struct_name)
@ GREASE_PENCIL_INFLUENCE_INVERT_VERTEX_GROUP
@ eModifierType_GreasePencilArmature
@ OB_ARMATURE
static bool is_disabled
ModifierTypeInfo modifierType_GreasePencilArmature
PanelType * modifier_panel_register(ARegionType *region_type, ModifierType type, PanelDrawFn draw)
PointerRNA * modifier_panel_get_property_pointers(Panel *panel, PointerRNA *r_ob_ptr)
void modifier_error_message_draw(uiLayout *layout, PointerRNA *ptr)
#define C
Definition RandGen.cpp:29
#define UI_ITEM_NONE
BMesh const char void * data
const void * data() const
void materialize(void *dst) const
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:573
constexpr bool is_empty() const
Definition BLI_span.hh:509
GAttributeReader lookup(const StringRef attribute_id) const
MutableSpan< float3 > positions_for_write()
OffsetIndices< int > points_by_curve() const
Span< MDeformVert > deform_verts() const
Span< float3 > positions() const
bool has_curve_with_type(CurveType type) const
AttributeAccessor attributes() const
std::unique_ptr< GreasePencilEditHints > grease_pencil_edit_hints_
static void remember_deformed_positions_if_necessary(GeometrySet &geometry)
std::optional< MutableSpan< float3 > > positions_for_write()
std::optional< Span< float3 > > positions() const
std::optional< Array< float3x3 > > deform_mats
bke::CurvesGeometry & strokes_for_write()
const bke::CurvesGeometry & strokes() const
void foreach_index(Fn &&fn) const
uint col
void read_influence_data(BlendDataReader *reader, GreasePencilModifierInfluenceData *influence_data)
void init_influence_data(GreasePencilModifierInfluenceData *influence_data, const bool has_custom_curve)
static IndexMask get_filtered_stroke_mask(const Object *ob, const bke::CurvesGeometry &curves, const Material *material_filter, const std::optional< int > material_pass_filter, const bool material_filter_invert, const bool material_pass_filter_invert, IndexMaskMemory &memory)
void write_influence_data(BlendWriter *writer, const GreasePencilModifierInfluenceData *influence_data)
void draw_vertex_group_settings(const bContext *, uiLayout *layout, PointerRNA *ptr)
void free_influence_data(GreasePencilModifierInfluenceData *influence_data)
void foreach_influence_ID_link(GreasePencilModifierInfluenceData *influence_data, Object *ob, IDWalkFunc walk, void *user_data)
void copy_influence_data(const GreasePencilModifierInfluenceData *influence_data_src, GreasePencilModifierInfluenceData *influence_data_dst, const int)
void ensure_no_bezier_curves(Drawing &drawing)
void parallel_for_each(Range &&range, const Function &function)
Definition BLI_task.hh:56
static void copy_data(const ModifierData *md, ModifierData *target, const int flag)
static void blend_write(BlendWriter *writer, const ID *, const ModifierData *md)
static void modify_curves(ModifierData &md, const ModifierEvalContext &ctx, Drawing &drawing, bke::GreasePencilDrawingEditHints *edit_hints)
static void init_data(ModifierData *md)
static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
static void panel_draw(const bContext *C, Panel *panel)
static void modify_geometry_set(ModifierData *md, const ModifierEvalContext *ctx, bke::GeometrySet *geometry_set)
static ImplicitSharingPtrAndData save_shared_attribute(const bke::GAttributeReader &attribute)
static void free_data(ModifierData *md)
static void panel_register(ARegionType *region_type)
static void update_depsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
VecBase< float, 3 > float3
static bool is_disabled(const Scene *, ModifierData *md, bool)
static void blend_read(BlendDataReader *reader, ModifierData *md)
ListBase vertex_group_names
GreasePencilRuntimeHandle * runtime
Definition DNA_ID.h:414
struct uiLayout * layout
const ImplicitSharingInfo * sharing_info
GeometryComponent & get_component_for_write(GeometryComponent::Type component_type)
GreasePencil * get_grease_pencil_for_write()
uiLayout & column(bool align)
void use_property_split_set(bool value)
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)
#define N_(msgid)
PointerRNA * ptr
Definition wm_files.cc:4238
uint8_t flag
Definition wm_window.cc:145