Blender V4.3
BCAnimationSampler.cpp
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2008 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include <algorithm> /* std::find */
6#include <map>
7#include <vector>
8
9#include "BCAnimationCurve.h"
10#include "BCAnimationSampler.h"
11#include "BCMath.h"
12#include "ExportSettings.h"
13#include "collada_utils.h"
14
15#include "BKE_action.hh"
16#include "BKE_constraint.h"
17#include "BKE_key.hh"
18#include "BKE_lib_id.hh"
19#include "BKE_main.hh"
20#include "BKE_material.h"
21
22#include "BLI_listbase.h"
23
24#include "DNA_anim_types.h"
26#include "DNA_key_types.h"
27#include "DNA_scene_types.h"
28
29#include "ED_object.hh"
30
31#include "ANIM_action_legacy.hh"
32
33static std::string EMPTY_STRING;
35
36BCAnimationSampler::BCAnimationSampler(BCExportSettings &export_settings, BCObjectSet &object_set)
37 : export_settings(export_settings)
38{
39 BCObjectSet::iterator it;
40 for (it = object_set.begin(); it != object_set.end(); ++it) {
41 Object *ob = *it;
42 add_object(ob);
43 }
44}
45
47{
48 BCAnimationObjectMap::iterator it;
49 for (it = objects.begin(); it != objects.end(); ++it) {
50 BCAnimation *animation = it->second;
51 delete animation;
52 }
53}
54
56{
57 BlenderContext blender_context = export_settings.get_blender_context();
58 BCAnimation *animation = new BCAnimation(blender_context.get_context(), ob);
59 objects[ob] = animation;
60
61 initialize_keyframes(animation->frame_set, ob);
62 initialize_curves(animation->curve_map, ob);
63}
64
66{
67 BCAnimation &animation = *objects[ob];
68 if (animation.curve_map.empty()) {
69 initialize_curves(animation.curve_map, ob);
70 }
71 return &animation.curve_map;
72}
73
74static void get_sample_frames(BCFrameSet &sample_frames,
75 int sampling_rate,
76 bool keyframe_at_end,
77 Scene *scene)
78{
79 sample_frames.clear();
80
81 if (sampling_rate < 1) {
82 return; /* no sample frames in this case */
83 }
84
85 float sfra = scene->r.sfra;
86 float efra = scene->r.efra;
87
88 int frame_index;
89 for (frame_index = nearbyint(sfra); frame_index < efra; frame_index += sampling_rate) {
90 sample_frames.insert(frame_index);
91 }
92
93 if (frame_index >= efra && keyframe_at_end) {
94 sample_frames.insert(efra);
95 }
96}
97
98static bool is_object_keyframe(Object *ob, int frame_index)
99{
100 return false;
101}
102
103static void add_keyframes_from(AnimData *adt, BCFrameSet &frameset)
104{
106 BezTriple *bezt = fcu->bezt;
107 for (int i = 0; i < fcu->totvert; bezt++, i++) {
108 int frame_index = nearbyint(bezt->vec[1][0]);
109 frameset.insert(frame_index);
110 }
111 }
112}
113
114void BCAnimationSampler::check_property_is_animated(
115 BCAnimation &animation, float *ref, float *val, std::string data_path, int length)
116{
117 for (int array_index = 0; array_index < length; array_index++) {
118 if (!bc_in_range(ref[length], val[length], 0.00001)) {
119 BCCurveKey key(BC_ANIMATION_TYPE_OBJECT, data_path, array_index);
120 BCAnimationCurveMap::iterator it = animation.curve_map.find(key);
121 if (it == animation.curve_map.end()) {
122 animation.curve_map[key] = new BCAnimationCurve(key, animation.get_reference());
123 }
124 }
125 }
126}
127
128void BCAnimationSampler::update_animation_curves(BCAnimation &animation,
130 Object *ob,
131 int frame)
132{
133 BCAnimationCurveMap::iterator it;
134 for (it = animation.curve_map.begin(); it != animation.curve_map.end(); ++it) {
135 BCAnimationCurve *curve = it->second;
136 if (curve->is_transform_curve()) {
137 curve->add_value_from_matrix(sample, frame);
138 }
139 else {
140 curve->add_value_from_rna(frame);
141 }
142 }
143}
144
145BCSample &BCAnimationSampler::sample_object(Object *ob, int frame_index, bool for_opensim)
146{
147 BCSample &ob_sample = sample_data.add(ob, frame_index);
148#if 0
149 if (export_settings.get_apply_global_orientation()) {
150 const BCMatrix &global_transform = export_settings.get_global_transform();
151 ob_sample.get_matrix(global_transform);
152 }
153#endif
154
155 if (ob->type == OB_ARMATURE) {
156 LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
157 Bone *bone = pchan->bone;
158 Matrix bmat;
159 if (bc_bone_matrix_local_get(ob, bone, bmat, for_opensim)) {
160
161 ob_sample.add_bone_matrix(bone, bmat);
162 }
163 }
164 }
165 return ob_sample;
166}
167
168void BCAnimationSampler::sample_scene(BCExportSettings &export_settings, bool keyframe_at_end)
169{
170 BlenderContext blender_context = export_settings.get_blender_context();
171 int sampling_rate = export_settings.get_sampling_rate();
172 bool for_opensim = export_settings.get_open_sim();
173 bool keep_keyframes = export_settings.get_keep_keyframes();
174 BC_export_animation_type export_animation_type = export_settings.get_export_animation_type();
175
176 Scene *scene = blender_context.get_scene();
177 BCFrameSet scene_sample_frames;
178 get_sample_frames(scene_sample_frames, sampling_rate, keyframe_at_end, scene);
179
180 int startframe = scene->r.sfra;
181 int endframe = scene->r.efra;
182
183 for (int frame_index = startframe; frame_index <= endframe; frame_index++) {
184 /* Loop over all frames and decide for each frame if sampling is necessary */
185 bool is_scene_sample_frame = false;
186 bool needs_update = true;
187 if (scene_sample_frames.find(frame_index) != scene_sample_frames.end()) {
188 bc_update_scene(blender_context, frame_index);
189 needs_update = false;
190 is_scene_sample_frame = true;
191 }
192
193 bool needs_sampling = is_scene_sample_frame || keep_keyframes ||
194 export_animation_type == BC_ANIMATION_EXPORT_KEYS;
195 if (!needs_sampling) {
196 continue;
197 }
198
199 BCAnimationObjectMap::iterator obit;
200 for (obit = objects.begin(); obit != objects.end(); ++obit) {
201 Object *ob = obit->first;
202 BCAnimation *animation = obit->second;
203 BCFrameSet &object_keyframes = animation->frame_set;
204 if (is_scene_sample_frame || object_keyframes.find(frame_index) != object_keyframes.end()) {
205
206 if (needs_update) {
207 bc_update_scene(blender_context, frame_index);
208 needs_update = false;
209 }
210
211 BCSample &sample = sample_object(ob, frame_index, for_opensim);
212 update_animation_curves(*animation, sample, ob, frame_index);
213 }
214 }
215 }
216}
217
219 ListBase *conlist,
220 std::set<Object *> &animated_objects)
221{
222 LISTBASE_FOREACH (bConstraint *, con, conlist) {
223 ListBase targets = {nullptr, nullptr};
224
225 if (!bc_validateConstraints(con)) {
226 continue;
227 }
228
229 if (BKE_constraint_targets_get(con, &targets)) {
230 Object *obtar;
231 bool found = false;
232
233 LISTBASE_FOREACH (bConstraintTarget *, ct, &targets) {
234 obtar = ct->tar;
235 if (obtar) {
236 if (animated_objects.find(obtar) != animated_objects.end()) {
237 found = true;
238 break;
239 }
240 }
241 }
242 BKE_constraint_targets_flush(con, &targets, true);
243 return found;
244 }
245 }
246 return false;
247}
248
249void BCAnimationSampler::find_depending_animated(std::set<Object *> &animated_objects,
250 std::set<Object *> &candidates)
251{
252 bool found_more;
253 do {
254 found_more = false;
255 std::set<Object *>::iterator it;
256 for (it = candidates.begin(); it != candidates.end(); ++it) {
257 Object *cob = *it;
259 if (is_animated_by_constraint(cob, conlist, animated_objects)) {
260 animated_objects.insert(cob);
261 candidates.erase(cob);
262 found_more = true;
263 break;
264 }
265 }
266 } while (found_more && !candidates.empty());
267}
268
269void BCAnimationSampler::get_animated_from_export_set(std::set<Object *> &animated_objects,
270 LinkNode &export_set)
271{
272 /* Check if this object is animated. That is: Check if it has its own action, or:
273 *
274 * - Check if it has constraints to other objects.
275 * - at least one of the other objects is animated as well.
276 */
277
278 animated_objects.clear();
279 std::set<Object *> candidates;
280
281 LinkNode *node;
282 for (node = &export_set; node; node = node->next) {
283 Object *cob = (Object *)node->link;
284 if (bc_has_animations(cob)) {
285 animated_objects.insert(cob);
286 }
287 else {
288 ListBase conlist = cob->constraints;
289 if (conlist.first) {
290 candidates.insert(cob);
291 }
292 }
293 }
294 find_depending_animated(animated_objects, candidates);
295}
296
298{
299 sample_data.get_frames(ob, frames);
300}
301
303{
304 sample_data.get_frames(ob, bone, frames);
305}
306
308{
309 sample_data.get_matrices(ob, bone, samples);
310 return bc_is_animated(samples);
311}
312
314{
315 sample_data.get_matrices(ob, samples);
316 return bc_is_animated(samples);
317}
318
319#if 0
332void BCAnimationSampler::add_value_set(BCAnimationCurve &curve,
333 BCFrameSampleMap &samples,
334 BC_export_animation_type animation_type)
335{
336 int array_index = curve.get_array_index();
337 const BC_animation_transform_type tm_type = curve.get_transform_type();
338
339 BCFrameSampleMap::iterator it;
340 for (it = samples.begin(); it != samples.end(); ++it) {
341 const int frame_index = nearbyint(it->first);
342 if (animation_type == BC_ANIMATION_EXPORT_SAMPLES || curve.is_keyframe(frame_index)) {
343
344 const BCSample *sample = it->second;
345 float val = 0;
346
347 int subindex = curve.get_subindex();
348 bool good;
349 if (subindex == -1) {
350 good = sample->get_value(tm_type, array_index, &val);
351 }
352 else {
353 good = sample->get_value(tm_type, array_index, &val, subindex);
354 }
355
356 if (good) {
357 curve.add_value(val, frame_index);
358 }
359 }
360 }
361 curve.remove_unused_keyframes();
362 curve.calchandles();
363}
364#endif
365
366void BCAnimationSampler::generate_transform(Object *ob,
367 const BCCurveKey &key,
368 BCAnimationCurveMap &curves)
369{
370 BCAnimationCurveMap::const_iterator it = curves.find(key);
371 if (it == curves.end()) {
372 curves[key] = new BCAnimationCurve(key, ob);
373 }
374}
375
376void BCAnimationSampler::generate_transforms(Object *ob,
377 const std::string prep,
378 const BC_animation_type type,
379 BCAnimationCurveMap &curves)
380{
381 generate_transform(ob, BCCurveKey(type, prep + "location", 0), curves);
382 generate_transform(ob, BCCurveKey(type, prep + "location", 1), curves);
383 generate_transform(ob, BCCurveKey(type, prep + "location", 2), curves);
384 generate_transform(ob, BCCurveKey(type, prep + "rotation_euler", 0), curves);
385 generate_transform(ob, BCCurveKey(type, prep + "rotation_euler", 1), curves);
386 generate_transform(ob, BCCurveKey(type, prep + "rotation_euler", 2), curves);
387 generate_transform(ob, BCCurveKey(type, prep + "scale", 0), curves);
388 generate_transform(ob, BCCurveKey(type, prep + "scale", 1), curves);
389 generate_transform(ob, BCCurveKey(type, prep + "scale", 2), curves);
390}
391
392void BCAnimationSampler::generate_transforms(Object *ob, Bone *bone, BCAnimationCurveMap &curves)
393{
394 std::string prep = "pose.bones[\"" + std::string(bone->name) + "\"].";
395 generate_transforms(ob, prep, BC_ANIMATION_TYPE_BONE, curves);
396
397 LISTBASE_FOREACH (Bone *, child, &bone->childbase) {
398 generate_transforms(ob, child, curves);
399 }
400}
401
402void BCAnimationSampler::initialize_keyframes(BCFrameSet &frameset, Object *ob)
403{
404 frameset.clear();
405 add_keyframes_from(ob->adt, frameset);
408
409 for (int a = 0; a < ob->totcol; a++) {
410 Material *ma = BKE_object_material_get(ob, a + 1);
412 }
413}
414
415void BCAnimationSampler::initialize_curves(BCAnimationCurveMap &curves, Object *ob)
416{
418
419 for (FCurve *fcu : blender::animrig::legacy::fcurves_for_assigned_action(ob->adt)) {
420 object_type = BC_ANIMATION_TYPE_OBJECT;
421 if (ob->type == OB_ARMATURE) {
422 char boneName[MAXBONENAME];
423 if (BLI_str_quoted_substr(fcu->rna_path, "pose.bones[", boneName, sizeof(boneName))) {
424 object_type = BC_ANIMATION_TYPE_BONE;
425 }
426 }
427
428 /* Adding action curves on object */
429 BCCurveKey key(object_type, fcu->rna_path, fcu->array_index);
430 curves[key] = new BCAnimationCurve(key, ob, fcu);
431 }
432
433 /* Add missing curves */
434 object_type = BC_ANIMATION_TYPE_OBJECT;
435 generate_transforms(ob, EMPTY_STRING, object_type, curves);
436 if (ob->type == OB_ARMATURE) {
437 bArmature *arm = (bArmature *)ob->data;
438 LISTBASE_FOREACH (Bone *, root_bone, &arm->bonebase) {
439 generate_transforms(ob, root_bone, curves);
440 }
441 }
442
443 /* Add curves on Object->data actions */
444 AnimData *adt = nullptr;
445 if (ob->type == OB_CAMERA) {
447 object_type = BC_ANIMATION_TYPE_CAMERA;
448 }
449 else if (ob->type == OB_LAMP) {
450 adt = bc_getSceneLightAnimData(ob);
451 object_type = BC_ANIMATION_TYPE_LIGHT;
452 }
453
454 /* Add light action or Camera action */
455 for (FCurve *fcu : blender::animrig::legacy::fcurves_for_assigned_action(adt)) {
456 BCCurveKey key(object_type, fcu->rna_path, fcu->array_index);
457 curves[key] = new BCAnimationCurve(key, ob, fcu);
458 }
459
460 /* Add curves on Object->material actions. */
461 object_type = BC_ANIMATION_TYPE_MATERIAL;
462 for (int a = 0; a < ob->totcol; a++) {
463 /* Export Material parameter animations. */
464 Material *ma = BKE_object_material_get(ob, a + 1);
465 if (ma) {
467 // isMatAnim = true;
468 for (FCurve *fcu : blender::animrig::legacy::fcurves_for_assigned_action(adt)) {
469 BCCurveKey key(object_type, fcu->rna_path, fcu->array_index, a);
470 curves[key] = new BCAnimationCurve(key, ob, fcu);
471 }
472 }
473 }
474}
475
476/* ==================================================================== */
477
479{
480 BCSample *sample = new BCSample(ob);
481 sampleMap[ob] = sample;
482 return *sample;
483}
484
486{
487 BCSampleMap::const_iterator it = sampleMap.find(ob);
488 if (it == sampleMap.end()) {
489 return nullptr;
490 }
491 return it->second;
492}
493
495{
496 BCSampleMap::const_iterator it = sampleMap.find(ob);
497 if (it == sampleMap.end()) {
498 return nullptr;
499 }
500 BCSample *sample = it->second;
501 return &sample->get_matrix();
502}
503
505{
506 BCSampleMap::const_iterator it = sampleMap.find(ob);
507 if (it == sampleMap.end()) {
508 return nullptr;
509 }
510
511 BCSample *sample = it->second;
512 const BCMatrix *bc_bone = sample->get_matrix(bone);
513 return bc_bone;
514}
515
517{
518 return sampleMap.find(ob) != sampleMap.end();
519}
520
522{
523 const BCMatrix *bc_bone = get_sample_matrix(ob, bone);
524 return bc_bone;
525}
526
527/* ==================================================================== */
528
530{
531 BCSampleFrame &frame = sample_frames[frame_index];
532 return frame.add(ob);
533}
534
535/* ====================================================== */
536/* Below are the getters which we need to export the data */
537/* ====================================================== */
538
540{
541 BCSampleFrameMap::iterator it = sample_frames.find(frame_index);
542 BCSampleFrame *frame = (it == sample_frames.end()) ? nullptr : &it->second;
543 return frame;
544}
545
546int BCSampleFrameContainer::get_frames(std::vector<int> &frames) const
547{
548 frames.clear(); /* safety; */
549 BCSampleFrameMap::const_iterator it;
550 for (it = sample_frames.begin(); it != sample_frames.end(); ++it) {
551 frames.push_back(it->first);
552 }
553 return frames.size();
554}
555
557{
558 frames.clear(); /* safety; */
559 BCSampleFrameMap::const_iterator it;
560 for (it = sample_frames.begin(); it != sample_frames.end(); ++it) {
561 const BCSampleFrame &frame = it->second;
562 if (frame.has_sample_for(ob)) {
563 frames.push_back(it->first);
564 }
565 }
566 return frames.size();
567}
568
570{
571 frames.clear(); /* safety; */
572 BCSampleFrameMap::const_iterator it;
573 for (it = sample_frames.begin(); it != sample_frames.end(); ++it) {
574 const BCSampleFrame &frame = it->second;
575 if (frame.has_sample_for(ob, bone)) {
576 frames.push_back(it->first);
577 }
578 }
579 return frames.size();
580}
581
583{
584 samples.clear(); /* safety; */
585 BCSampleFrameMap::const_iterator it;
586 for (it = sample_frames.begin(); it != sample_frames.end(); ++it) {
587 const BCSampleFrame &frame = it->second;
588 const BCSample *sample = frame.get_sample(ob);
589 if (sample) {
590 samples[it->first] = sample;
591 }
592 }
593 return samples.size();
594}
595
597{
598 samples.clear(); /* safety; */
599 BCSampleFrameMap::const_iterator it;
600 for (it = sample_frames.begin(); it != sample_frames.end(); ++it) {
601 const BCSampleFrame &frame = it->second;
602 const BCMatrix *matrix = frame.get_sample_matrix(ob);
603 if (matrix) {
604 samples[it->first] = matrix;
605 }
606 }
607 return samples.size();
608}
609
611{
612 samples.clear(); /* safety; */
613 BCSampleFrameMap::const_iterator it;
614 for (it = sample_frames.begin(); it != sample_frames.end(); ++it) {
615 const BCSampleFrame &frame = it->second;
616 const BCMatrix *sample = frame.get_sample_matrix(ob, bone);
617 if (sample) {
618 samples[it->first] = sample;
619 }
620 }
621 return samples.size();
622}
Functions for backward compatibility with the legacy Action API.
std::string EMPTY_STRING
BC_animation_type
@ BC_ANIMATION_TYPE_MATERIAL
@ BC_ANIMATION_TYPE_LIGHT
@ BC_ANIMATION_TYPE_OBJECT
@ BC_ANIMATION_TYPE_BONE
@ BC_ANIMATION_TYPE_CAMERA
std::map< BCCurveKey, BCAnimationCurve * > BCAnimationCurveMap
std::vector< float > BCFrames
std::set< float > BCFrameSet
static BCAnimationCurveMap BCEmptyAnimationCurves
static void add_keyframes_from(AnimData *adt, BCFrameSet &frameset)
static bool is_object_keyframe(Object *ob, int frame_index)
static void get_sample_frames(BCFrameSet &sample_frames, int sampling_rate, bool keyframe_at_end, Scene *scene)
static std::string EMPTY_STRING
std::map< int, const BCSample * > BCFrameSampleMap
std::map< int, const BCMatrix * > BCMatrixSampleMap
Blender kernel action and pose functionality.
void BKE_constraint_targets_flush(struct bConstraint *con, struct ListBase *targets, bool no_copy)
int BKE_constraint_targets_get(struct bConstraint *con, struct ListBase *r_targets)
General operations, lookup, etc. for materials.
struct Material * BKE_object_material_get(struct Object *ob, short act)
#define LISTBASE_FOREACH(type, var, list)
bool bool BLI_str_quoted_substr(const char *__restrict str, const char *__restrict prefix, char *result, size_t result_maxncpy)
Definition string.c:517
#define MAXBONENAME
@ OB_CAMERA
@ OB_ARMATURE
@ OB_LAMP
BC_export_animation_type
@ BC_ANIMATION_EXPORT_KEYS
@ BC_ANIMATION_EXPORT_SAMPLES
SIMD_FORCE_INLINE btScalar length() const
Return the length of the vector.
Definition btVector3.h:257
bool add_value_from_matrix(const BCSample &sample, int frame)
void get_object_frames(BCFrames &frames, Object *ob)
static void find_depending_animated(std::set< Object * > &animated_objects, std::set< Object * > &candidates)
bool get_bone_samples(BCMatrixSampleMap &samples, Object *ob, Bone *bone)
static void get_animated_from_export_set(std::set< Object * > &animated_objects, LinkNode &export_set)
void get_bone_frames(BCFrames &frames, Object *ob, Bone *bone)
BCAnimationSampler(BCExportSettings &export_settings, BCObjectSet &object_set)
void add_object(Object *ob)
void sample_scene(BCExportSettings &export_settings, bool keyframe_at_end)
static bool is_animated_by_constraint(Object *ob, ListBase *conlist, std::set< Object * > &animated_objects)
bool get_object_samples(BCMatrixSampleMap &samples, Object *ob)
BCAnimationCurveMap * get_curves(Object *ob)
BCFrameSet frame_set
void get_matrix(DMatrix &matrix, bool transposed=false, int precision=-1) const
Definition BCMath.cpp:169
BCSample & add(Object *ob, int frame_index)
int get_frames(std::vector< int > &frames) const
BCSampleFrame * get_frame(int frame_index)
int get_samples(Object *ob, BCFrameSampleMap &samples) const
int get_matrices(Object *ob, BCMatrixSampleMap &samples) const
const BCMatrix * get_sample_matrix(Object *ob) const
bool has_sample_for(Object *ob) const
const BCSample * get_sample(Object *ob) const
BCSample & add(Object *ob)
bool get_value(std::string channel_target, int array_index, float *val) const
const BCMatrix & get_matrix() const
void add_bone_matrix(Bone *bone, Matrix &mat)
bool bc_has_animations(Object *ob)
bool bc_is_animated(BCMatrixSampleMap &values)
bool bc_validateConstraints(bConstraint *con)
void bc_update_scene(BlenderContext &blender_context, float ctime)
bool bc_bone_matrix_local_get(Object *ob, Bone *bone, Matrix &mat, bool for_opensim)
std::set< Object * > BCObjectSet
AnimData * bc_getSceneLightAnimData(Object *ob)
AnimData * bc_getSceneCameraAnimData(Object *ob)
bool bc_in_range(float a, float b, float range)
AnimData * bc_getSceneMaterialAnimData(Material *ma)
OperationNode * node
Vector< FCurve * > fcurves_for_assigned_action(AnimData *adt)
ListBase * constraint_active_list(Object *ob)
float vec[3][3]
char name[64]
ListBase childbase
struct LinkNode * next
void * first
ListBase constraints
struct bPose * pose
struct AnimData * adt
ListBase chanbase