Blender V4.5
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 if (active_object->type == OB_GREASE_PENCIL) {
81 bake_targets.append(active_object);
82 }
83 else if (active_object->type == OB_EMPTY) {
84 ListBase *lb = object_duplilist(&depsgraph, &scene, active_object);
85 LISTBASE_FOREACH (DupliObject *, duplicate_object, lb) {
86 if (duplicate_object->ob->type != OB_GREASE_PENCIL) {
87 continue;
88 }
89
90 bake_targets.append(duplicate_object->ob);
91 }
93 }
94
95 CTX_DATA_BEGIN (&C, Object *, object, selected_objects) {
96 if (object == active_object) {
97 continue;
98 }
99
100 if (object->type == OB_GREASE_PENCIL) {
101 bake_targets.append(object);
102 }
103 else if (object->type == OB_EMPTY) {
104 ListBase *lb = object_duplilist(&depsgraph, &scene, active_object);
105 LISTBASE_FOREACH (DupliObject *, duplicate_object, lb) {
106 if (duplicate_object->ob->type != OB_GREASE_PENCIL) {
107 continue;
108 }
109
110 bake_targets.append(duplicate_object->ob);
111 }
113 }
114 }
116 return bake_targets;
117}
118
120{
121 Set<int> keyframes;
122 for (Object *bake_target : bake_targets) {
123 AnimData *adt = BKE_animdata_from_id(&bake_target->id);
125 for (const int i : IndexRange(fcurve->totvert)) {
126 BezTriple bezt = fcurve->bezt[i];
127 if (bezt.f2 & SELECT) {
128 keyframes.add(int(bezt.vec[1][0]));
129 }
130 }
131 }
132 }
133
134 return keyframes;
135}
136
138{
139 using namespace bke::greasepencil;
140
141 Main &bmain = *CTX_data_main(C);
143 Scene &scene = *CTX_data_scene(C);
144
145 const int step = RNA_int_get(op->ptr, "step");
146
147 const int frame_start = (scene.r.sfra > RNA_int_get(op->ptr, "frame_start")) ?
148 scene.r.sfra :
149 RNA_int_get(op->ptr, "frame_start");
150
151 const int frame_end = (scene.r.efra < RNA_int_get(op->ptr, "frame_end")) ?
152 scene.r.efra :
153 RNA_int_get(op->ptr, "frame_end");
154
155 const bool only_selected = RNA_boolean_get(op->ptr, "only_selected");
156 const int frame_offset = RNA_int_get(op->ptr, "frame_target") - frame_start;
157 const ReprojectMode reproject_mode = ReprojectMode(RNA_enum_get(op->ptr, "project_type"));
158
159 View3D *v3d = CTX_wm_view3d(C);
160 ARegion *region = CTX_wm_region(C);
161
162 Vector<Object *> bake_targets = get_bake_targets(*C, depsgraph, scene);
163
164 uint8_t local_view_bits = (v3d && v3d->localvd) ? v3d->local_view_uid : 0;
165 Object *target_object = object::add_type(
166 C, OB_GREASE_PENCIL, nullptr, scene.cursor.location, float3(0), false, local_view_bits);
167
168 const float4x4 target_imat = math::invert(target_object->object_to_world());
169
170 WM_cursor_wait(true);
171
172 GreasePencil &target = *static_cast<GreasePencil *>(target_object->data);
173 Object *target_object_eval = DEG_get_evaluated(&depsgraph, target_object);
174
175 std::optional<Set<int>> keyframes;
176 if (only_selected) {
177 keyframes = get_selected_object_keyframes(bake_targets);
178 }
179
180 const int prior_frame = int(DEG_get_ctime(&depsgraph));
181
182 for (int frame = frame_start; frame <= frame_end; frame++) {
183 if ((frame - frame_start) % step != 0 && frame != frame_end) {
184 continue;
185 }
186
187 if (keyframes && keyframes->contains(frame)) {
188 continue;
189 }
190
191 scene.r.cfra = frame;
193
194 for (Object *source_object : bake_targets) {
195 Object *source_object_eval = DEG_get_evaluated(&depsgraph, source_object);
196 GreasePencil &source_eval_grease_pencil = *static_cast<GreasePencil *>(
197 source_object_eval->data);
198 const float4x4 to_target = source_object_eval->object_to_world() * target_imat;
199
200 for (const Layer *source_layer : source_eval_grease_pencil.layers()) {
201 std::string layer_name = fmt::format(
202 "{}_{}", source_object->id.name + 2, source_layer->name());
203 TreeNode *node = target.find_node_by_name(layer_name);
204 if (node == nullptr) {
205 Layer &new_layer = target.add_layer(layer_name);
206 target.set_active_layer(&new_layer);
207 }
208
209 Layer &target_layer = target.find_node_by_name(layer_name)->as_layer();
210 std::optional<DrawingPlacement> drawing_placement;
211 if (reproject_mode != ReprojectMode::Keep) {
212 drawing_placement = DrawingPlacement(
213 scene, *region, *v3d, *target_object_eval, &target_layer, reproject_mode);
214 }
215
216 const GreasePencilFrame *source_frame = source_layer->frame_at(scene.r.cfra);
217 if (source_frame == nullptr) {
218 continue;
219 }
220
221 const int target_frame_num = scene.r.cfra + frame_offset;
222 Drawing &source_drawing = *source_eval_grease_pencil.get_drawing_at(*source_layer,
223 scene.r.cfra);
224 Drawing &target_drawing = *target.insert_frame(target_layer, target_frame_num);
225 target_drawing.strokes_for_write() = source_drawing.strokes();
226
227 bke::AttributeAccessor source_attributes = source_drawing.strokes().attributes();
228 const VArray<int> source_material_indices = *source_attributes.lookup_or_default<int>(
229 "material_index", bke::AttrDomain::Curve, 0);
230 bke::CurvesGeometry &target_strokes = target_drawing.strokes_for_write();
231 bke::SpanAttributeWriter<int> target_material_indices =
232 target_strokes.attributes_for_write().lookup_or_add_for_write_span<int>(
233 "material_index", bke::AttrDomain::Curve);
234
235 for (const int i : target_drawing.strokes().curves_range()) {
236 Material *source_material = BKE_object_material_get(source_object,
237 source_material_indices[i] + 1);
238 BLI_assert(source_material != nullptr);
239
240 bool found = false;
241 for (const int target_index : IndexRange(target_object->totcol)) {
242 Material *target_material = BKE_object_material_get(target_object, target_index + 1);
243 if (source_material == target_material) {
244 found = true;
245 break;
246 }
247 }
248
249 if (!found) {
250 BKE_object_material_slot_add(&bmain, target_object);
252 target_object,
253 source_material,
254 target_object->totcol,
256 }
257
258 target_material_indices.span[i] = BKE_object_material_index_get(target_object,
259 source_material);
260 }
261
262 target_material_indices.finish();
263 MutableSpan<float3> positions = target_strokes.positions_for_write();
264 threading::parallel_for(positions.index_range(), 4096, [&](IndexRange range) {
265 for (const int i : range) {
266 positions[i] = math::transform_point(to_target, positions[i]);
267 }
268 });
269 if (drawing_placement) {
270 threading::parallel_for(positions.index_range(), 4096, [&](IndexRange range) {
271 for (const int i : range) {
272 positions[i] = drawing_placement->reproject(positions[i]);
273 }
274 });
275 }
276 }
277 }
278 }
279 scene.r.cfra = prior_frame;
281
287
288 WM_cursor_wait(false);
289 return OPERATOR_FINISHED;
290}
291
293{
294 const Object *obact = CTX_data_active_object(C);
295
296 /* Check if grease pencil or empty for dupli groups. */
297 if ((obact == nullptr) || (obact->mode != OB_MODE_OBJECT) ||
299 {
300 return false;
301 }
302
303 /* Only if the current view is 3D View. */
304 const ScrArea *area = CTX_wm_area(C);
305 return (area && area->spacetype);
306}
307
309{
310 ot->name = "Bake Object Transform to Grease Pencil";
311 ot->idname = "GREASE_PENCIL_OT_bake_grease_pencil_animation";
312 ot->description = "Bake Grease Pencil object transform to Grease Pencil keyframes";
313
317
319
320 RNA_def_int(ot->srna, "frame_start", 1, 1, 100000, "Start Frame", "The start frame", 1, 100000);
321
322 PropertyRNA *prop = RNA_def_int(
323 ot->srna, "frame_end", 250, 1, 100000, "End Frame", "The end frame of animation", 1, 100000);
325
326 RNA_def_int(ot->srna, "step", 1, 1, 100, "Step", "Step between generated frames", 1, 100);
327
328 RNA_def_boolean(ot->srna,
329 "only_selected",
330 false,
331 "Only Selected Keyframes",
332 "Convert only selected keyframes");
334 ot->srna, "frame_target", 1, 1, 100000, "Target Frame", "Destination frame", 1, 100000);
335
336 static const EnumPropertyItem rna_grease_pencil_reproject_type_items[] = {
337 {int(ReprojectMode::Keep), "KEEP", 0, "No Reproject", ""},
339 "FRONT",
340 0,
341 "Front",
342 "Reproject the strokes using the X-Z plane"},
343 {int(ReprojectMode::Side), "SIDE", 0, "Side", "Reproject the strokes using the Y-Z plane"},
344 {int(ReprojectMode::Top), "TOP", 0, "Top", "Reproject the strokes using the X-Y plane"},
346 "VIEW",
347 0,
348 "View",
349 "Reproject the strokes to end up on the same plane, as if drawn from the current "
350 "viewpoint "
351 "using 'Cursor' Stroke Placement"},
353 "CURSOR",
354 0,
355 "Cursor",
356 "Reproject the strokes using the orientation of 3D cursor"},
357 {0, nullptr, 0, nullptr, nullptr},
358 };
359
360 RNA_def_enum(ot->srna,
361 "project_type",
362 rna_grease_pencil_reproject_type_items,
364 "Projection Type",
365 "");
366}
367} // namespace blender::ed::greasepencil
368
Functions for backward compatibility with the legacy Action API.
AnimData * BKE_animdata_from_id(const ID *id)
Definition anim_data.cc:82
#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.
void free_object_duplilist(ListBase *lb)
ListBase * object_duplilist(Depsgraph *depsgraph, Scene *sce, Object *ob, blender::Set< const Object * > *include_objects=nullptr)
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:2697
#define BLI_assert(a)
Definition BLI_assert.h:46
#define LISTBASE_FOREACH(type, var, list)
#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:1009
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1026
@ OB_MODE_OBJECT
@ OB_EMPTY
@ OB_GREASE_PENCIL
@ OPERATOR_FINISHED
#define C
Definition RandGen.cpp:29
#define ND_OB_ACTIVE
Definition WM_types.hh:437
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define NC_SCENE
Definition WM_types.hh:375
#define NA_ADDED
Definition WM_types.hh:583
#define NC_OBJECT
Definition WM_types.hh:376
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, eCustomDataType 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 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:4226
wmOperatorType * ot
Definition wm_files.cc:4225
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)