Blender V5.0
fbx_import_anim.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2025 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <algorithm>
10
11#include "ANIM_action.hh"
12#include "ANIM_animdata.hh"
13
14#include "BKE_action.hh"
15#include "BKE_fcurve.hh"
16#include "BKE_lib_id.hh"
17#include "BKE_object_types.hh"
18
20#include "BLI_map.hh"
23#include "BLI_set.hh"
24#include "BLI_string.h"
25#include "BLI_vector.hh"
26#include "BLI_vector_set.hh"
27
28#include "DNA_key_types.h"
29#include "DNA_material_types.h"
30#include "DNA_object_types.h"
31
32#include "fbx_import_anim.hh"
33#include "fbx_import_util.hh"
34
35namespace blender::io::fbx {
36
39 int64_t key_count)
40{
41 FCurve *cu = channelbag.fcurve_create_unique(nullptr, descriptor);
42 BLI_assert_msg(cu, "The same F-Curve is being created twice, this is unexpected.");
43 BKE_fcurve_bezt_resize(cu, key_count);
44 return cu;
45}
46
47static void set_curve_sample(FCurve *curve, int64_t key_index, float time, float value)
48{
49 BLI_assert(key_index >= 0 && key_index < curve->totvert);
50 BezTriple &bez = curve->bezt[key_index];
51 bez.vec[1][0] = time;
52 bez.vec[1][1] = value;
53 bez.ipo = BEZT_IPO_LIN;
54 bez.f1 = bez.f2 = bez.f3 = SELECT;
55 bez.h1 = bez.h2 = HD_AUTO_ANIM;
56}
57
59 const ufbx_element *fbx_elem = nullptr;
60 ID *target_id = nullptr;
63 const ufbx_anim_prop *prop_position = nullptr;
64 const ufbx_anim_prop *prop_rotation = nullptr;
65 const ufbx_anim_prop *prop_scale = nullptr;
66 const ufbx_anim_prop *prop_blend_shape = nullptr;
67 const ufbx_anim_prop *prop_focal_length = nullptr;
68 const ufbx_anim_prop *prop_focus_dist = nullptr;
69 const ufbx_anim_prop *prop_mat_diffuse = nullptr;
70};
71
73 const ufbx_anim_layer &flayer)
74{
75 int64_t order = 0;
77 for (const ufbx_anim_prop &fprop : flayer.anim_props) {
78 if (fprop.anim_value->curves[0] == nullptr) {
79 continue;
80 }
81 bool supported_prop = false;
82 //@TODO: "Visibility"?
83 const bool is_position = STREQ(fprop.prop_name.data, "Lcl Translation");
84 const bool is_rotation = STREQ(fprop.prop_name.data, "Lcl Rotation");
85 const bool is_scale = STREQ(fprop.prop_name.data, "Lcl Scaling");
86 const bool is_blend_shape = STREQ(fprop.prop_name.data, "DeformPercent");
87 const bool is_focal_length = STREQ(fprop.prop_name.data, "FocalLength");
88 const bool is_focus_dist = STREQ(fprop.prop_name.data, "FocusDistance");
89 const bool is_diffuse = STREQ(fprop.prop_name.data, "DiffuseColor");
90 if (is_position || is_rotation || is_scale || is_blend_shape || is_focal_length ||
91 is_focus_dist || is_diffuse)
92 {
93 supported_prop = true;
94 }
95
96 if (!supported_prop) {
97 continue;
98 }
99
100 const bool is_anim_camera = is_focal_length || is_focus_dist;
101 const bool is_anim_mat = is_diffuse;
102
103 ID *target_id = nullptr;
104 eRotationModes object_rotmode = ROT_MODE_QUAT;
105
106 if (is_blend_shape) {
107 /* Animating blend shape weight. */
108 Key *target_key = mapping.el_to_shape_key.lookup_default(fprop.element, nullptr);
109 if (target_key != nullptr) {
110 target_id = &target_key->id;
111 }
112 }
113 else if (is_anim_camera) {
114 /* Animating camera property. */
115 if (fprop.element->instances.count > 0) {
116 Object *obj = mapping.el_to_object.lookup_default(&fprop.element->instances[0]->element,
117 nullptr);
118 if (obj != nullptr && obj->type == OB_CAMERA) {
119 target_id = (ID *)obj->data;
120 object_rotmode = static_cast<eRotationModes>(obj->rotmode);
121 }
122 }
123 }
124 else if (is_anim_mat) {
125 /* Animating material property. */
126 Material *mat = mapping.mat_to_material.lookup_default((ufbx_material *)fprop.element,
127 nullptr);
128 if (mat != nullptr) {
129 target_id = (ID *)mat;
130 }
131 }
132 else {
133 /* Animating Bone/Armature/Object property. */
134 const ufbx_node *fnode = ufbx_as_node(fprop.element);
135 Object *obj = nullptr;
136 if (fnode) {
137 obj = mapping.bone_to_armature.lookup_default(fnode, nullptr);
138 }
139 if (obj == nullptr) {
140 obj = mapping.el_to_object.lookup_default(fprop.element, nullptr);
141 }
142 if (obj == nullptr) {
143 continue;
144 }
145 /* Ignore animation of rigged meshes (very hard to handle; matches behavior of python fbx
146 * importer). */
147 if (obj->type == OB_MESH && obj->parent && obj->parent->type == OB_ARMATURE) {
148 continue;
149 }
150 target_id = &obj->id;
151 object_rotmode = static_cast<eRotationModes>(obj->rotmode);
152 }
153
154 if (target_id == nullptr) {
155 continue;
156 }
157
158 ElementAnimations &anims = elem_map.lookup_or_add_default(fprop.element);
159 anims.fbx_elem = fprop.element;
160 anims.order = order++;
161 anims.target_id = target_id;
162 anims.object_rotmode = object_rotmode;
163
164 if (is_position) {
165 anims.prop_position = &fprop;
166 }
167 if (is_rotation) {
168 anims.prop_rotation = &fprop;
169 }
170 if (is_scale) {
171 anims.prop_scale = &fprop;
172 }
173 if (is_blend_shape) {
174 anims.prop_blend_shape = &fprop;
175 }
176 if (is_focal_length) {
177 anims.prop_focal_length = &fprop;
178 }
179 if (is_focus_dist) {
180 anims.prop_focus_dist = &fprop;
181 }
182 if (is_diffuse) {
183 anims.prop_mat_diffuse = &fprop;
184 }
185 }
186
187 /* Sort returned result in the original fbx file order. */
188 Vector<ElementAnimations> animations(elem_map.values().begin(), elem_map.values().end());
189 std::sort(
190 animations.begin(),
191 animations.end(),
192 [](const ElementAnimations &a, const ElementAnimations &b) { return a.order < b.order; });
193 return animations;
194}
195
196static void finalize_curve(FCurve *cu)
197{
198 if (cu != nullptr) {
200 }
201}
202
204 const ElementAnimations &anim,
205 LinearAllocator<> &curve_name_alloc,
207{
208 /* For animated bones, prepend bone path to animation curve path. */
209 std::string rna_prefix;
210 std::string group_name_str = get_fbx_name(anim.fbx_elem->name);
211 const ufbx_node *fnode = ufbx_as_node(anim.fbx_elem);
212 const bool is_bone = mapping.node_is_blender_bone.contains(fnode);
213 if (is_bone) {
214 group_name_str = mapping.node_to_name.lookup_default(fnode, "");
215 rna_prefix = std::string("pose.bones[\"") + group_name_str + "\"].";
216 }
217
218 StringRefNull group_name = curve_name_alloc.copy_string(group_name_str);
219
220 StringRefNull rna_position = curve_name_alloc.copy_string(rna_prefix + "location");
221
222 StringRefNull rna_rotation;
223 int rot_channels = 3;
224 /* Bones are created with quaternion rotation. */
225 eRotationModes rot_mode = is_bone ? ROT_MODE_QUAT : anim.object_rotmode;
226 switch (rot_mode) {
227 case ROT_MODE_QUAT:
228 rna_rotation = curve_name_alloc.copy_string(rna_prefix + "rotation_quaternion");
229 rot_channels = 4;
230 break;
232 rna_rotation = curve_name_alloc.copy_string(rna_prefix + "rotation_axis_angle");
233 rot_channels = 4;
234 break;
235 default:
236 rna_rotation = curve_name_alloc.copy_string(rna_prefix + "rotation_euler");
237 rot_channels = 3;
238 break;
239 }
240
241 StringRefNull rna_scale = curve_name_alloc.copy_string(rna_prefix + "scale");
242
243 /* Fill the f-curve descriptors. */
244 for (int i = 0; i < 3; i++) {
245 r_curve_desc.append({rna_position, i, {}, {}, group_name});
246 }
247 for (int i = 0; i < rot_channels; i++) {
248 r_curve_desc.append({rna_rotation, i, {}, {}, group_name});
249 }
250 for (int i = 0; i < 3; i++) {
251 r_curve_desc.append({rna_scale, i, {}, {}, group_name});
252 }
253}
254
256 const ufbx_anim *fbx_anim,
257 const ElementAnimations &anim,
258 const double fps,
259 const float anim_offset,
260 FCurve **curves)
261{
262 const ufbx_node *fnode = ufbx_as_node(anim.fbx_elem);
263 ufbx_matrix bone_xform = ufbx_identity_matrix;
264 const bool is_bone = mapping.node_is_blender_bone.contains(fnode);
265 if (is_bone) {
266 /* Bone transform curves need to be transformed to the bind transform
267 * in joint-local space:
268 * - Calculate local space bind matrix: inv(parent_bind) * bind
269 * - Invert the result; this will be used to transform loc/rot/scale curves. */
270
271 const bool bone_at_scene_root = fnode->node_depth <= 1;
272 ufbx_matrix world_to_arm = ufbx_identity_matrix;
273 if (!bone_at_scene_root) {
274 Object *arm_obj = mapping.bone_to_armature.lookup_default(fnode, nullptr);
275 if (arm_obj != nullptr) {
277 arm_obj, ufbx_identity_matrix);
278 }
279 }
280
281 bone_xform = mapping.calc_local_bind_matrix(fnode, world_to_arm);
282 bone_xform = ufbx_matrix_invert(&bone_xform);
283 }
284
285 int rot_channels = 3;
286 /* Bones are created with quaternion rotation. */
287 eRotationModes rot_mode = is_bone ? ROT_MODE_QUAT : anim.object_rotmode;
288 switch (rot_mode) {
289 case ROT_MODE_QUAT:
290 rot_channels = 4;
291 break;
293 rot_channels = 4;
294 break;
295 default:
296 rot_channels = 3;
297 break;
298 }
299
300 /* Note: Python importer was always creating all pos/rot/scale curves: "due to all FBX
301 * transform magic, we need to add curves for whole loc/rot/scale in any case".
302 *
303 * Also, we create a full transform keyframe at any point where input pos/rot/scale curves have
304 * a keyframe. It should not be needed if we fully imported curves with all their proper
305 * handles, but again currently this is to match Python importer behavior. */
306 const ufbx_anim_curve *input_curves[9] = {};
307 if (anim.prop_position) {
308 input_curves[0] = anim.prop_position->anim_value->curves[0];
309 input_curves[1] = anim.prop_position->anim_value->curves[1];
310 input_curves[2] = anim.prop_position->anim_value->curves[2];
311 }
312 if (anim.prop_rotation) {
313 input_curves[3] = anim.prop_rotation->anim_value->curves[0];
314 input_curves[4] = anim.prop_rotation->anim_value->curves[1];
315 input_curves[5] = anim.prop_rotation->anim_value->curves[2];
316 }
317 if (anim.prop_scale) {
318 input_curves[6] = anim.prop_scale->anim_value->curves[0];
319 input_curves[7] = anim.prop_scale->anim_value->curves[1];
320 input_curves[8] = anim.prop_scale->anim_value->curves[2];
321 }
322
323 /* Figure out timestamps of where any of input curves have a keyframe. */
324 Set<double> unique_key_times;
325 for (int i = 0; i < 9; i++) {
326 if (input_curves[i] != nullptr) {
327 for (const ufbx_keyframe &key : input_curves[i]->keyframes) {
328 if (key.interpolation == UFBX_INTERPOLATION_CUBIC) {
329 /* Hack: force cubic keyframes to be linear, to match Python importer behavior. */
330 const_cast<ufbx_keyframe &>(key).interpolation = UFBX_INTERPOLATION_LINEAR;
331 }
332 unique_key_times.add(key.time);
333 }
334 }
335 }
336 Vector<double> sorted_key_times(unique_key_times.begin(), unique_key_times.end());
337 std::sort(sorted_key_times.begin(), sorted_key_times.end());
338
339 int64_t pos_index = 0;
340 int64_t rot_index = pos_index + 3;
341 int64_t scale_index = rot_index + rot_channels;
342 int64_t tot_curves = scale_index + 3;
343 for (int64_t i = 0; i < tot_curves; i++) {
344 BLI_assert_msg(curves[i], "fbx: animation curve was not created successfully");
345 BKE_fcurve_bezt_resize(curves[i], sorted_key_times.size());
346 }
347
348 /* Evaluate transforms at all the key times. */
350 for (int64_t i = 0; i < sorted_key_times.size(); i++) {
351 double t = sorted_key_times[i];
352 float tf = float(t * fps + anim_offset);
353 ufbx_transform xform = ufbx_evaluate_transform(fbx_anim, fnode, t);
354
355 if (is_bone) {
356 ufbx_matrix matrix = calc_bone_pose_matrix(xform, *fnode, bone_xform);
357 xform = ufbx_matrix_to_transform(&matrix);
358 }
359
360 set_curve_sample(curves[pos_index + 0], i, tf, float(xform.translation.x));
361 set_curve_sample(curves[pos_index + 1], i, tf, float(xform.translation.y));
362 set_curve_sample(curves[pos_index + 2], i, tf, float(xform.translation.z));
363
364 math::Quaternion quat(xform.rotation.w, xform.rotation.x, xform.rotation.y, xform.rotation.z);
365 switch (rot_mode) {
366 case ROT_MODE_QUAT:
367 /* Ensure shortest interpolation path between consecutive quaternions. */
368 if (i != 0 && math::dot(quat, quat_prev) < 0.0f) {
369 quat = -quat;
370 }
371 quat_prev = quat;
372 set_curve_sample(curves[rot_index + 0], i, tf, quat.w);
373 set_curve_sample(curves[rot_index + 1], i, tf, quat.x);
374 set_curve_sample(curves[rot_index + 2], i, tf, quat.y);
375 set_curve_sample(curves[rot_index + 3], i, tf, quat.z);
376 break;
377 case ROT_MODE_AXISANGLE: {
378 const math::AxisAngle axis_angle = math::to_axis_angle(quat);
379 set_curve_sample(curves[rot_index + 0], i, tf, axis_angle.angle().radian());
380 set_curve_sample(curves[rot_index + 1], i, tf, axis_angle.axis().x);
381 set_curve_sample(curves[rot_index + 2], i, tf, axis_angle.axis().y);
382 set_curve_sample(curves[rot_index + 3], i, tf, axis_angle.axis().z);
383 } break;
384 default: {
385 math::EulerXYZ euler = math::to_euler(quat);
386 set_curve_sample(curves[rot_index + 0], i, tf, euler.x().radian());
387 set_curve_sample(curves[rot_index + 1], i, tf, euler.y().radian());
388 set_curve_sample(curves[rot_index + 2], i, tf, euler.z().radian());
389 } break;
390 }
391
392 set_curve_sample(curves[scale_index + 0], i, tf, float(xform.scale.x));
393 set_curve_sample(curves[scale_index + 1], i, tf, float(xform.scale.y));
394 set_curve_sample(curves[scale_index + 2], i, tf, float(xform.scale.z));
395 }
396}
397
398static void create_camera_curves(const ufbx_metadata &metadata,
399 const ElementAnimations &anim,
400 animrig::Channelbag &channelbag,
401 const double fps,
402 const float anim_offset)
403{
404 if (anim.target_id == nullptr || GS(anim.target_id->name) != ID_CA) {
405 return;
406 }
407
408 if (anim.prop_focal_length != nullptr) {
409 const ufbx_anim_curve *input_curve = anim.prop_focal_length->anim_value->curves[0];
410 FCurve *curve = create_fcurve(channelbag, {"lens", 0}, input_curve->keyframes.count);
411 for (int i = 0; i < input_curve->keyframes.count; i++) {
412 const ufbx_keyframe &fkey = input_curve->keyframes[i];
413 float tf = float(fkey.time * fps + anim_offset);
414 float val = float(fkey.value);
415 set_curve_sample(curve, i, tf, val);
416 }
417 finalize_curve(curve);
418 }
419
420 if (anim.prop_focus_dist != nullptr) {
421 const ufbx_anim_curve *input_curve = anim.prop_focus_dist->anim_value->curves[0];
422 FCurve *curve = create_fcurve(
423 channelbag, {"dof.focus_distance", 0}, input_curve->keyframes.count);
424 for (int i = 0; i < input_curve->keyframes.count; i++) {
425 const ufbx_keyframe &fkey = input_curve->keyframes[i];
426 float tf = float(fkey.time * fps + anim_offset);
427 /* Animation curves containing camera focus distance have values multiplied by 1000.0 */
428 float val = float(fkey.value / 1000.0 * metadata.geometry_scale * metadata.root_scale);
429 set_curve_sample(curve, i, tf, val);
430 }
431 finalize_curve(curve);
432 }
433}
434
436 bAction *action,
437 animrig::Channelbag &channelbag,
438 const double fps,
439 const float anim_offset)
440{
441 if (anim.target_id == nullptr || GS(anim.target_id->name) != ID_MA) {
442 return;
443 }
444
445 const char *rna_path_1 = "diffuse_color";
446 const char *rna_path_2 = "nodes[\"Principled BSDF\"].inputs[0].default_value";
447
448 /* Also create animation curves for the node tree diffuse color input. */
449 Material *target_mat = (Material *)anim.target_id;
450 ID *target_ntree = (ID *)target_mat->nodetree;
451 animrig::Action &act = action->wrap();
452 const animrig::Slot *slot = animrig::assign_action_ensure_slot_for_keying(act, *target_ntree);
453 BLI_assert(slot != nullptr);
454 UNUSED_VARS_NDEBUG(slot);
455 animrig::Channelbag &chbag_node = animrig::action_channelbag_ensure(*action, *target_ntree);
456
457 if (anim.prop_mat_diffuse != nullptr) {
458 for (int ch = 0; ch < 3; ch++) {
459 const ufbx_anim_curve *input_curve = anim.prop_mat_diffuse->anim_value->curves[ch];
460 FCurve *curve_1 = create_fcurve(channelbag, {rna_path_1, ch}, input_curve->keyframes.count);
461 FCurve *curve_2 = create_fcurve(chbag_node, {rna_path_2, ch}, input_curve->keyframes.count);
462 for (int i = 0; i < input_curve->keyframes.count; i++) {
463 const ufbx_keyframe &fkey = input_curve->keyframes[i];
464 float tf = float(fkey.time * fps + anim_offset);
465 float val = float(fkey.value);
466 set_curve_sample(curve_1, i, tf, val);
467 set_curve_sample(curve_2, i, tf, val);
468 }
469 finalize_curve(curve_1);
470 finalize_curve(curve_2);
471 }
472 }
473}
474
476 animrig::Channelbag &channelbag,
477 const double fps,
478 const float anim_offset)
479{
480 const ufbx_blend_channel *fchan = ufbx_as_blend_channel(anim.prop_blend_shape->element);
481 BLI_assert(fchan != nullptr);
482 std::string rna_path = std::string("key_blocks[\"") + fchan->target_shape->name.data +
483 "\"].value";
484 const ufbx_anim_curve *input_curve = anim.prop_blend_shape->anim_value->curves[0];
485 FCurve *curve = create_fcurve(channelbag, {rna_path, 0}, input_curve->keyframes.count);
486 for (int i = 0; i < input_curve->keyframes.count; i++) {
487 const ufbx_keyframe &fkey = input_curve->keyframes[i];
488 double t = fkey.time;
489 float tf = float(t * fps + anim_offset);
490 float val = float(fkey.value / 100.0); /* FBX shape weights are 0..100 range. */
491 set_curve_sample(curve, i, tf, val);
492 }
493 finalize_curve(curve);
494}
495
497 const ufbx_scene &fbx,
498 const FbxElementMapping &mapping,
499 const double fps,
500 const float anim_offset)
501{
502 /* Note: mixing is completely ignored for now, each layer results in an independent set of
503 * actions. */
504 for (const ufbx_anim_stack *fstack : fbx.anim_stacks) {
505 for (const ufbx_anim_layer *flayer : fstack->layers) {
506 Vector<ElementAnimations> animations = gather_animated_properties(mapping, *flayer);
507 if (animations.is_empty()) {
508 continue;
509 }
510
511 /* Create action for this layer. */
512 std::string action_name = fstack->name.data;
513 if (!STREQ(fstack->name.data, flayer->name.data) && fstack->layers.count != 1) {
514 action_name += '|';
515 action_name += flayer->name.data;
516 }
517 animrig::Action &action = animrig::action_add(bmain, action_name);
518 id_fake_user_set(&action.id);
519 action.layer_keystrip_ensure();
520 animrig::StripKeyframeData &strip_data =
521 action.layer(0)->strip(0)->data<animrig::StripKeyframeData>(action);
522
523 /* Figure out the set of IDs that are animated. We want to preserve the order
524 * of this set to match order of animations inside the FBX file. */
525 VectorSet<ID *> animated_ids;
527 for (const ElementAnimations &anim : animations) {
528 animated_ids.add(anim.target_id);
530 anim.target_id);
531 anims.append(&anim);
532 }
533
534 /* Create action slots for each animated ID. */
535 for (ID *id : animated_ids) {
536 /* Create a slot for this ID. */
537 BLI_assert(id != nullptr);
538 const std::string slot_name = id->name;
539 animrig::Slot &slot = action.slot_add_for_id_type(GS(id->name));
540 action.slot_identifier_define(slot, slot_name);
541
542 /* Assign this action & slot to ID. */
543 const AnimData *adt = BKE_animdata_ensure_id(id);
544 BLI_assert_msg(adt != nullptr, "fbx: could not create animation data for an ID");
545 if (adt->action == nullptr) {
546 bool ok = animrig::assign_action(&action, *id);
547 BLI_assert_msg(ok, "fbx: could not assign action to ID");
549 }
553 "fbx: failed to assign slot to ID");
555 }
556 animrig::Channelbag &channelbag = strip_data.channelbag_for_slot_ensure(slot);
557
558 /* Create animation curves for this ID. */
559 Vector<const ElementAnimations *> id_anims = id_to_anims.lookup(id);
560 /* Batch create the transform curves: creating them one by one is not very fast,
561 * especially for armatures where many bones often are animated. So first create
562 * their descriptors, then create the f-curves in one step, and finally fill their data. */
564 Vector<int64_t> anim_transform_curve_index(id_anims.size());
565 LinearAllocator name_alloc;
566 for (const int64_t index : id_anims.index_range()) {
567 const ElementAnimations *anim = id_anims[index];
568 if (anim->prop_position || anim->prop_rotation || anim->prop_scale) {
569 anim_transform_curve_index[index] = curve_desc.size();
570 create_transform_curve_desc(mapping, *anim, name_alloc, curve_desc);
571 }
572 else {
573 anim_transform_curve_index[index] = -1;
574 }
575 }
576 blender::Vector<FCurve *> transform_curves;
577 if (!curve_desc.is_empty()) {
578 transform_curves = channelbag.fcurve_create_many(nullptr, curve_desc.as_span());
579 }
580
581 for (const int64_t index : id_anims.index_range()) {
582 const ElementAnimations *anim = id_anims[index];
583 if (anim->prop_position || anim->prop_rotation || anim->prop_scale) {
585 flayer->anim,
586 *anim,
587 fps,
588 anim_offset,
589 transform_curves.data() +
590 anim_transform_curve_index[index]);
591 }
592 if (anim->prop_focal_length || anim->prop_focus_dist) {
593 create_camera_curves(fbx.metadata, *anim, channelbag, fps, anim_offset);
594 }
595 if (anim->prop_mat_diffuse) {
596 create_material_curves(*anim, &action, channelbag, fps, anim_offset);
597 }
598 if (anim->prop_blend_shape) {
599 create_blend_shape_curves(*anim, channelbag, fps, anim_offset);
600 }
601 }
602
603 for (FCurve *curve : transform_curves) {
604 finalize_curve(curve);
605 }
606 }
607 }
608 }
609}
610
611} // namespace blender::io::fbx
Functions and classes to work with Actions.
Functions to work with AnimData.
Blender kernel action and pose functionality.
AnimData * BKE_animdata_ensure_id(ID *id)
Definition anim_data.cc:97
void BKE_fcurve_handles_recalc(FCurve *fcu)
void BKE_fcurve_bezt_resize(FCurve *fcu, int new_totvert)
void id_fake_user_set(ID *id)
Definition lib_id.cc:396
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
#define UNUSED_VARS_NDEBUG(...)
#define STREQ(a, b)
@ ID_CA
@ ID_MA
eRotationModes
@ ROT_MODE_QUAT
@ ROT_MODE_AXISANGLE
@ HD_AUTO_ANIM
@ BEZT_IPO_LIN
Object is a sort of wrapper for general info.
@ OB_CAMERA
@ OB_ARMATURE
@ OB_MESH
long long int int64_t
SubIterator begin() const
Definition BLI_map.hh:768
SubIterator end() const
Definition BLI_map.hh:778
Value lookup_default(const Key &key, const Value &default_value) const
Definition BLI_map.hh:570
bool contains(const Key &key) const
Definition BLI_set.hh:310
StringRefNull copy_string(StringRef str)
Value & lookup_or_add_default(const Key &key)
Definition BLI_map.hh:639
ValueIterator values() const &
Definition BLI_map.hh:884
const Value & lookup(const Key &key) const
Definition BLI_map.hh:545
Iterator begin() const
Definition BLI_set.hh:480
Iterator end() const
Definition BLI_set.hh:490
bool add(const Key &key)
Definition BLI_set.hh:248
int64_t size() const
void append(const T &value)
bool is_empty() const
IndexRange index_range() const
Span< T > as_span() const
const Layer * layer(int64_t index) const
Slot & slot_add_for_id_type(ID_Type idtype)
void slot_identifier_define(Slot &slot, StringRefNull new_identifier)
Vector< FCurve * > fcurve_create_many(Main *bmain, Span< FCurveDescriptor > fcurve_descriptors)
FCurve * fcurve_create_unique(Main *bmain, const FCurveDescriptor &fcurve_descriptor)
const Strip * strip(int64_t index) const
static constexpr slot_handle_t unassigned
Channelbag & channelbag_for_slot_ensure(const Slot &slot)
const T & data(const Action &owning_action) const
nullptr float
#define SELECT
#define GS(x)
descriptor
Slot * assign_action_ensure_slot_for_keying(Action &action, ID &animated_id)
Action & action_add(Main &bmain, StringRefNull name)
Channelbag & action_channelbag_ensure(bAction &dna_action, ID &animated_id)
bool assign_action(bAction *action, ID &animated_id)
ActionSlotAssignmentResult assign_action_slot(Slot *slot_to_assign, ID &animated_id)
static void create_transform_curve_desc(const FbxElementMapping &mapping, const ElementAnimations &anim, LinearAllocator<> &curve_name_alloc, Vector< animrig::FCurveDescriptor > &r_curve_desc)
static void create_camera_curves(const ufbx_metadata &metadata, const ElementAnimations &anim, animrig::Channelbag &channelbag, const double fps, const float anim_offset)
const char * get_fbx_name(const ufbx_string &name, const char *def)
static void create_transform_curve_data(const FbxElementMapping &mapping, const ufbx_anim *fbx_anim, const ElementAnimations &anim, const double fps, const float anim_offset, FCurve **curves)
static void create_material_curves(const ElementAnimations &anim, bAction *action, animrig::Channelbag &channelbag, const double fps, const float anim_offset)
static void set_curve_sample(FCurve *curve, int64_t key_index, float time, float value)
static void finalize_curve(FCurve *cu)
static Vector< ElementAnimations > gather_animated_properties(const FbxElementMapping &mapping, const ufbx_anim_layer &flayer)
void import_animations(Main &bmain, const ufbx_scene &fbx, const FbxElementMapping &mapping, const double fps, const float anim_offset)
static FCurve * create_fcurve(animrig::Channelbag &channelbag, const animrig::FCurveDescriptor &descriptor, int64_t key_count)
ufbx_matrix calc_bone_pose_matrix(const ufbx_transform &local_xform, const ufbx_node &node, const ufbx_matrix &local_bind_inv_matrix)
static void create_blend_shape_curves(const ElementAnimations &anim, animrig::Channelbag &channelbag, const double fps, const float anim_offset)
QuaternionBase< float > Quaternion
AxisAngleBase< float, AngleRadianBase< float > > AxisAngle
EulerXYZBase< float > EulerXYZ
T dot(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
AxisAngleBase< T, AngleT > to_axis_angle(const EulerXYZBase< T > &euler)
Euler3Base< T > to_euler(const AxisAngleBase< T, AngleT > &axis_angle, EulerOrder order)
bAction * action
int32_t slot_handle
float vec[3][3]
BezTriple * bezt
Definition DNA_ID.h:414
char name[258]
Definition DNA_ID.h:432
struct bNodeTree * nodetree
const ufbx_anim_prop * prop_focal_length
const ufbx_anim_prop * prop_blend_shape
const ufbx_anim_prop * prop_mat_diffuse
const ufbx_anim_prop * prop_focus_dist
Map< const ufbx_node *, Object * > bone_to_armature
Map< const ufbx_material *, Material * > mat_to_material
Map< const ufbx_node *, std::string > node_to_name
Set< const ufbx_node * > node_is_blender_bone
Map< const Object *, ufbx_matrix > armature_world_to_arm_pose_matrix
ufbx_matrix calc_local_bind_matrix(const ufbx_node *bone_node, const ufbx_matrix &world_to_arm) const
Map< const ufbx_element *, Object * > el_to_object
Map< const ufbx_element *, Key * > el_to_shape_key
i
Definition text_draw.cc:230