Blender V4.5
usd_skel_convert.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 NVIDIA Corporation. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "usd_skel_convert.hh"
6
9#include "usd_hash_types.hh"
10
11#include <pxr/usd/usdGeom/primvarsAPI.h>
12#include <pxr/usd/usdSkel/animation.h>
13#include <pxr/usd/usdSkel/bindingAPI.h>
14#include <pxr/usd/usdSkel/blendShape.h>
15#include <pxr/usd/usdSkel/cache.h>
16#include <pxr/usd/usdSkel/skeletonQuery.h>
17#include <pxr/usd/usdSkel/utils.h>
18
19#include "DNA_anim_types.h"
20#include "DNA_armature_types.h"
21#include "DNA_key_types.h"
22#include "DNA_mesh_types.h"
23#include "DNA_meshdata_types.h"
24
25#include "BKE_armature.hh"
26#include "BKE_deform.hh"
27#include "BKE_fcurve.hh"
28#include "BKE_key.hh"
29#include "BKE_lib_id.hh"
30#include "BKE_modifier.hh"
31#include "BKE_object_deform.h"
32#include "BKE_report.hh"
33
35#include "BLI_listbase.h"
36#include "BLI_map.hh"
37#include "BLI_math_vector.h"
38#include "BLI_set.hh"
39#include "BLI_span.hh"
40#include "BLI_vector.hh"
41
42#include "ED_armature.hh"
43#include "ED_object_vgroup.hh"
44
45#include "ANIM_action.hh"
46#include "ANIM_animdata.hh"
47
48#include <algorithm>
49#include <string>
50#include <vector>
51
52#include "CLG_log.h"
53static CLG_LogRef LOG = {"io.usd"};
54
55namespace {
56
57/* Utility: return the magnitude of the largest component
58 * of the given vector. */
59inline float max_mag_component(const pxr::GfVec3d &vec)
60{
61 return pxr::GfMax(pxr::GfAbs(vec[0]), pxr::GfAbs(vec[1]), pxr::GfAbs(vec[2]));
62}
63
64void resize_fcurve(FCurve *fcu, uint bezt_count)
65{
66 /* There is no need to resize if the counts match. */
67 if (!fcu || bezt_count == fcu->totvert) {
68 return;
69 }
70
71 BKE_fcurve_bezt_resize(fcu, bezt_count);
72}
73
85void import_skeleton_curves(Main *bmain,
86 Object *arm_obj,
87 const pxr::UsdSkelSkeletonQuery &skel_query,
88 const blender::Map<pxr::TfToken, std::string> &joint_to_bone_map,
90{
91 using namespace blender::io::usd;
92
93 if (!(bmain && arm_obj && skel_query)) {
94 return;
95 }
96
97 if (joint_to_bone_map.is_empty()) {
98 return;
99 }
100
101 const pxr::UsdSkelAnimQuery &anim_query = skel_query.GetAnimQuery();
102
103 if (!anim_query) {
104 /* No animation is defined. */
105 return;
106 }
107
108 std::vector<double> samples;
109 anim_query.GetJointTransformTimeSamples(&samples);
110
111 if (samples.empty()) {
112 return;
113 }
114
115 const size_t num_samples = samples.size();
116
117 /* Create the action on the armature. */
118 bAction *act = blender::animrig::id_action_ensure(bmain, &arm_obj->id);
119 BKE_id_rename(*bmain, act->id, anim_query.GetPrim().GetName().GetText());
120
122 *act, arm_obj->id);
123
124 /* Get the joint paths. */
125 const pxr::VtTokenArray joint_order = skel_query.GetJointOrder();
126
127 /* Create the curves. */
128 constexpr int curves_per_joint = 10; /* 3 loc, 4 rot, 3 scale */
129 blender::LinearAllocator path_alloc;
131 curve_desc.reserve(joint_order.size() * curves_per_joint);
132
133 /* Iterate over the joints and create the corresponding curves for the bones. */
134 for (const pxr::TfToken &joint : joint_order) {
135 const std::string *name = joint_to_bone_map.lookup_ptr(joint);
136 if (name == nullptr) {
137 /* This joint doesn't correspond to any bone we created.
138 * Add null placeholders for the channel curves. */
139 curve_desc.append_n_times({}, curves_per_joint);
140 continue;
141 }
142
143 /* Translation curves. */
144 std::string rna_path = "pose.bones[\"" + *name + "\"].location";
145 blender::StringRefNull path_desc = path_alloc.copy_string(rna_path);
146 curve_desc.append({path_desc, 0, {}, {}, *name});
147 curve_desc.append({path_desc, 1, {}, {}, *name});
148 curve_desc.append({path_desc, 2, {}, {}, *name});
149
150 /* Rotation curves. */
151 rna_path = "pose.bones[\"" + *name + "\"].rotation_quaternion";
152 path_desc = path_alloc.copy_string(rna_path);
153 curve_desc.append({path_desc, 0, {}, {}, *name});
154 curve_desc.append({path_desc, 1, {}, {}, *name});
155 curve_desc.append({path_desc, 2, {}, {}, *name});
156 curve_desc.append({path_desc, 3, {}, {}, *name});
157
158 /* Scale curves. */
159 rna_path = "pose.bones[\"" + *name + "\"].scale";
160 path_desc = path_alloc.copy_string(rna_path);
161 curve_desc.append({path_desc, 0, {}, {}, *name});
162 curve_desc.append({path_desc, 1, {}, {}, *name});
163 curve_desc.append({path_desc, 2, {}, {}, *name});
164 }
165
166 blender::Vector<FCurve *> fcurves = channelbag.fcurve_create_many(nullptr, curve_desc.as_span());
167 BLI_assert_msg(fcurves.size() == curve_desc.size(), "USD: animation curve count mismatch");
168 for (FCurve *fcu : fcurves) {
169 if (fcu != nullptr) {
170 BKE_fcurve_bezt_resize(fcu, num_samples);
171 }
172 }
173
174 /* The curve for each joint represents the transform relative
175 * to the bind transform in joint-local space. I.e.,
176 *
177 * `jointLocalTransform * inv(jointLocalBindTransform)`
178 *
179 * There doesn't appear to be a way to query the joint-local
180 * bind transform through the API, so we have to compute it
181 * ourselves from the world bind transforms and the skeleton
182 * topology.
183 */
184
185 /* Get the world space joint transforms at bind time. */
186 pxr::VtMatrix4dArray usd_bind_xforms;
187 if (!skel_query.GetJointWorldBindTransforms(&usd_bind_xforms)) {
190 "%s: Couldn't get world bind transforms for skeleton %s",
191 __func__,
192 skel_query.GetSkeleton().GetPrim().GetPath().GetAsString().c_str());
193 return;
194 }
195
196 if (usd_bind_xforms.size() != joint_order.size()) {
199 "%s: Number of bind transforms doesn't match the number of joints for skeleton %s",
200 __func__,
201 skel_query.GetSkeleton().GetPrim().GetPath().GetAsString().c_str());
202 return;
203 }
204
205 const pxr::UsdSkelTopology &skel_topology = skel_query.GetTopology();
206
207 const pxr::VtMatrix4dArray &bind_xforms = usd_bind_xforms.AsConst();
208 pxr::VtMatrix4dArray joint_local_bind_xforms(bind_xforms.size());
209 for (int i = 0; i < bind_xforms.size(); ++i) {
210 const int parent_id = skel_topology.GetParent(i);
211
212 if (parent_id >= 0) {
213 /* This is a non-root joint. Compute the bind transform of the joint
214 * relative to its parent. */
215 joint_local_bind_xforms[i] = bind_xforms[i] * bind_xforms[parent_id].GetInverse();
216 }
217 else {
218 /* This is the root joint. */
219 joint_local_bind_xforms[i] = bind_xforms[i];
220 }
221 }
222
223 /* Set the curve samples. */
224 blender::Array<pxr::GfQuatf> prev_rot(joint_order.size());
225 uint bezt_index = 0;
226 for (const double frame : samples) {
227 pxr::VtMatrix4dArray joint_local_xforms;
228 if (!skel_query.ComputeJointLocalTransforms(&joint_local_xforms, frame)) {
229 CLOG_WARN(&LOG, "Couldn't compute joint local transforms on frame %f", frame);
230 continue;
231 }
232
233 if (joint_local_xforms.size() != joint_order.size()) {
234 CLOG_WARN(
235 &LOG,
236 "Number of joint local transform entries %zu doesn't match the number of joints %zu",
237 joint_local_xforms.size(),
238 joint_order.size());
239 continue;
240 }
241
242 for (int i = 0; i < joint_local_xforms.size(); ++i) {
243 const pxr::GfMatrix4d bone_xform = joint_local_xforms.AsConst()[i] *
244 joint_local_bind_xforms[i].GetInverse();
245
246 pxr::GfVec3f t;
247 pxr::GfQuatf qrot;
248 pxr::GfVec3h s;
249
250 if (!pxr::UsdSkelDecomposeTransform(bone_xform, &t, &qrot, &s)) {
251 CLOG_WARN(&LOG, "Error decomposing matrix on frame %f", frame);
252 continue;
253 }
254
255 if (bezt_index > 0) {
256 /* Quaternion "neighborhood" check to prevent most cases of discontinuous rotations.
257 * Note: An alternate method, comparing to the rotation of the rest position rather than
258 * to the previous rotation, was attempted but yielded much worse results for joints
259 * representing objects that are supposed to spin, like wheels and propellers. */
260 if (pxr::GfDot(prev_rot[i], qrot) < 0.0f) {
261 qrot = -qrot;
262 }
263 }
264 prev_rot[i] = qrot;
265
266 const float re = qrot.GetReal();
267 const pxr::GfVec3f &im = qrot.GetImaginary();
268
269 for (int j = 0; j < 3; ++j) {
270 const int k = curves_per_joint * i + j;
271 if (k >= fcurves.size()) {
272 CLOG_ERROR(&LOG, "Out of bounds translation curve index %d", k);
273 break;
274 }
275 if (FCurve *fcu = fcurves[k]) {
276 set_fcurve_sample(fcu, bezt_index, frame, t[j]);
277 }
278 }
279
280 for (int j = 0; j < 4; ++j) {
281 const int k = curves_per_joint * i + j + 3;
282 if (k >= fcurves.size()) {
283 CLOG_ERROR(&LOG, "Out of bounds rotation curve index %d", k);
284 break;
285 }
286 if (FCurve *fcu = fcurves[k]) {
287 if (j == 0) {
288 set_fcurve_sample(fcu, bezt_index, frame, re);
289 }
290 else {
291 set_fcurve_sample(fcu, bezt_index, frame, im[j - 1]);
292 }
293 }
294 }
295
296 for (int j = 0; j < 3; ++j) {
297 const int k = curves_per_joint * i + j + 7;
298 if (k >= fcurves.size()) {
299 CLOG_ERROR(&LOG, "Out of bounds scale curve index %d", k);
300 break;
301 }
302 if (FCurve *fcu = fcurves[k]) {
303 set_fcurve_sample(fcu, bezt_index, frame, s[j]);
304 }
305 }
306 }
307
308 bezt_index++;
309 }
310
311 /* Recalculate curve handles. */
312 for (FCurve *fcu : fcurves) {
313 if (fcu != nullptr) {
314 resize_fcurve(fcu, bezt_index);
316 }
317 }
318}
319
320/* Set the skeleton path and bind transform on the given mesh. */
321void add_skinned_mesh_bindings(const pxr::UsdSkelSkeleton &skel,
322 const pxr::UsdPrim &mesh_prim,
323 pxr::UsdGeomXformCache &xf_cache)
324{
325 pxr::UsdSkelBindingAPI skel_api = pxr::UsdSkelBindingAPI::Apply(mesh_prim);
326
327 if (!skel_api) {
328 CLOG_WARN(&LOG,
329 "Couldn't apply UsdSkelBindingAPI to skinned mesh prim %s",
330 mesh_prim.GetPath().GetAsString().c_str());
331 return;
332 }
333
334 /* Specify the path to the skeleton. */
335 pxr::SdfPath skel_path = skel.GetPath();
336 skel_api.CreateSkeletonRel().SetTargets(pxr::SdfPathVector({skel_path}));
337
338 /* Set the mesh's bind transform. */
339 if (pxr::UsdAttribute geom_bind_attr = skel_api.CreateGeomBindTransformAttr()) {
340 /* The bind matrix is the mesh transform relative to the skeleton transform. */
341 pxr::GfMatrix4d mesh_xf = xf_cache.GetLocalToWorldTransform(mesh_prim);
342 pxr::GfMatrix4d skel_xf = xf_cache.GetLocalToWorldTransform(skel.GetPrim());
343 pxr::GfMatrix4d bind_xf = mesh_xf * skel_xf.GetInverse();
344 geom_bind_attr.Set(bind_xf);
345 }
346 else {
347 CLOG_WARN(&LOG,
348 "Couldn't create geom bind transform attribute for skinned mesh %s",
349 mesh_prim.GetPath().GetAsString().c_str());
350 }
351}
352
353} // namespace
354
355namespace blender::io::usd {
356
358 Object *mesh_obj,
359 const pxr::UsdPrim &prim,
361 const bool import_anim)
362{
363 if (!(mesh_obj && mesh_obj->data && mesh_obj->type == OB_MESH && prim)) {
364 return;
365 }
366
367 if (prim.IsInstanceProxy()) {
368 /* Attempting to create a UsdSkelBindingAPI for
369 * instance proxies generates USD errors. */
370 return;
371 }
372
373 pxr::UsdSkelBindingAPI skel_api(prim);
374
375 /* Get the blend shape targets, which are the USD paths to the
376 * blend shape primitives. */
377
378 if (!skel_api.GetBlendShapeTargetsRel().HasAuthoredTargets()) {
379 /* No targets. */
380 return;
381 }
382
383 pxr::SdfPathVector targets;
384 if (!skel_api.GetBlendShapeTargetsRel().GetTargets(&targets)) {
387 "%s: Couldn't get blendshape targets for prim %s",
388 __func__,
389 prim.GetPath().GetAsString().c_str());
390 return;
391 }
392
393 if (targets.empty()) {
394 return;
395 }
396
397 if (!skel_api.GetBlendShapesAttr().HasAuthoredValue()) {
398 return;
399 }
400
401 /* Get the blend shape name tokens. */
402 pxr::VtTokenArray usd_blendshapes;
403 if (!skel_api.GetBlendShapesAttr().Get(&usd_blendshapes)) {
404 return;
405 }
406
407 if (usd_blendshapes.empty()) {
408 return;
409 }
410
411 /* Sanity check. */
412 if (targets.size() != usd_blendshapes.size()) {
415 "%s: Number of blendshapes doesn't match number of blendshape targets for prim %s",
416 __func__,
417 prim.GetPath().GetAsString().c_str());
418 return;
419 }
420
421 pxr::UsdStageRefPtr stage = prim.GetStage();
422
423 if (!stage) {
426 "%s: Couldn't get stage for prim %s",
427 __func__,
428 prim.GetPath().GetAsString().c_str());
429 return;
430 }
431
432 Mesh *mesh = static_cast<Mesh *>(mesh_obj->data);
433
434 /* Insert key to source mesh. */
435 Key *key = BKE_key_add(bmain, (ID *)mesh);
436 key->type = KEY_RELATIVE;
437
438 mesh->key = key;
439
440 /* Insert basis key. */
441 KeyBlock *kb = BKE_keyblock_add(key, "Basis");
442 BKE_keyblock_convert_from_mesh(mesh, key, kb);
443
444 /* Keep track of the shape-keys we're adding,
445 * for validation when creating curves later. */
446 blender::Set<pxr::TfToken> shapekey_names;
447 Span<pxr::TfToken> blendshapes = Span(usd_blendshapes.cdata(), usd_blendshapes.size());
448
449 for (int i = 0; i < targets.size(); ++i) {
450 /* Get USD path to blend shape. */
451 const pxr::SdfPath &path = targets[i];
452 pxr::UsdSkelBlendShape blendshape(stage->GetPrimAtPath(path));
453
454 if (!blendshape) {
455 continue;
456 }
457
458 /* Get the blend shape offsets. */
459 if (!blendshape.GetOffsetsAttr().HasAuthoredValue()) {
460 /* Blend shape has no authored offsets. */
461 continue;
462 }
463
464 pxr::VtVec3fArray usd_offsets;
465 if (!blendshape.GetOffsetsAttr().Get(&usd_offsets)) {
468 "%s: Couldn't get offsets for blend shape %s",
469 __func__,
470 path.GetAsString().c_str());
471 continue;
472 }
473
474 if (usd_offsets.empty()) {
477 "%s: No offsets for blend shape %s",
478 __func__,
479 path.GetAsString().c_str());
480 continue;
481 }
482
483 shapekey_names.add(blendshapes[i]);
484
485 /* Add the key block. */
486 kb = BKE_keyblock_add(key, blendshapes[i].GetString().c_str());
487 BKE_keyblock_convert_from_mesh(mesh, key, kb);
488 if (!kb->data) {
489 /* Nothing to do. This can happen if the mesh has no vertices. */
490 continue;
491 }
492
493 /* if authored, point indices are indices into the original mesh
494 * that correspond to the values in the offsets array. */
495 pxr::VtArray<int> point_indices;
496 if (blendshape.GetPointIndicesAttr().HasAuthoredValue()) {
497 blendshape.GetPointIndicesAttr().Get(&point_indices);
498 }
499
500 float *fp = static_cast<float *>(kb->data);
501 Span<pxr::GfVec3f> offsets = Span(usd_offsets.cdata(), usd_offsets.size());
502
503 if (point_indices.empty()) {
504 /* Iterate over all key block elements and add the corresponding
505 * offset to the key block point. */
506 for (int a = 0; a < kb->totelem; ++a, fp += 3) {
507 if (a >= offsets.size()) {
509 reports,
511 "%s: Number of offsets greater than number of mesh vertices for blend shape %s",
512 __func__,
513 path.GetAsString().c_str());
514 break;
515 }
516 add_v3_v3(fp, offsets[a].data());
517 }
518 }
519 else {
520 /* Iterate over the point indices and add the offset to the corresponding
521 * key block point. */
522 int a = 0;
523 for (const int point : point_indices.AsConst()) {
524 if (point < 0 || point > kb->totelem) {
525 CLOG_WARN(&LOG,
526 "Out of bounds point index %d for blendshape %s",
527 point,
528 path.GetAsString().c_str());
529 ++a;
530 continue;
531 }
532 if (a >= offsets.size()) {
534 reports,
536 "%s: Number of offsets greater than number of mesh vertices for blend shape %s",
537 __func__,
538 path.GetAsString().c_str());
539 break;
540 }
541 add_v3_v3(&fp[3 * point], offsets[a].data());
542 ++a;
543 }
544 }
545 }
546
547 if (!import_anim) {
548 /* We're not importing animation, so we are done. */
549 return;
550 }
551
552 /* Get the blend animation source from the skeleton. */
553
554 pxr::UsdSkelSkeleton skel_prim = skel_api.GetInheritedSkeleton();
555
556 if (!skel_prim) {
557 return;
558 }
559
560 skel_api = pxr::UsdSkelBindingAPI(skel_prim.GetPrim());
561
562 pxr::UsdPrim anim_prim = skel_api.GetInheritedAnimationSource();
563
564 if (!anim_prim) {
565 /* Querying the directly bound animation source may be necessary
566 * if the prim does not have an applied skel binding API schema. */
567 skel_api.GetAnimationSource(&anim_prim);
568 }
569
570 if (!anim_prim) {
571 return;
572 }
573
574 pxr::UsdSkelAnimation skel_anim(anim_prim);
575
576 if (!skel_anim) {
577 return;
578 }
579
580 /* Check if a blend shape weight animation was authored. */
581 if (!skel_anim.GetBlendShapesAttr().HasAuthoredValue()) {
582 return;
583 }
584
585 pxr::UsdAttribute weights_attr = skel_anim.GetBlendShapeWeightsAttr();
586
587 if (!(weights_attr && weights_attr.HasAuthoredValue())) {
588 return;
589 }
590
591 /* Get the animation time samples. */
592 std::vector<double> times;
593 if (!weights_attr.GetTimeSamples(&times)) {
594 return;
595 }
596
597 if (times.empty()) {
598 return;
599 }
600
601 /* Get the blend shape name tokens. */
602 if (!skel_anim.GetBlendShapesAttr().Get(&usd_blendshapes)) {
603 return;
604 }
605
606 if (usd_blendshapes.empty()) {
607 return;
608 }
609
610 /* Create the animation and curves. */
611 bAction *act = blender::animrig::id_action_ensure(bmain, &key->id);
613 key->id);
614
616 curves.reserve(usd_blendshapes.size());
617
618 for (auto blendshape_name : usd_blendshapes.AsConst()) {
619 if (!shapekey_names.contains(blendshape_name)) {
620 /* We didn't create a shape-key for this blend-shape, so we don't
621 * create a curve and insert a null placeholder in the curve array. */
622 curves.append(nullptr);
623 continue;
624 }
625
626 /* Create the curve for this shape key. */
627 std::string rna_path = "key_blocks[\"" + blendshape_name.GetString() + "\"].value";
628 FCurve *fcu = create_fcurve(channelbag, {rna_path, 0}, times.size());
629 curves.append(fcu);
630 }
631
632 /* Add the weight time samples to the curves. */
633 uint bezt_index = 0;
634 for (double frame : times) {
635 pxr::VtFloatArray usd_weights;
636 if (!weights_attr.Get(&usd_weights, frame)) {
637 CLOG_WARN(&LOG, "Couldn't get blendshape weights for time %f", frame);
638 continue;
639 }
640
641 if (usd_weights.size() != curves.size()) {
642 CLOG_WARN(
643 &LOG,
644 "Number of weight samples doesn't match number of shapekey curve entries for frame %f",
645 frame);
646 continue;
647 }
648
649 Span<float> weights = Span(usd_weights.cdata(), usd_weights.size());
650 for (int wi = 0; wi < weights.size(); ++wi) {
651 if (curves[wi] != nullptr) {
652 set_fcurve_sample(curves[wi], bezt_index, frame, weights[wi]);
653 }
654 }
655
656 bezt_index++;
657 }
658
659 /* Recalculate curve handles. */
660 auto recalc_handles = [bezt_index](FCurve *fcu) {
661 resize_fcurve(fcu, bezt_index);
663 };
664 std::for_each(curves.begin(), curves.end(), recalc_handles);
665}
666
667static void set_rest_pose(Main *bmain,
668 Object *arm_obj,
669 bArmature *arm,
670 const pxr::VtArray<pxr::GfMatrix4d> &bind_xforms,
671 const pxr::VtTokenArray &joint_order,
672 const blender::Map<pxr::TfToken, std::string> &joint_to_bone_map,
673 const pxr::UsdSkelTopology &skel_topology,
674 const pxr::UsdSkelSkeletonQuery &skel_query)
675{
676 if (!skel_query.HasRestPose()) {
677 return;
678 }
679
680 pxr::VtArray<pxr::GfMatrix4d> rest_xforms;
681 if (skel_query.ComputeJointLocalTransforms(&rest_xforms, pxr::UsdTimeCode::Default(), true)) {
682 BKE_pose_ensure(bmain, arm_obj, arm, false);
683
684 int64_t i = 0;
685 for (const pxr::TfToken &joint : joint_order) {
686 const std::string *name = joint_to_bone_map.lookup_ptr(joint);
687 if (name == nullptr) {
688 /* This joint doesn't correspond to any bone we created. Skip. */
689 continue;
690 }
691
692 bPoseChannel *pchan = BKE_pose_channel_find_name(arm_obj->pose, name->c_str());
693
694 pxr::GfMatrix4d xf = rest_xforms.AsConst()[i];
695 pxr::GfMatrix4d bind_xf = bind_xforms[i];
696
697 const int parent_id = skel_topology.GetParent(i);
698 if (parent_id >= 0) {
699 bind_xf = bind_xf * bind_xforms[parent_id].GetInverse();
700 }
701
702 xf = xf * bind_xf.GetInverse();
703
704 pxr::GfMatrix4f mat(xf);
705 BKE_pchan_apply_mat4(pchan, (float(*)[4])mat.data(), false);
706
707 i++;
708 }
709 }
710}
711
713 Object *arm_obj,
714 const pxr::UsdSkelSkeleton &skel,
716 const bool import_anim)
717{
718 if (!(arm_obj && arm_obj->data && arm_obj->type == OB_ARMATURE)) {
719 return;
720 }
721
722 pxr::UsdSkelCache skel_cache;
723 pxr::UsdSkelSkeletonQuery skel_query = skel_cache.GetSkelQuery(skel);
724
725 if (!skel_query.IsValid()) {
728 "%s: Couldn't query skeleton %s",
729 __func__,
730 skel.GetPath().GetAsString().c_str());
731 return;
732 }
733
734 const pxr::UsdSkelTopology &skel_topology = skel_query.GetTopology();
735 const pxr::VtTokenArray joint_order = skel_query.GetJointOrder();
736
737 if (joint_order.size() != skel_topology.size()) {
740 "%s: Topology and joint order size mismatch for skeleton %s",
741 __func__,
742 skel.GetPath().GetAsString().c_str());
743 return;
744 }
745
746 /* Each joint path should be valid and unique. */
747 blender::Set<pxr::TfToken> unique_joint_paths;
748 unique_joint_paths.reserve(joint_order.size());
749 const bool all_valid_paths = std::all_of(
750 joint_order.cbegin(), joint_order.cend(), [&unique_joint_paths](const pxr::TfToken &val) {
751 const bool is_valid = pxr::SdfPath::IsValidPathString(val);
752 return is_valid && unique_joint_paths.add(val);
753 });
754 if (!all_valid_paths) {
757 "%s: USD joint order array contains invalid or duplicated paths for skeleton %s",
758 __func__,
759 skel.GetPath().GetAsString().c_str());
760 return;
761 }
762
763 bArmature *arm = static_cast<bArmature *>(arm_obj->data);
764
765 /* Set the armature to edit mode when creating the bones. */
767
768 /* The bones we create, stored in the skeleton's joint order. */
770
771 /* Keep track of the bones we create for each joint.
772 * We'll need this when creating animation curves
773 * later. */
775
776 /* Create the bones. */
777 for (const pxr::TfToken &joint : joint_order) {
778 pxr::SdfPath bone_path(joint);
779 const std::string &bone_name = bone_path.GetName();
780 EditBone *bone = ED_armature_ebone_add(arm, bone_name.c_str());
781 if (!bone) {
784 "%s: Couldn't add bone for joint %s",
785 __func__,
786 joint.GetString().c_str());
787 edit_bones.append(nullptr);
788 continue;
789 }
790 joint_to_bone_map.add(joint, bone->name);
791 edit_bones.append(bone);
792 }
793
794 /* Sanity check: we should have created a bone for each joint. */
795 const size_t num_joints = skel_topology.GetNumJoints();
796 if (edit_bones.size() != num_joints) {
799 "%s: Mismatch in bone and joint counts for skeleton %s",
800 __func__,
801 skel.GetPath().GetAsString().c_str());
802 return;
803 }
804
805 /* Get the world space joint transforms at bind time. */
806 pxr::VtMatrix4dArray bind_xforms;
807 if (!skel_query.GetJointWorldBindTransforms(&bind_xforms)) {
810 "%s: Couldn't get world bind transforms for skeleton %s",
811 __func__,
812 skel.GetPath().GetAsString().c_str());
813 return;
814 }
815
816 if (bind_xforms.size() != num_joints) {
819 "%s: Mismatch in bind xforms and joint counts for skeleton %s",
820 __func__,
821 skel.GetPath().GetAsString().c_str());
822 return;
823 }
824
825 /* Check if any bone matrices have negative determinants,
826 * indicating negative scales, possibly due to mirroring
827 * operations. Such matrices can't be properly converted
828 * to Blender's axis/roll bone representation (see
829 * https://projects.blender.org/blender/blender/issues/82930).
830 * If we detect such matrices, we will flag an error and won't
831 * try to import the animation, since the rotations would
832 * be incorrect in such cases. Unfortunately, the Pixar
833 * `UsdSkel` examples of the "HumanFemale" suffer from
834 * this issue. */
835 bool negative_determinant = false;
836
837 /* Set bone rest transforms. */
838 for (size_t i = 0; i < num_joints; ++i) {
839 EditBone *ebone = edit_bones[i];
840
841 if (!ebone) {
842 continue;
843 }
844
845 pxr::GfMatrix4f mat(bind_xforms.AsConst()[i]);
846
847 float mat4[4][4];
848 mat.Get(mat4);
849
850 pxr::GfVec3f head(0.0f, 0.0f, 0.0f);
851 pxr::GfVec3f tail(0.0f, 1.0f, 0.0f);
852
853 copy_v3_v3(ebone->head, head.data());
854 copy_v3_v3(ebone->tail, tail.data());
855
856 ED_armature_ebone_from_mat4(ebone, mat4);
857
858 if (mat.GetDeterminant() < 0.0) {
859 negative_determinant = true;
860 }
861 }
862
863 bool valid_skeleton = true;
864 if (negative_determinant) {
865 valid_skeleton = false;
867 reports,
869 "USD Skeleton Import: bone matrices with negative determinants detected in prim %s. "
870 "Such matrices may indicate negative scales, possibly due to mirroring operations, "
871 "and can't currently be converted to Blender's bone representation. "
872 "The skeletal animation won't be imported",
873 skel.GetPath().GetAsString().c_str());
874 }
875
876 /* Set bone parenting. In addition, scale bones to account
877 * for separation between parents and children, so that the
878 * bone size is in proportion with the overall skeleton hierarchy.
879 * USD skeletons are composed of joints which we imperfectly
880 * represent as bones. */
881
882 /* This will record the child bone indices per parent bone,
883 * to simplify accessing children when computing lengths. */
884 blender::Vector<blender::Vector<int>> child_bones(num_joints);
885
886 for (size_t i = 0; i < num_joints; ++i) {
887 const int parent_idx = skel_topology.GetParent(i);
888 if (parent_idx < 0) {
889 continue;
890 }
891 if (parent_idx >= edit_bones.size()) {
892 CLOG_WARN(&LOG,
893 "Out of bounds parent index for bone %s on skeleton %s",
894 pxr::SdfPath(joint_order[i]).GetAsString().c_str(),
895 skel.GetPath().GetAsString().c_str());
896 continue;
897 }
898
899 child_bones[parent_idx].append(i);
900 if (edit_bones[i] && edit_bones[parent_idx]) {
901 edit_bones[i]->parent = edit_bones[parent_idx];
902 }
903 }
904
905 /* Use our custom bone length data if possible, otherwise fall back to estimated lengths. */
906 const pxr::UsdGeomPrimvarsAPI pv_api = pxr::UsdGeomPrimvarsAPI(skel.GetPrim());
907 const pxr::UsdGeomPrimvar pv_lengths = pv_api.GetPrimvar(BlenderBoneLengths);
908 if (pv_lengths.HasValue()) {
909 pxr::VtArray<float> blender_bone_lengths;
910 pv_lengths.ComputeFlattened(&blender_bone_lengths);
911
912 Span<float> bone_lengths = Span(blender_bone_lengths.cdata(), blender_bone_lengths.size());
913 for (size_t i = 0; i < num_joints; ++i) {
914 EditBone *bone = edit_bones[i];
915 pxr::GfVec3f head(bone->head);
916 pxr::GfVec3f tail(bone->tail);
917
918 tail = head + (tail - head).GetNormalized() * bone_lengths[i];
919 copy_v3_v3(bone->tail, tail.data());
920 }
921 }
922 else {
923 float avg_len_scale = 0;
924 for (size_t i = 0; i < num_joints; ++i) {
925
926 /* If the bone has any children, scale its length
927 * by the distance between this bone's head
928 * and the average head location of its children. */
929
930 if (child_bones[i].is_empty()) {
931 continue;
932 }
933
934 EditBone *parent = edit_bones[i];
935 if (!parent) {
936 continue;
937 }
938
939 pxr::GfVec3f avg_child_head(0);
940 for (int j : child_bones[i]) {
941 EditBone *child = edit_bones[j];
942 if (!child) {
943 continue;
944 }
945 pxr::GfVec3f child_head(child->head);
946 avg_child_head += child_head;
947 }
948
949 avg_child_head /= child_bones[i].size();
950
951 pxr::GfVec3f parent_head(parent->head);
952 pxr::GfVec3f parent_tail(parent->tail);
953
954 const float new_len = (avg_child_head - parent_head).GetLength();
955
956 /* Check for epsilon relative to the parent head before scaling. */
957 if (new_len > .00001 * max_mag_component(parent_head)) {
958 parent_tail = parent_head + (parent_tail - parent_head).GetNormalized() * new_len;
959 copy_v3_v3(parent->tail, parent_tail.data());
960 avg_len_scale += new_len;
961 }
962 }
963
964 /* Scale terminal bones by the average length scale. */
965 avg_len_scale /= num_joints;
966
967 for (size_t i = 0; i < num_joints; ++i) {
968 if (!child_bones[i].is_empty()) {
969 /* Not a terminal bone. */
970 continue;
971 }
972 EditBone *bone = edit_bones[i];
973 if (!bone) {
974 continue;
975 }
976 pxr::GfVec3f head(bone->head);
977
978 /* Check for epsilon relative to the head before scaling. */
979 if (avg_len_scale > .00001 * max_mag_component(head)) {
980 pxr::GfVec3f tail(bone->tail);
981 tail = head + (tail - head).GetNormalized() * avg_len_scale;
982 copy_v3_v3(bone->tail, tail.data());
983 }
984 }
985 }
986
987 /* Get out of edit mode. */
988 ED_armature_from_edit(bmain, arm);
990
992 bmain, arm_obj, arm, bind_xforms, joint_order, joint_to_bone_map, skel_topology, skel_query);
993
994 if (import_anim && valid_skeleton) {
995 import_skeleton_curves(bmain, arm_obj, skel_query, joint_to_bone_map, reports);
996 }
997}
998
999void import_mesh_skel_bindings(Object *mesh_obj, const pxr::UsdPrim &prim, ReportList *reports)
1000{
1001 if (!(mesh_obj && mesh_obj->type == OB_MESH && prim)) {
1002 return;
1003 }
1004
1005 if (prim.IsInstanceProxy()) {
1006 /* Attempting to create a UsdSkelBindingAPI for
1007 * instance proxies generates USD errors. */
1008 return;
1009 }
1010
1011 pxr::UsdSkelBindingAPI skel_api(prim);
1012
1013 pxr::UsdSkelSkeleton skel = skel_api.GetInheritedSkeleton();
1014
1015 if (!skel) {
1016 return;
1017 }
1018
1019 /* Get the joint identifiers from the skeleton. We will
1020 * need these to construct deform groups. */
1021 pxr::VtArray<pxr::TfToken> joints;
1022
1023 if (skel_api.GetJointsAttr().HasAuthoredValue()) {
1024 skel_api.GetJointsAttr().Get(&joints);
1025 }
1026 else if (skel.GetJointsAttr().HasAuthoredValue()) {
1027 skel.GetJointsAttr().Get(&joints);
1028 }
1029
1030 if (joints.empty()) {
1031 return;
1032 }
1033
1034 /* Get the joint indices, which specify which joints influence a given point. */
1035 pxr::UsdGeomPrimvar joint_indices_primvar = skel_api.GetJointIndicesPrimvar();
1036 if (!(joint_indices_primvar && joint_indices_primvar.HasAuthoredValue())) {
1037 return;
1038 }
1039
1040 /* Get the weights, which specify the weight of a joint on a given point. */
1041 pxr::UsdGeomPrimvar joint_weights_primvar = skel_api.GetJointWeightsPrimvar();
1042 if (!(joint_weights_primvar && joint_weights_primvar.HasAuthoredValue())) {
1043 return;
1044 }
1045
1046 /* Element size specifies the number of joints that might influence a given point.
1047 * This is the stride we take when accessing the indices and weights for a given point. */
1048 int joint_indices_elem_size = joint_indices_primvar.GetElementSize();
1049 int joint_weights_elem_size = joint_weights_primvar.GetElementSize();
1050
1051 /* We expect the element counts to match. */
1052 if (joint_indices_elem_size != joint_weights_elem_size) {
1055 "%s: Joint weights and joint indices element size mismatch for prim %s",
1056 __func__,
1057 prim.GetPath().GetAsString().c_str());
1058 return;
1059 }
1060
1061 /* Get the joint indices and weights. */
1062 pxr::VtIntArray joint_indices;
1063 joint_indices_primvar.ComputeFlattened(&joint_indices);
1064
1065 pxr::VtFloatArray joint_weights;
1066 joint_weights_primvar.ComputeFlattened(&joint_weights);
1067
1068 if (joint_indices.empty() || joint_weights.empty()) {
1069 return;
1070 }
1071
1072 if (joint_indices.size() != joint_weights.size()) {
1075 "%s: Joint weights and joint indices size mismatch for prim %s",
1076 __func__,
1077 prim.GetPath().GetAsString().c_str());
1078 return;
1079 }
1080
1081 Mesh *mesh = static_cast<Mesh *>(mesh_obj->data);
1082
1083 const pxr::TfToken interp = joint_weights_primvar.GetInterpolation();
1084
1085 /* Sanity check: we expect only vertex or constant interpolation. */
1086 if (!ELEM(interp, pxr::UsdGeomTokens->vertex, pxr::UsdGeomTokens->constant)) {
1089 "%s: Unexpected joint weights interpolation type %s for prim %s",
1090 __func__,
1091 interp.GetString().c_str(),
1092 prim.GetPath().GetAsString().c_str());
1093 return;
1094 }
1095
1096 /* Sanity check: make sure we have the expected number of values for the interpolation type. */
1097 if (interp == pxr::UsdGeomTokens->vertex &&
1098 joint_weights.size() != mesh->verts_num * joint_weights_elem_size)
1099 {
1102 "%s: Joint weights of unexpected size for vertex interpolation for prim %s",
1103 __func__,
1104 prim.GetPath().GetAsString().c_str());
1105 return;
1106 }
1107
1108 if (interp == pxr::UsdGeomTokens->constant && joint_weights.size() != joint_weights_elem_size) {
1111 "%s: Joint weights of unexpected size for constant interpolation for prim %s",
1112 __func__,
1113 prim.GetPath().GetAsString().c_str());
1114 return;
1115 }
1116
1117 /* Determine which joint indices are used for skinning this prim. */
1118 blender::Vector<int> used_indices;
1119 for (int index : joint_indices.AsConst()) {
1120 if (std::find(used_indices.begin(), used_indices.end(), index) == used_indices.end()) {
1121 /* We haven't accounted for this index yet. */
1122 if (index < 0 || index >= joints.size()) {
1123 CLOG_ERROR(&LOG, "Out of bound joint index %d for mesh %s", index, mesh_obj->id.name + 2);
1124 return;
1125 }
1126 used_indices.append(index);
1127 }
1128 }
1129
1130 if (used_indices.is_empty()) {
1131 return;
1132 }
1133
1134 if (BKE_object_defgroup_data_create(static_cast<ID *>(mesh_obj->data)) == nullptr) {
1137 "%s: Error creating deform group data for mesh %s",
1138 __func__,
1139 mesh_obj->id.name + 2);
1140 return;
1141 }
1142
1143 /* Add the armature modifier, if one doesn't exist. */
1146 BLI_addtail(&mesh_obj->modifiers, md);
1147 BKE_modifiers_persistent_uid_init(*mesh_obj, *md);
1148 }
1149
1150 /* Create a deform group per joint. */
1151 blender::Vector<bDeformGroup *> joint_def_grps(joints.size(), nullptr);
1152
1153 for (int idx : used_indices) {
1154 std::string joint_name = pxr::SdfPath(joints.AsConst()[idx]).GetName();
1155 if (!BKE_object_defgroup_find_name(mesh_obj, joint_name.c_str())) {
1156 bDeformGroup *def_grp = BKE_object_defgroup_add_name(mesh_obj, joint_name.c_str());
1157 joint_def_grps[idx] = def_grp;
1158 }
1159 }
1160
1161 /* Set the deform group verts and weights. */
1162 for (int i = 0; i < mesh->verts_num; ++i) {
1163 /* Offset into the weights array, which is
1164 * always 0 for constant interpolation. */
1165 int offset = 0;
1166 if (interp == pxr::UsdGeomTokens->vertex) {
1167 offset = i * joint_weights_elem_size;
1168 }
1169 for (int j = 0; j < joint_weights_elem_size; ++j) {
1170 const int k = offset + j;
1171 const float w = joint_weights.AsConst()[k];
1172 if (w < .00001) {
1173 /* No deform group if zero weight. */
1174 continue;
1175 }
1176 const int joint_idx = joint_indices.AsConst()[k];
1177 if (bDeformGroup *def_grp = joint_def_grps[joint_idx]) {
1179 }
1180 }
1181 }
1182}
1183
1184void skel_export_chaser(pxr::UsdStageRefPtr stage,
1185 const ObjExportMap &armature_export_map,
1186 const ObjExportMap &skinned_mesh_export_map,
1187 const ObjExportMap &shape_key_mesh_export_map,
1188 const Depsgraph *depsgraph)
1189{
1190 /* We may need to compute the world transforms of certain primitives when
1191 * setting skinning data. Using a shared transform cache can make computing
1192 * the transforms more efficient. */
1193 pxr::UsdGeomXformCache xf_cache(1.0);
1195 stage, armature_export_map, skinned_mesh_export_map, xf_cache, depsgraph);
1196 shape_key_export_chaser(stage, shape_key_mesh_export_map);
1197}
1198
1199void skinned_mesh_export_chaser(pxr::UsdStageRefPtr stage,
1200 const ObjExportMap &armature_export_map,
1201 const ObjExportMap &skinned_mesh_export_map,
1202 pxr::UsdGeomXformCache &xf_cache,
1203 const Depsgraph *depsgraph)
1204{
1205 /* Finish creating skinned mesh bindings. */
1206 for (const auto &item : skinned_mesh_export_map.items()) {
1207 const Object *mesh_obj = item.key;
1208 const pxr::SdfPath &mesh_path = item.value;
1209
1210 /* Get the mesh prim from the stage. */
1211 pxr::UsdPrim mesh_prim = stage->GetPrimAtPath(mesh_path);
1212 if (!mesh_prim) {
1213 CLOG_WARN(&LOG,
1214 "Invalid export map prim path %s for mesh object %s",
1215 mesh_path.GetAsString().c_str(),
1216 mesh_obj->id.name + 2);
1217 continue;
1218 }
1219
1220 /* Get the armature bound to the mesh's armature modifier. */
1221 const Object *arm_obj = get_armature_modifier_obj(*mesh_obj, depsgraph);
1222 if (!arm_obj) {
1223 CLOG_WARN(&LOG, "Invalid armature modifier for skinned mesh %s", mesh_obj->id.name + 2);
1224 continue;
1225 }
1226 /* Look up the USD skeleton corresponding to the armature object. */
1227 const pxr::SdfPath *path = armature_export_map.lookup_ptr(arm_obj);
1228 if (!path) {
1229 CLOG_WARN(&LOG, "No export map entry for armature object %s", mesh_obj->id.name + 2);
1230 continue;
1231 }
1232 /* Get the skeleton prim. */
1233 pxr::UsdPrim skel_prim = stage->GetPrimAtPath(*path);
1234 pxr::UsdSkelSkeleton skel(skel_prim);
1235 if (!skel) {
1236 CLOG_WARN(&LOG, "Invalid USD skeleton for armature object %s", arm_obj->id.name + 2);
1237 continue;
1238 }
1239
1240 add_skinned_mesh_bindings(skel, mesh_prim, xf_cache);
1241 }
1242}
1243
1244void shape_key_export_chaser(pxr::UsdStageRefPtr stage,
1245 const ObjExportMap &shape_key_mesh_export_map)
1246{
1248
1249 /* We will keep track of the mesh primitives to clean up the temporary
1250 * weights attribute at the end. */
1251 Vector<pxr::UsdPrim> mesh_prims;
1252
1253 /* Finish creating blend shape bindings. */
1254 for (const auto &item : shape_key_mesh_export_map.items()) {
1255 const Object *mesh_obj = item.key;
1256 const pxr::SdfPath &mesh_path = item.value;
1257
1258 /* Get the mesh prim from the stage. */
1259 pxr::UsdPrim mesh_prim = stage->GetPrimAtPath(mesh_path);
1260 if (!mesh_prim) {
1261 CLOG_WARN(&LOG,
1262 "Invalid export map prim path %s for mesh object %s",
1263 mesh_path.GetAsString().c_str(),
1264 mesh_obj->id.name + 2);
1265 continue;
1266 }
1267
1268 /* Keep track of all the mesh primitives with blend shapes, for cleanup below. */
1269 mesh_prims.append(mesh_prim);
1270
1271 pxr::UsdSkelBindingAPI skel_api = pxr::UsdSkelBindingAPI::Apply(mesh_prim);
1272
1273 if (!skel_api) {
1274 CLOG_WARN(&LOG,
1275 "Couldn't apply UsdSkelBindingAPI to prim %s",
1276 mesh_prim.GetPath().GetAsString().c_str());
1277 return;
1278 }
1279
1280 pxr::UsdSkelSkeleton skel;
1281 if (skel_api.GetSkeleton(&skel)) {
1282 /* We have a bound skeleton, so we add it to the map. */
1283 pxr::SdfPathSet *mesh_paths = skel_to_mesh.lookup_ptr(skel.GetPath());
1284 if (!mesh_paths) {
1285 skel_to_mesh.add_new(skel.GetPath(), pxr::SdfPathSet());
1286 mesh_paths = skel_to_mesh.lookup_ptr(skel.GetPath());
1287 }
1288 if (mesh_paths) {
1289 mesh_paths->insert(mesh_prim.GetPath());
1290 }
1291 continue;
1292 }
1293
1294 /* The mesh is not bound to a skeleton, so we must create one for it. */
1295 ensure_blend_shape_skeleton(stage, mesh_prim);
1296 }
1297
1298 if (skel_to_mesh.is_empty()) {
1299 return;
1300 }
1301
1302 for (const auto &item : skel_to_mesh.items()) {
1303 remap_blend_shape_anim(stage, item.key, item.value);
1304 }
1305
1306 /* Finally, delete the temp blendshape weights attributes. */
1307 for (const pxr::UsdPrim &prim : mesh_prims) {
1308 pxr::UsdGeomPrimvarsAPI(prim).RemovePrimvar(TempBlendShapeWeightsPrimvarName);
1309 }
1310}
1311
1312void export_deform_verts(const Mesh *mesh,
1313 const pxr::UsdSkelBindingAPI &skel_api,
1314 const Span<StringRef> bone_names)
1315{
1316 BLI_assert(mesh);
1317 BLI_assert(skel_api);
1318
1319 /* Map a deform vertex group index to the
1320 * index of the corresponding joint. I.e.,
1321 * joint_index[n] is the joint index of the
1322 * n-th vertex group. */
1323 Vector<int> joint_index;
1324
1325 /* Build the index mapping. */
1326 LISTBASE_FOREACH (const bDeformGroup *, def, &mesh->vertex_group_names) {
1327 int bone_idx = -1;
1328 /* For now, n-squared search is acceptable. */
1329 for (int i = 0; i < bone_names.size(); ++i) {
1330 if (bone_names[i] == def->name) {
1331 bone_idx = i;
1332 break;
1333 }
1334 }
1335
1336 joint_index.append(bone_idx);
1337 }
1338
1339 if (joint_index.is_empty()) {
1340 return;
1341 }
1342
1343 const Span<MDeformVert> dverts = mesh->deform_verts();
1344
1345 int max_totweight = 1;
1346 for (const int i : dverts.index_range()) {
1347 const MDeformVert &vert = dverts[i];
1348 max_totweight = std::max(vert.totweight, max_totweight);
1349 }
1350
1351 /* elem_size will specify the number of
1352 * joints that can influence a given point. */
1353 const int element_size = max_totweight;
1354 int num_points = mesh->verts_num;
1355
1356 pxr::VtArray<int> joint_indices(num_points * element_size, 0);
1357 pxr::VtArray<float> joint_weights(num_points * element_size, 0.0f);
1358
1359 /* Current offset into the indices and weights arrays. */
1360 int offset = 0;
1361
1362 for (const int i : dverts.index_range()) {
1363 const MDeformVert &vert = dverts[i];
1364
1365 for (int j = 0; j < element_size; ++j, ++offset) {
1366
1367 if (offset >= joint_indices.size()) {
1369 return;
1370 }
1371
1372 if (j >= vert.totweight) {
1373 continue;
1374 }
1375
1376 int def_nr = int(vert.dw[j].def_nr);
1377
1378 if (def_nr >= joint_index.size()) {
1380 continue;
1381 }
1382
1383 if (joint_index[def_nr] == -1) {
1384 continue;
1385 }
1386
1387 joint_indices[offset] = joint_index[def_nr];
1388 joint_weights[offset] = vert.dw[j].weight;
1389 }
1390 }
1391
1392 pxr::UsdSkelNormalizeWeights(joint_weights, element_size);
1393
1394 skel_api.CreateJointIndicesPrimvar(false, element_size).GetAttr().Set(joint_indices);
1395 skel_api.CreateJointWeightsPrimvar(false, element_size).GetAttr().Set(joint_weights);
1396}
1397
1398} // namespace blender::io::usd
Functions and classes to work with Actions.
Functions to work with AnimData.
bPoseChannel * BKE_pose_channel_find_name(const bPose *pose, const char *name)
void BKE_pchan_apply_mat4(bPoseChannel *pchan, const float mat[4][4], bool use_compat)
Definition armature.cc:2389
void BKE_pose_ensure(Main *bmain, Object *ob, bArmature *arm, bool do_id_user)
Definition armature.cc:2932
support for deformation groups and hooks.
bDeformGroup * BKE_object_defgroup_find_name(const Object *ob, blender::StringRef name)
Definition deform.cc:515
void BKE_fcurve_handles_recalc(FCurve *fcu)
void BKE_fcurve_bezt_resize(FCurve *fcu, int new_totvert)
Key * BKE_key_add(Main *bmain, ID *id)
Definition key.cc:258
KeyBlock * BKE_keyblock_add(Key *key, const char *name)
Definition key.cc:1835
void BKE_keyblock_convert_from_mesh(const Mesh *mesh, const Key *key, KeyBlock *kb)
Definition key.cc:2203
IDNewNameResult BKE_id_rename(Main &bmain, ID &id, blender::StringRefNull name, const IDNewNameMode mode=IDNewNameMode::RenameExistingNever)
Definition lib_id.cc:2372
ModifierData * BKE_modifiers_findby_type(const Object *ob, ModifierType type)
void BKE_modifiers_persistent_uid_init(const Object &object, ModifierData &md)
ModifierData * BKE_modifier_new(int type)
Functions for dealing with objects and deform verts, used by painting and tools.
struct bDeformGroup * BKE_object_defgroup_add_name(struct Object *ob, const char *name)
struct MDeformVert * BKE_object_defgroup_data_create(struct ID *id)
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
#define LISTBASE_FOREACH(type, var, list)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void add_v3_v3(float r[3], const float a[3])
unsigned int uint
#define ELEM(...)
#define CLOG_ERROR(clg_ref,...)
Definition CLG_log.h:182
#define CLOG_WARN(clg_ref,...)
Definition CLG_log.h:181
@ KEY_RELATIVE
@ eModifierType_Armature
@ OB_ARMATURE
@ OB_MESH
#define WEIGHT_REPLACE
ReportList * reports
Definition WM_types.hh:1025
EditBone * ED_armature_ebone_add(bArmature *arm, const char *name)
void ED_armature_edit_free(bArmature *arm)
void ED_armature_from_edit(Main *bmain, bArmature *arm)
void ED_armature_ebone_from_mat4(EditBone *ebone, const float mat[4][4])
void ED_armature_to_edit(bArmature *arm)
BMesh const char void * data
BPy_StructRNA * depsgraph
long long int int64_t
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
StringRefNull copy_string(StringRef str)
const Value * lookup_ptr(const Key &key) const
Definition BLI_map.hh:508
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:295
void add_new(const Key &key, const Value &value)
Definition BLI_map.hh:265
bool is_empty() const
Definition BLI_map.hh:986
ItemIterator items() const &
Definition BLI_map.hh:902
void reserve(const int64_t n)
Definition BLI_set.hh:637
bool contains(const Key &key) const
Definition BLI_set.hh:310
bool add(const Key &key)
Definition BLI_set.hh:248
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
int64_t size() const
void append(const T &value)
bool is_empty() const
void reserve(const int64_t min_capacity)
Span< T > as_span() const
void append_n_times(const T &value, const int64_t n)
Vector< FCurve * > fcurve_create_many(Main *bmain, Span< FCurveDescriptor > fcurve_descriptors)
#define LOG(severity)
Definition log.h:32
ccl_device_inline float interp(const float a, const float b, const float t)
Definition math_base.h:502
bAction * id_action_ensure(Main *bmain, ID *id)
Definition animdata.cc:195
Channelbag & action_channelbag_ensure(bAction &dna_action, ID &animated_id)
void vgroup_vert_add(Object *ob, bDeformGroup *dg, int vertnum, float weight, int assignmode)
Map< const Object *, pxr::SdfPath > ObjExportMap
void shape_key_export_chaser(pxr::UsdStageRefPtr stage, const ObjExportMap &shape_key_mesh_export_map)
void import_mesh_skel_bindings(Object *mesh_obj, const pxr::UsdPrim &prim, ReportList *reports)
void set_fcurve_sample(FCurve *fcu, int64_t sample_index, const float frame, const float value)
FCurve * create_fcurve(blender::animrig::Channelbag &channelbag, const blender::animrig::FCurveDescriptor &fcurve_descriptor, const int sample_count)
void skel_export_chaser(pxr::UsdStageRefPtr stage, const ObjExportMap &armature_export_map, const ObjExportMap &skinned_mesh_export_map, const ObjExportMap &shape_key_mesh_export_map, const Depsgraph *depsgraph)
void export_deform_verts(const Mesh *mesh, const pxr::UsdSkelBindingAPI &skel_api, const Span< StringRef > bone_names)
void remap_blend_shape_anim(pxr::UsdStageRefPtr stage, const pxr::SdfPath &skel_path, const pxr::SdfPathSet &mesh_paths)
const pxr::TfToken BlenderBoneLengths("blender:bone_lengths", pxr::TfToken::Immortal)
void import_blendshapes(Main *bmain, Object *mesh_obj, const pxr::UsdPrim &prim, ReportList *reports, const bool import_anim)
void skinned_mesh_export_chaser(pxr::UsdStageRefPtr stage, const ObjExportMap &armature_export_map, const ObjExportMap &skinned_mesh_export_map, pxr::UsdGeomXformCache &xf_cache, const Depsgraph *depsgraph)
void ensure_blend_shape_skeleton(pxr::UsdStageRefPtr stage, pxr::UsdPrim &mesh_prim)
pxr::TfToken TempBlendShapeWeightsPrimvarName
void import_skeleton(Main *bmain, Object *arm_obj, const pxr::UsdSkelSkeleton &skel, ReportList *reports, const bool import_anim)
const Object * get_armature_modifier_obj(const Object &obj, const Depsgraph *depsgraph)
static void set_rest_pose(Main *bmain, Object *arm_obj, bArmature *arm, const pxr::VtArray< pxr::GfMatrix4d > &bind_xforms, const pxr::VtTokenArray &joint_order, const blender::Map< pxr::TfToken, std::string > &joint_to_bone_map, const pxr::UsdSkelTopology &skel_topology, const pxr::UsdSkelSkeletonQuery &skel_query)
char name[64]
float tail[3]
float head[3]
unsigned int totvert
Definition DNA_ID.h:404
char name[66]
Definition DNA_ID.h:415
void * data
char type
struct MDeformWeight * dw
unsigned int def_nr
ListBase vertex_group_names
struct Key * key
int verts_num
struct bPose * pose
ListBase modifiers
i
Definition text_draw.cc:230