Blender V5.0
grease_pencil_bake_animation.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 <fmt/format.h>
10
11#include "ANIM_action_legacy.hh"
12
13#include "BKE_anim_data.hh"
14#include "BKE_context.hh"
15#include "BKE_curves.hh"
16#include "BKE_duplilist.hh"
17#include "BKE_material.hh"
18#include "BKE_scene.hh"
19
20#include "BLI_listbase.h"
21#include "BLI_math_matrix.hh"
22#include "BLI_set.hh"
23
24#include "BLT_translation.hh"
25
27
28#include "DNA_anim_types.h"
29
30#include "ED_grease_pencil.hh"
31#include "ED_object.hh"
32
33#include "RNA_access.hh"
34#include "RNA_define.hh"
35
36#include "WM_types.hh"
37
39static void ensure_valid_frame_end(Main * /*main*/, Scene * /*scene*/, PointerRNA *ptr)
40{
41 const int frame_start = RNA_int_get(ptr, "frame_start");
42 const int frame_end = RNA_int_get(ptr, "frame_end");
43
44 if (frame_end <= frame_start) {
45 RNA_int_set(ptr, "frame_end", frame_start + 1);
46 }
47}
48
50 wmOperator *op,
51 const wmEvent * /*event*/)
52{
53 const Scene *scene = CTX_data_scene(C);
54
55 PropertyRNA *prop_frame_start = RNA_struct_find_property(op->ptr, "frame_start");
56 if (!RNA_property_is_set(op->ptr, prop_frame_start)) {
57 const int frame_start = RNA_property_int_get(op->ptr, prop_frame_start);
58 if (frame_start < scene->r.sfra) {
59 RNA_property_int_set(op->ptr, prop_frame_start, scene->r.sfra);
60 }
61 }
62
63 PropertyRNA *prop_frame_end = RNA_struct_find_property(op->ptr, "frame_end");
64 if (!RNA_property_is_set(op->ptr, prop_frame_end)) {
65 const int frame_end = RNA_property_int_get(op->ptr, prop_frame_end);
66 if (frame_end > scene->r.efra) {
67 RNA_property_int_set(op->ptr, prop_frame_end, scene->r.efra);
68 }
69 }
70
72 C, op, 250, IFACE_("Bake Object Transform to Grease Pencil"), IFACE_("Bake"));
73}
74
76{
77 Vector<Object *> bake_targets;
78 Object *active_object = CTX_data_active_object(&C);
79
80 DupliList duplilist;
81
82 if (active_object->type == OB_GREASE_PENCIL) {
83 bake_targets.append(active_object);
84 }
85 else if (active_object->type == OB_EMPTY) {
86 object_duplilist(&depsgraph, &scene, active_object, nullptr, duplilist);
87 for (DupliObject &duplicate_object : duplilist) {
88 if (duplicate_object.ob->type != OB_GREASE_PENCIL) {
89 continue;
90 }
91
92 bake_targets.append(duplicate_object.ob);
93 }
94 duplilist.clear();
95 }
96
97 CTX_DATA_BEGIN (&C, Object *, object, selected_objects) {
98 if (object == active_object) {
99 continue;
100 }
101
102 if (object->type == OB_GREASE_PENCIL) {
103 bake_targets.append(object);
104 }
105 else if (object->type == OB_EMPTY) {
106 object_duplilist(&depsgraph, &scene, active_object, nullptr, duplilist);
107 for (DupliObject &duplicate_object : duplilist) {
108 if (duplicate_object.ob->type != OB_GREASE_PENCIL) {
109 continue;
110 }
111
112 bake_targets.append(duplicate_object.ob);
113 }
114 duplilist.clear();
115 }
116 }
118 return bake_targets;
119}
120
122{
123 Set<int> keyframes;
124 for (Object *bake_target : bake_targets) {
125 AnimData *adt = BKE_animdata_from_id(&bake_target->id);
127 for (const int i : IndexRange(fcurve->totvert)) {
128 BezTriple bezt = fcurve->bezt[i];
129 if (bezt.f2 & SELECT) {
130 keyframes.add(int(bezt.vec[1][0]));
131 }
132 }
133 }
134 }
135
136 return keyframes;
137}
138
140{
141 using namespace bke::greasepencil;
142
143 Main &bmain = *CTX_data_main(C);
145 Scene &scene = *CTX_data_scene(C);
146
147 const int step = RNA_int_get(op->ptr, "step");
148
149 const int frame_start = (scene.r.sfra > RNA_int_get(op->ptr, "frame_start")) ?
150 scene.r.sfra :
151 RNA_int_get(op->ptr, "frame_start");
152
153 const int frame_end = (scene.r.efra < RNA_int_get(op->ptr, "frame_end")) ?
154 scene.r.efra :
155 RNA_int_get(op->ptr, "frame_end");
156
157 const bool only_selected = RNA_boolean_get(op->ptr, "only_selected");
158 const int frame_offset = RNA_int_get(op->ptr, "frame_target") - frame_start;
159 const ReprojectMode reproject_mode = ReprojectMode(RNA_enum_get(op->ptr, "project_type"));
160
161 View3D *v3d = CTX_wm_view3d(C);
162 ARegion *region = CTX_wm_region(C);
163
164 Vector<Object *> bake_targets = get_bake_targets(*C, depsgraph, scene);
165
166 uint8_t local_view_bits = (v3d && v3d->localvd) ? v3d->local_view_uid : 0;
167 Object *target_object = object::add_type(
168 C, OB_GREASE_PENCIL, nullptr, scene.cursor.location, float3(0), false, local_view_bits);
169
170 const float4x4 target_imat = math::invert(target_object->object_to_world());
171
172 WM_cursor_wait(true);
173
174 GreasePencil &target = *static_cast<GreasePencil *>(target_object->data);
175 Object *target_object_eval = DEG_get_evaluated(&depsgraph, target_object);
176
177 std::optional<Set<int>> keyframes;
178 if (only_selected) {
179 keyframes = get_selected_object_keyframes(bake_targets);
180 }
181
182 const int prior_frame = int(DEG_get_ctime(&depsgraph));
183
184 for (int frame = frame_start; frame <= frame_end; frame++) {
185 if ((frame - frame_start) % step != 0 && frame != frame_end) {
186 continue;
187 }
188
189 if (keyframes && keyframes->contains(frame)) {
190 continue;
191 }
192
193 scene.r.cfra = frame;
195
196 for (Object *source_object : bake_targets) {
197 Object *source_object_eval = DEG_get_evaluated(&depsgraph, source_object);
198 GreasePencil &source_eval_grease_pencil = *static_cast<GreasePencil *>(
199 source_object_eval->data);
200 const float4x4 to_target = source_object_eval->object_to_world() * target_imat;
201
202 for (const Layer *source_layer : source_eval_grease_pencil.layers()) {
203 std::string layer_name = fmt::format(
204 "{}_{}", source_object->id.name + 2, source_layer->name());
205 TreeNode *node = target.find_node_by_name(layer_name);
206 if (node == nullptr) {
207 Layer &new_layer = target.add_layer(layer_name);
208 target.set_active_layer(&new_layer);
209 }
210
211 Layer &target_layer = target.find_node_by_name(layer_name)->as_layer();
212 std::optional<DrawingPlacement> drawing_placement;
213 if (reproject_mode != ReprojectMode::Keep) {
214 drawing_placement = DrawingPlacement(
215 scene, *region, *v3d, *target_object_eval, &target_layer, reproject_mode);
216 }
217
218 const GreasePencilFrame *source_frame = source_layer->frame_at(scene.r.cfra);
219 if (source_frame == nullptr) {
220 continue;
221 }
222
223 const int target_frame_num = scene.r.cfra + frame_offset;
224 Drawing &source_drawing = *source_eval_grease_pencil.get_drawing_at(*source_layer,
225 scene.r.cfra);
226 Drawing &target_drawing = *target.insert_frame(target_layer, target_frame_num);
227 target_drawing.strokes_for_write() = source_drawing.strokes();
228
229 bke::AttributeAccessor source_attributes = source_drawing.strokes().attributes();
230 const VArray<int> source_material_indices = *source_attributes.lookup_or_default<int>(
231 "material_index", bke::AttrDomain::Curve, 0);
232 bke::CurvesGeometry &target_strokes = target_drawing.strokes_for_write();
233 bke::SpanAttributeWriter<int> target_material_indices =
234 target_strokes.attributes_for_write().lookup_or_add_for_write_span<int>(
235 "material_index", bke::AttrDomain::Curve);
236
237 for (const int i : target_drawing.strokes().curves_range()) {
238 Material *source_material = BKE_object_material_get(source_object,
239 source_material_indices[i] + 1);
240 BLI_assert(source_material != nullptr);
241
242 bool found = false;
243 for (const int target_index : IndexRange(target_object->totcol)) {
244 Material *target_material = BKE_object_material_get(target_object, target_index + 1);
245 if (source_material == target_material) {
246 found = true;
247 break;
248 }
249 }
250
251 if (!found) {
252 BKE_object_material_slot_add(&bmain, target_object);
254 target_object,
255 source_material,
256 target_object->totcol,
258 }
259
260 target_material_indices.span[i] = BKE_object_material_index_get(target_object,
261 source_material);
262 }
263
264 target_material_indices.finish();
265 MutableSpan<float3> positions = target_strokes.positions_for_write();
266 math::transform_points(to_target, positions);
267 if (drawing_placement) {
268 threading::parallel_for(positions.index_range(), 4096, [&](IndexRange range) {
269 for (const int i : range) {
270 positions[i] = drawing_placement->reproject(positions[i]);
271 }
272 });
273 }
274 }
275 }
276 }
277 scene.r.cfra = prior_frame;
279
285
286 WM_cursor_wait(false);
287 return OPERATOR_FINISHED;
288}
289
291{
292 const Object *obact = CTX_data_active_object(C);
293
294 /* Check if grease pencil or empty for dupli groups. */
295 if ((obact == nullptr) || (obact->mode != OB_MODE_OBJECT) ||
297 {
298 return false;
299 }
300
301 /* Only if the current view is 3D View. */
302 const ScrArea *area = CTX_wm_area(C);
303 return (area && area->spacetype);
304}
305
307{
308 ot->name = "Bake Object Transform to Grease Pencil";
309 ot->idname = "GREASE_PENCIL_OT_bake_grease_pencil_animation";
310 ot->description = "Bake Grease Pencil object transform to Grease Pencil keyframes";
311
315
317
318 RNA_def_int(ot->srna, "frame_start", 1, 1, 100000, "Start Frame", "The start frame", 1, 100000);
319
320 PropertyRNA *prop = RNA_def_int(
321 ot->srna, "frame_end", 250, 1, 100000, "End Frame", "The end frame of animation", 1, 100000);
323
324 RNA_def_int(ot->srna, "step", 1, 1, 100, "Step", "Step between generated frames", 1, 100);
325
326 RNA_def_boolean(ot->srna,
327 "only_selected",
328 false,
329 "Only Selected Keyframes",
330 "Convert only selected keyframes");
332 ot->srna, "frame_target", 1, 1, 100000, "Target Frame", "Destination frame", 1, 100000);
333
334 static const EnumPropertyItem rna_grease_pencil_reproject_type_items[] = {
335 {int(ReprojectMode::Keep), "KEEP", 0, "No Reproject", ""},
337 "FRONT",
338 0,
339 "Front",
340 "Reproject the strokes using the X-Z plane"},
341 {int(ReprojectMode::Side), "SIDE", 0, "Side", "Reproject the strokes using the Y-Z plane"},
342 {int(ReprojectMode::Top), "TOP", 0, "Top", "Reproject the strokes using the X-Y plane"},
344 "VIEW",
345 0,
346 "View",
347 "Reproject the strokes to end up on the same plane, as if drawn from the current "
348 "viewpoint "
349 "using 'Cursor' Stroke Placement"},
351 "CURSOR",
352 0,
353 "Cursor",
354 "Reproject the strokes using the orientation of 3D cursor"},
355 {0, nullptr, 0, nullptr, nullptr},
356 };
357
358 RNA_def_enum(ot->srna,
359 "project_type",
360 rna_grease_pencil_reproject_type_items,
362 "Projection Type",
363 "");
364}
365} // namespace blender::ed::greasepencil
366
Functions for backward compatibility with the legacy Action API.
AnimData * BKE_animdata_from_id(const ID *id)
Definition anim_data.cc:83
#define CTX_DATA_BEGIN(C, Type, instance, member)
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
ScrArea * CTX_wm_area(const bContext *C)
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Main * CTX_data_main(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
#define CTX_DATA_END
View3D * CTX_wm_view3d(const bContext *C)
Low-level operations for curves.
blender::VectorList< DupliObject > DupliList
void object_duplilist(Depsgraph *depsgraph, Scene *sce, Object *ob, blender::Set< const Object * > *include_objects, DupliList &r_duplilist)
General operations, lookup, etc. for materials.
@ BKE_MAT_ASSIGN_USERPREF
bool BKE_object_material_slot_add(Main *bmain, Object *ob, bool set_active=true)
Material * BKE_object_material_get(Object *ob, short act)
void BKE_object_material_assign(Main *bmain, Object *ob, Material *ma, short act, int assign_type)
int BKE_object_material_index_get(Object *ob, const Material *ma)
void BKE_scene_graph_update_for_newframe(Depsgraph *depsgraph)
Definition scene.cc:2700
#define BLI_assert(a)
Definition BLI_assert.h:46
#define ELEM(...)
#define IFACE_(msgid)
void DEG_id_tag_update(ID *id, unsigned int flags)
void DEG_relations_tag_update(Main *bmain)
float DEG_get_ctime(const Depsgraph *graph)
T * DEG_get_evaluated(const Depsgraph *depsgraph, T *id)
@ ID_RECALC_SELECT
Definition DNA_ID.h:1101
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1118
@ OB_MODE_OBJECT
@ OB_EMPTY
@ OB_GREASE_PENCIL
@ OPERATOR_FINISHED
#define C
Definition RandGen.cpp:29
#define ND_OB_ACTIVE
Definition WM_types.hh:440
#define NC_SCENE
Definition WM_types.hh:378
#define NA_ADDED
Definition WM_types.hh:586
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define NC_OBJECT
Definition WM_types.hh:379
BPy_StructRNA * depsgraph
constexpr IndexRange index_range() const
Definition BLI_span.hh:670
bool add(const Key &key)
Definition BLI_set.hh:248
void append(const T &value)
GAttributeReader lookup_or_default(StringRef attribute_id, AttrDomain domain, AttrType data_type, const void *default_value=nullptr) const
IndexRange curves_range() const
AttributeAccessor attributes() const
bke::CurvesGeometry & strokes_for_write()
const bke::CurvesGeometry & strokes() const
#define SELECT
VecBase< float, D > step(VecOp< float, D >, VecOp< float, D >) RET
void ED_operatortypes_grease_pencil_bake_animation()
Vector< FCurve * > fcurves_for_assigned_action(AnimData *adt)
static Vector< Object * > get_bake_targets(bContext &C, Depsgraph &depsgraph, Scene &scene)
static wmOperatorStatus bake_grease_pencil_animation_exec(bContext *C, wmOperator *op)
static wmOperatorStatus bake_grease_pencil_animation_invoke(bContext *C, wmOperator *op, const wmEvent *)
static Set< int > get_selected_object_keyframes(Span< Object * > bake_targets)
static bool bake_grease_pencil_animation_poll(bContext *C)
static void GREASE_PENCIL_OT_bake_grease_pencil_animation(wmOperatorType *ot)
static void ensure_valid_frame_end(Main *, Scene *, PointerRNA *ptr)
Object * add_type(bContext *C, int type, const char *name, const float loc[3], const float rot[3], bool enter_editmode, unsigned short local_view_bits) ATTR_NONNULL(1) ATTR_RETURNS_NONNULL
CartesianBasis invert(const CartesianBasis &basis)
void transform_points(const float4x4 &transform, MutableSpan< float3 > points, bool use_threading=true)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:93
MatBase< float, 4, 4 > float4x4
VecBase< float, 3 > float3
void RNA_property_int_set(PointerRNA *ptr, PropertyRNA *prop, int value)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
bool RNA_property_is_set(PointerRNA *ptr, PropertyRNA *prop)
void RNA_int_set(PointerRNA *ptr, const char *name, int value)
int RNA_property_int_get(PointerRNA *ptr, PropertyRNA *prop)
int RNA_int_get(PointerRNA *ptr, const char *name)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
int RNA_enum_get(PointerRNA *ptr, const char *name)
void RNA_def_property_update_runtime(PropertyRNA *prop, RNAPropertyUpdateFunc func)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, const int default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
float vec[3][3]
struct RenderData r
View3DCursor cursor
struct View3D * localvd
unsigned short local_view_uid
struct PointerRNA * ptr
i
Definition text_draw.cc:230
void WM_cursor_wait(bool val)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
PointerRNA * ptr
Definition wm_files.cc:4238
wmOperatorType * ot
Definition wm_files.cc:4237
void WM_operatortype_append(void(*opfunc)(wmOperatorType *))
wmOperatorStatus WM_operator_props_dialog_popup(bContext *C, wmOperator *op, int width, std::optional< std::string > title, std::optional< std::string > confirm_text, const bool cancel_default, std::optional< std::string > message)