Blender V4.3
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
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.h"
18#include "BKE_scene.hh"
19
20#include "BLI_math_matrix.h"
21#include "BLI_math_matrix.hh"
22#include "BLI_set.hh"
23#include "BLI_string.h"
24
25#include "BLT_translation.hh"
26
28
29#include "DNA_anim_types.h"
30
31#include "ED_grease_pencil.hh"
32#include "ED_object.hh"
33
34#include "RNA_access.hh"
35#include "RNA_define.hh"
36
37#include "WM_types.hh"
38
40static void ensure_valid_frame_end(Main * /*main*/, Scene * /*scene*/, PointerRNA *ptr)
41{
42 const int frame_start = RNA_int_get(ptr, "frame_start");
43 const int frame_end = RNA_int_get(ptr, "frame_end");
44
45 if (frame_end <= frame_start) {
46 RNA_int_set(ptr, "frame_end", frame_start + 1);
47 }
48}
49
51 wmOperator *op,
52 const wmEvent * /*event*/)
53{
54 const Scene *scene = CTX_data_scene(C);
55
56 PropertyRNA *prop_frame_start = RNA_struct_find_property(op->ptr, "frame_start");
57 if (!RNA_property_is_set(op->ptr, prop_frame_start)) {
58 const int frame_start = RNA_property_int_get(op->ptr, prop_frame_start);
59 if (frame_start < scene->r.sfra) {
60 RNA_property_int_set(op->ptr, prop_frame_start, scene->r.sfra);
61 }
62 }
63
64 PropertyRNA *prop_frame_end = RNA_struct_find_property(op->ptr, "frame_end");
65 if (!RNA_property_is_set(op->ptr, prop_frame_end)) {
66 const int frame_end = RNA_property_int_get(op->ptr, prop_frame_end);
67 if (frame_end > scene->r.efra) {
68 RNA_property_int_set(op->ptr, prop_frame_end, scene->r.efra);
69 }
70 }
71
73 C, op, 250, IFACE_("Bake Object Transform to Grease Pencil"), IFACE_("Bake"));
74}
75
77{
78 Vector<Object *> bake_targets;
79 Object *active_object = CTX_data_active_object(&C);
80
81 if (active_object->type == OB_GREASE_PENCIL) {
82 bake_targets.append(active_object);
83 }
84 else if (active_object->type == OB_EMPTY) {
85 ListBase *lb = object_duplilist(&depsgraph, &scene, active_object);
86 LISTBASE_FOREACH (DupliObject *, duplicate_object, lb) {
87 if (duplicate_object->ob->type != OB_GREASE_PENCIL) {
88 continue;
89 }
90
91 bake_targets.append(duplicate_object->ob);
92 }
94 }
95
96 CTX_DATA_BEGIN (&C, Object *, object, selected_objects) {
97 if (object == active_object) {
98 continue;
99 }
100
101 if (object->type == OB_GREASE_PENCIL) {
102 bake_targets.append(object);
103 }
104 else if (object->type == OB_EMPTY) {
105 ListBase *lb = object_duplilist(&depsgraph, &scene, active_object);
106 LISTBASE_FOREACH (DupliObject *, duplicate_object, lb) {
107 if (duplicate_object->ob->type != OB_GREASE_PENCIL) {
108 continue;
109 }
110
111 bake_targets.append(duplicate_object->ob);
112 }
114 }
115 }
117 return bake_targets;
118}
119
121{
122 Set<int> keyframes;
123 for (Object *bake_target : bake_targets) {
124 AnimData *adt = BKE_animdata_from_id(&bake_target->id);
126 for (const int i : IndexRange(fcurve->totvert)) {
127 BezTriple bezt = fcurve->bezt[i];
128 if (bezt.f2 & SELECT) {
129 keyframes.add(int(bezt.vec[1][0]));
130 }
131 }
132 }
133 }
134
135 return keyframes;
136}
137
139{
140 using namespace bke::greasepencil;
141
142 Main &bmain = *CTX_data_main(C);
144 Scene &scene = *CTX_data_scene(C);
145
146 const int step = RNA_int_get(op->ptr, "step");
147
148 const int frame_start = (scene.r.sfra > RNA_int_get(op->ptr, "frame_start")) ?
149 scene.r.sfra :
150 RNA_int_get(op->ptr, "frame_start");
151
152 const int frame_end = (scene.r.efra < RNA_int_get(op->ptr, "frame_end")) ?
153 scene.r.efra :
154 RNA_int_get(op->ptr, "frame_end");
155
156 const bool only_selected = RNA_boolean_get(op->ptr, "only_selected");
157 const int frame_offset = RNA_int_get(op->ptr, "frame_target") - frame_start;
158 const ReprojectMode reproject_mode = ReprojectMode(RNA_enum_get(op->ptr, "project_type"));
159
160 View3D *v3d = CTX_wm_view3d(C);
161 ARegion *region = CTX_wm_region(C);
162
163 Vector<Object *> bake_targets = get_bake_targets(*C, depsgraph, scene);
164
165 uint8_t local_view_bits = (v3d && v3d->localvd) ? v3d->local_view_uid : 0;
166 Object *target_object = object::add_type(
167 C, OB_GREASE_PENCIL, nullptr, scene.cursor.location, float3(0), false, local_view_bits);
168
169 const float4x4 target_imat = math::invert(target_object->object_to_world());
170
171 WM_cursor_wait(true);
172
173 GreasePencil &target = *static_cast<GreasePencil *>(target_object->data);
174 Object *target_object_eval = DEG_get_evaluated_object(&depsgraph, target_object);
175
176 std::optional<Set<int>> keyframes;
177 if (only_selected) {
178 keyframes = get_selected_object_keyframes(bake_targets);
179 }
180
181 const int prior_frame = int(DEG_get_ctime(&depsgraph));
182
183 for (int frame = frame_start; frame <= frame_end; frame++) {
184 if ((frame - frame_start) % step != 0 && frame != frame_end) {
185 continue;
186 }
187
188 if (keyframes && keyframes->contains(frame)) {
189 continue;
190 }
191
192 scene.r.cfra = frame;
194
195 for (Object *source_object : bake_targets) {
196 Object *source_object_eval = DEG_get_evaluated_object(&depsgraph, source_object);
197 GreasePencil &source_eval_grease_pencil = *static_cast<GreasePencil *>(
198 source_object_eval->data);
199 const float4x4 to_target = source_object_eval->object_to_world() * target_imat;
200
201 for (const Layer *source_layer : source_eval_grease_pencil.layers()) {
202 std::string layer_name = fmt::format(
203 "{}_{}", source_object->id.name + 2, source_layer->name());
204 TreeNode *node = target.find_node_by_name(layer_name);
205 if (node == nullptr) {
206 Layer &new_layer = target.add_layer(layer_name);
207 target.set_active_layer(&new_layer);
208 }
209
210 Layer &target_layer = target.find_node_by_name(layer_name)->as_layer();
211 std::optional<DrawingPlacement> drawing_placement;
212 if (reproject_mode != ReprojectMode::Keep) {
213 drawing_placement = DrawingPlacement(
214 scene, *region, *v3d, *target_object_eval, &target_layer, reproject_mode);
215 }
216
217 const GreasePencilFrame *source_frame = source_layer->frame_at(scene.r.cfra);
218 if (source_frame == nullptr) {
219 continue;
220 }
221
222 const int target_frame_num = scene.r.cfra + frame_offset;
223 Drawing &source_drawing = *source_eval_grease_pencil.get_drawing_at(*source_layer,
224 scene.r.cfra);
225 Drawing &target_drawing = *target.insert_frame(target_layer, target_frame_num);
226 target_drawing.strokes_for_write() = source_drawing.strokes();
227
228 bke::AttributeAccessor source_attributes = source_drawing.strokes().attributes();
229 const VArray<int> source_material_indices = *source_attributes.lookup_or_default<int>(
230 "material_index", bke::AttrDomain::Curve, 0);
231 bke::CurvesGeometry &target_strokes = target_drawing.strokes_for_write();
232 bke::SpanAttributeWriter<int> target_material_indices =
233 target_strokes.attributes_for_write().lookup_or_add_for_write_span<int>(
234 "material_index", bke::AttrDomain::Curve);
235
236 for (const int i : target_drawing.strokes().curves_range()) {
237 Material *source_material = BKE_object_material_get(source_object,
238 source_material_indices[i] + 1);
239 BLI_assert(source_material != nullptr);
240
241 bool found = false;
242 for (const int target_index : IndexRange(target_object->totcol)) {
243 Material *target_material = BKE_object_material_get(target_object, target_index + 1);
244 if (source_material == target_material) {
245 found = true;
246 break;
247 }
248 }
249
250 if (!found) {
251 BKE_object_material_slot_add(&bmain, target_object);
253 target_object,
254 source_material,
255 target_object->totcol,
257 }
258
259 target_material_indices.span[i] = BKE_object_material_index_get(target_object,
260 source_material);
261 }
262
263 target_material_indices.finish();
264 MutableSpan<float3> positions = target_strokes.positions_for_write();
265 threading::parallel_for(positions.index_range(), 4096, [&](IndexRange range) {
266 for (const int i : range) {
267 positions[i] = math::transform_point(to_target, positions[i]);
268 }
269 });
270 if (drawing_placement) {
271 threading::parallel_for(positions.index_range(), 4096, [&](IndexRange range) {
272 for (const int i : range) {
273 positions[i] = drawing_placement->reproject(positions[i]);
274 }
275 });
276 }
277 }
278 }
279 }
280 scene.r.cfra = prior_frame;
282
288
289 WM_cursor_wait(false);
290 return OPERATOR_FINISHED;
291}
292
294{
295 const Object *obact = CTX_data_active_object(C);
296
297 /* Check if grease pencil or empty for dupli groups. */
298 if ((obact == nullptr) || (obact->mode != OB_MODE_OBJECT) ||
300 {
301 return false;
302 }
303
304 /* Only if the current view is 3D View. */
305 const ScrArea *area = CTX_wm_area(C);
306 return (area && area->spacetype);
307}
308
310{
311 ot->name = "Bake Object Transform to Grease Pencil";
312 ot->idname = "GREASE_PENCIL_OT_bake_grease_pencil_animation";
313 ot->description = "Bake grease pencil object transform to grease pencil keyframes";
314
318
320
321 RNA_def_int(ot->srna, "frame_start", 1, 1, 100000, "Start Frame", "The start frame", 1, 100000);
322
323 PropertyRNA *prop = RNA_def_int(
324 ot->srna, "frame_end", 250, 1, 100000, "End Frame", "The end frame of animation", 1, 100000);
326
327 RNA_def_int(ot->srna, "step", 1, 1, 100, "Step", "Step between generated frames", 1, 100);
328
330 "only_selected",
331 false,
332 "Only Selected Keyframes",
333 "Convert only selected keyframes");
335 ot->srna, "frame_target", 1, 1, 100000, "Target Frame", "Destination frame", 1, 100000);
336
337 static const EnumPropertyItem rna_grease_pencil_reproject_type_items[] = {
338 {int(ReprojectMode::Keep), "KEEP", 0, "No Reproject", ""},
339 {int(ReprojectMode::Front),
340 "FRONT",
341 0,
342 "Front",
343 "Reproject the strokes using the X-Z plane"},
344 {int(ReprojectMode::Side), "SIDE", 0, "Side", "Reproject the strokes using the Y-Z plane"},
345 {int(ReprojectMode::Top), "TOP", 0, "Top", "Reproject the strokes using the X-Y plane"},
346 {int(ReprojectMode::View),
347 "VIEW",
348 0,
349 "View",
350 "Reproject the strokes to end up on the same plane, as if drawn from the current "
351 "viewpoint "
352 "using 'Cursor' Stroke Placement"},
353 {int(ReprojectMode::Cursor),
354 "CURSOR",
355 0,
356 "Cursor",
357 "Reproject the strokes using the orientation of 3D cursor"},
358 {0, nullptr, 0, nullptr, nullptr},
359 };
360
362 "project_type",
363 rna_grease_pencil_reproject_type_items,
364 int(ReprojectMode::Keep),
365 "Projection Type",
366 "");
367}
368} // namespace blender::ed::greasepencil
369
371{
372 using namespace blender::ed::greasepencil;
373 WM_operatortype_append(GREASE_PENCIL_OT_bake_grease_pencil_animation);
374}
Functions for backward compatibility with the legacy Action API.
AnimData * BKE_animdata_from_id(const ID *id)
Definition anim_data.cc:89
#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.
ListBase * object_duplilist(Depsgraph *depsgraph, Scene *sce, Object *ob)
void free_object_duplilist(ListBase *lb)
General operations, lookup, etc. for materials.
struct Material * BKE_object_material_get(struct Object *ob, short act)
void BKE_object_material_assign(struct Main *bmain, struct Object *ob, struct Material *ma, short act, int assign_type)
@ BKE_MAT_ASSIGN_USERPREF
bool BKE_object_material_slot_add(struct Main *bmain, struct Object *ob)
int BKE_object_material_index_get(Object *ob, const Material *ma)
void BKE_scene_graph_update_for_newframe(Depsgraph *depsgraph)
Definition scene.cc:2647
#define BLI_assert(a)
Definition BLI_assert.h:50
#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)
Object * DEG_get_evaluated_object(const Depsgraph *depsgraph, Object *object)
@ ID_RECALC_SELECT
Definition DNA_ID.h:1068
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1085
@ OB_MODE_OBJECT
@ OB_EMPTY
@ OB_GREASE_PENCIL
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define ND_OB_ACTIVE
Definition WM_types.hh:407
#define NC_SCENE
Definition WM_types.hh:345
#define NA_ADDED
Definition WM_types.hh:552
#define NC_OBJECT
Definition WM_types.hh:346
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
bke::CurvesGeometry & strokes_for_write()
const bke::CurvesGeometry & strokes() const
#define SELECT
const Depsgraph * depsgraph
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
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 int bake_grease_pencil_animation_exec(bContext *C, wmOperator *op)
static int 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:95
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)
unsigned char uint8_t
Definition stdint.h:78
float vec[3][3]
struct View3D * localvd
unsigned short local_view_uid
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
StructRNA * srna
Definition WM_types.hh:1080
struct PointerRNA * ptr
void WM_cursor_wait(bool val)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
PointerRNA * ptr
Definition wm_files.cc:4126
wmOperatorType * ot
Definition wm_files.cc:4125
void WM_operatortype_append(void(*opfunc)(wmOperatorType *))
int 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)