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
615 blender::Set<pxr::TfToken> processed_shapes;
617 curves.reserve(usd_blendshapes.size());
618 processed_shapes.reserve(usd_blendshapes.size());
619
620 for (auto blendshape_name : usd_blendshapes.AsConst()) {
621 if (!shapekey_names.contains(blendshape_name)) {
622 /* We didn't create a shape-key for this blend-shape, so we don't
623 * create a curve and insert a null placeholder in the curve array. */
624 curves.append(nullptr);
625 continue;
626 }
627
628 if (!processed_shapes.add(blendshape_name)) {
629 CLOG_WARN(&LOG,
630 "Duplicate blendshape '%s' encountered for %s",
631 blendshape_name.GetText(),
632 skel_anim.GetPath().GetAsString().c_str());
633 curves.append(nullptr);
634 continue;
635 }
636
637 /* Create the curve for this shape key. */
638 const std::string rna_path = "key_blocks[\"" + blendshape_name.GetString() + "\"].value";
639 FCurve *fcu = create_fcurve(channelbag, {rna_path, 0}, times.size());
640 curves.append(fcu);
641 }
642
643 /* Add the weight time samples to the curves. */
644 uint bezt_index = 0;
645 for (double frame : times) {
646 pxr::VtFloatArray usd_weights;
647 if (!weights_attr.Get(&usd_weights, frame)) {
648 CLOG_WARN(&LOG, "Couldn't get blendshape weights for time %f", frame);
649 continue;
650 }
651
652 if (usd_weights.size() != curves.size()) {
653 CLOG_WARN(
654 &LOG,
655 "Number of weight samples doesn't match number of shapekey curve entries for frame %f",
656 frame);
657 continue;
658 }
659
660 Span<float> weights = Span(usd_weights.cdata(), usd_weights.size());
661 for (int wi = 0; wi < weights.size(); ++wi) {
662 if (curves[wi] != nullptr) {
663 set_fcurve_sample(curves[wi], bezt_index, frame, weights[wi]);
664 }
665 }
666
667 bezt_index++;
668 }
669
670 /* Recalculate curve handles. */
671 auto recalc_handles = [bezt_index](FCurve *fcu) {
672 resize_fcurve(fcu, bezt_index);
674 };
675 std::for_each(curves.begin(), curves.end(), recalc_handles);
676}
677
678static void set_rest_pose(Main *bmain,
679 Object *arm_obj,
680 bArmature *arm,
681 const pxr::VtArray<pxr::GfMatrix4d> &bind_xforms,
682 const pxr::VtTokenArray &joint_order,
683 const blender::Map<pxr::TfToken, std::string> &joint_to_bone_map,
684 const pxr::UsdSkelTopology &skel_topology,
685 const pxr::UsdSkelSkeletonQuery &skel_query)
686{
687 if (!skel_query.HasRestPose()) {
688 return;
689 }
690
691 pxr::VtArray<pxr::GfMatrix4d> rest_xforms;
692 if (skel_query.ComputeJointLocalTransforms(&rest_xforms, pxr::UsdTimeCode::Default(), true)) {
693 BKE_pose_ensure(bmain, arm_obj, arm, false);
694
695 int64_t i = 0;
696 for (const pxr::TfToken &joint : joint_order) {
697 const std::string *name = joint_to_bone_map.lookup_ptr(joint);
698 if (name == nullptr) {
699 /* This joint doesn't correspond to any bone we created. Skip. */
700 continue;
701 }
702
703 bPoseChannel *pchan = BKE_pose_channel_find_name(arm_obj->pose, name->c_str());
704
705 pxr::GfMatrix4d xf = rest_xforms.AsConst()[i];
706 pxr::GfMatrix4d bind_xf = bind_xforms[i];
707
708 const int parent_id = skel_topology.GetParent(i);
709 if (parent_id >= 0) {
710 bind_xf = bind_xf * bind_xforms[parent_id].GetInverse();
711 }
712
713 xf = xf * bind_xf.GetInverse();
714
715 pxr::GfMatrix4f mat(xf);
716 BKE_pchan_apply_mat4(pchan, (float(*)[4])mat.data(), false);
717
718 i++;
719 }
720 }
721}
722
724 Object *arm_obj,
725 const pxr::UsdSkelSkeleton &skel,
727 const bool import_anim)
728{
729 if (!(arm_obj && arm_obj->data && arm_obj->type == OB_ARMATURE)) {
730 return;
731 }
732
733 pxr::UsdSkelCache skel_cache;
734 pxr::UsdSkelSkeletonQuery skel_query = skel_cache.GetSkelQuery(skel);
735
736 if (!skel_query.IsValid()) {
739 "%s: Couldn't query skeleton %s",
740 __func__,
741 skel.GetPath().GetAsString().c_str());
742 return;
743 }
744
745 const pxr::UsdSkelTopology &skel_topology = skel_query.GetTopology();
746 const pxr::VtTokenArray joint_order = skel_query.GetJointOrder();
747
748 if (joint_order.size() != skel_topology.size()) {
751 "%s: Topology and joint order size mismatch for skeleton %s",
752 __func__,
753 skel.GetPath().GetAsString().c_str());
754 return;
755 }
756
757 /* Each joint path should be valid and unique. */
758 blender::Set<pxr::TfToken> unique_joint_paths;
759 unique_joint_paths.reserve(joint_order.size());
760 const bool all_valid_paths = std::all_of(
761 joint_order.cbegin(), joint_order.cend(), [&unique_joint_paths](const pxr::TfToken &val) {
762 const bool is_valid = pxr::SdfPath::IsValidPathString(val);
763 return is_valid && unique_joint_paths.add(val);
764 });
765 if (!all_valid_paths) {
768 "%s: USD joint order array contains invalid or duplicated paths for skeleton %s",
769 __func__,
770 skel.GetPath().GetAsString().c_str());
771 return;
772 }
773
774 bArmature *arm = static_cast<bArmature *>(arm_obj->data);
775
776 /* Set the armature to edit mode when creating the bones. */
778
779 /* The bones we create, stored in the skeleton's joint order. */
781
782 /* Keep track of the bones we create for each joint.
783 * We'll need this when creating animation curves
784 * later. */
786
787 /* Create the bones. */
788 for (const pxr::TfToken &joint : joint_order) {
789 pxr::SdfPath bone_path(joint);
790 const std::string &bone_name = bone_path.GetName();
791 EditBone *bone = ED_armature_ebone_add(arm, bone_name.c_str());
792 if (!bone) {
795 "%s: Couldn't add bone for joint %s",
796 __func__,
797 joint.GetString().c_str());
798 edit_bones.append(nullptr);
799 continue;
800 }
801 joint_to_bone_map.add(joint, bone->name);
802 edit_bones.append(bone);
803 }
804
805 /* Sanity check: we should have created a bone for each joint. */
806 const size_t num_joints = skel_topology.GetNumJoints();
807 if (edit_bones.size() != num_joints) {
810 "%s: Mismatch in bone and joint counts for skeleton %s",
811 __func__,
812 skel.GetPath().GetAsString().c_str());
813 return;
814 }
815
816 /* Get the world space joint transforms at bind time. */
817 pxr::VtMatrix4dArray bind_xforms;
818 if (!skel_query.GetJointWorldBindTransforms(&bind_xforms)) {
821 "%s: Couldn't get world bind transforms for skeleton %s",
822 __func__,
823 skel.GetPath().GetAsString().c_str());
824 return;
825 }
826
827 if (bind_xforms.size() != num_joints) {
830 "%s: Mismatch in bind xforms and joint counts for skeleton %s",
831 __func__,
832 skel.GetPath().GetAsString().c_str());
833 return;
834 }
835
836 /* Check if any bone matrices have negative determinants,
837 * indicating negative scales, possibly due to mirroring
838 * operations. Such matrices can't be properly converted
839 * to Blender's axis/roll bone representation (see
840 * https://projects.blender.org/blender/blender/issues/82930).
841 * If we detect such matrices, we will flag an error and won't
842 * try to import the animation, since the rotations would
843 * be incorrect in such cases. Unfortunately, the Pixar
844 * `UsdSkel` examples of the "HumanFemale" suffer from
845 * this issue. */
846 bool negative_determinant = false;
847
848 /* Set bone rest transforms. */
849 for (size_t i = 0; i < num_joints; ++i) {
850 EditBone *ebone = edit_bones[i];
851
852 if (!ebone) {
853 continue;
854 }
855
856 pxr::GfMatrix4f mat(bind_xforms.AsConst()[i]);
857
858 float mat4[4][4];
859 mat.Get(mat4);
860
861 pxr::GfVec3f head(0.0f, 0.0f, 0.0f);
862 pxr::GfVec3f tail(0.0f, 1.0f, 0.0f);
863
864 copy_v3_v3(ebone->head, head.data());
865 copy_v3_v3(ebone->tail, tail.data());
866
867 ED_armature_ebone_from_mat4(ebone, mat4);
868
869 if (mat.GetDeterminant() < 0.0) {
870 negative_determinant = true;
871 }
872 }
873
874 bool valid_skeleton = true;
875 if (negative_determinant) {
876 valid_skeleton = false;
878 reports,
880 "USD Skeleton Import: bone matrices with negative determinants detected in prim %s. "
881 "Such matrices may indicate negative scales, possibly due to mirroring operations, "
882 "and can't currently be converted to Blender's bone representation. "
883 "The skeletal animation won't be imported",
884 skel.GetPath().GetAsString().c_str());
885 }
886
887 /* Set bone parenting. In addition, scale bones to account
888 * for separation between parents and children, so that the
889 * bone size is in proportion with the overall skeleton hierarchy.
890 * USD skeletons are composed of joints which we imperfectly
891 * represent as bones. */
892
893 /* This will record the child bone indices per parent bone,
894 * to simplify accessing children when computing lengths. */
895 blender::Vector<blender::Vector<int>> child_bones(num_joints);
896
897 for (size_t i = 0; i < num_joints; ++i) {
898 const int parent_idx = skel_topology.GetParent(i);
899 if (parent_idx < 0) {
900 continue;
901 }
902 if (parent_idx >= edit_bones.size()) {
903 CLOG_WARN(&LOG,
904 "Out of bounds parent index for bone %s on skeleton %s",
905 pxr::SdfPath(joint_order[i]).GetAsString().c_str(),
906 skel.GetPath().GetAsString().c_str());
907 continue;
908 }
909
910 child_bones[parent_idx].append(i);
911 if (edit_bones[i] && edit_bones[parent_idx]) {
912 edit_bones[i]->parent = edit_bones[parent_idx];
913 }
914 }
915
916 /* Use our custom bone length data if possible, otherwise fall back to estimated lengths. */
917 const pxr::UsdGeomPrimvarsAPI pv_api = pxr::UsdGeomPrimvarsAPI(skel.GetPrim());
918 const pxr::UsdGeomPrimvar pv_lengths = pv_api.GetPrimvar(BlenderBoneLengths);
919 if (pv_lengths.HasValue()) {
920 pxr::VtArray<float> blender_bone_lengths;
921 pv_lengths.ComputeFlattened(&blender_bone_lengths);
922
923 Span<float> bone_lengths = Span(blender_bone_lengths.cdata(), blender_bone_lengths.size());
924 for (size_t i = 0; i < num_joints; ++i) {
925 EditBone *bone = edit_bones[i];
926 pxr::GfVec3f head(bone->head);
927 pxr::GfVec3f tail(bone->tail);
928
929 tail = head + (tail - head).GetNormalized() * bone_lengths[i];
930 copy_v3_v3(bone->tail, tail.data());
931 }
932 }
933 else {
934 float avg_len_scale = 0;
935 for (size_t i = 0; i < num_joints; ++i) {
936
937 /* If the bone has any children, scale its length
938 * by the distance between this bone's head
939 * and the average head location of its children. */
940
941 if (child_bones[i].is_empty()) {
942 continue;
943 }
944
945 EditBone *parent = edit_bones[i];
946 if (!parent) {
947 continue;
948 }
949
950 pxr::GfVec3f avg_child_head(0);
951 for (int j : child_bones[i]) {
952 EditBone *child = edit_bones[j];
953 if (!child) {
954 continue;
955 }
956 pxr::GfVec3f child_head(child->head);
957 avg_child_head += child_head;
958 }
959
960 avg_child_head /= child_bones[i].size();
961
962 pxr::GfVec3f parent_head(parent->head);
963 pxr::GfVec3f parent_tail(parent->tail);
964
965 const float new_len = (avg_child_head - parent_head).GetLength();
966
967 /* Check for epsilon relative to the parent head before scaling. */
968 if (new_len > .00001 * max_mag_component(parent_head)) {
969 parent_tail = parent_head + (parent_tail - parent_head).GetNormalized() * new_len;
970 copy_v3_v3(parent->tail, parent_tail.data());
971 avg_len_scale += new_len;
972 }
973 }
974
975 /* Scale terminal bones by the average length scale. */
976 avg_len_scale /= num_joints;
977
978 for (size_t i = 0; i < num_joints; ++i) {
979 if (!child_bones[i].is_empty()) {
980 /* Not a terminal bone. */
981 continue;
982 }
983 EditBone *bone = edit_bones[i];
984 if (!bone) {
985 continue;
986 }
987 pxr::GfVec3f head(bone->head);
988
989 /* Check for epsilon relative to the head before scaling. */
990 if (avg_len_scale > .00001 * max_mag_component(head)) {
991 pxr::GfVec3f tail(bone->tail);
992 tail = head + (tail - head).GetNormalized() * avg_len_scale;
993 copy_v3_v3(bone->tail, tail.data());
994 }
995 }
996 }
997
998 /* Get out of edit mode. */
999 ED_armature_from_edit(bmain, arm);
1001
1003 bmain, arm_obj, arm, bind_xforms, joint_order, joint_to_bone_map, skel_topology, skel_query);
1004
1005 if (import_anim && valid_skeleton) {
1006 import_skeleton_curves(bmain, arm_obj, skel_query, joint_to_bone_map, reports);
1007 }
1008}
1009
1010void import_mesh_skel_bindings(Object *mesh_obj, const pxr::UsdPrim &prim, ReportList *reports)
1011{
1012 if (!(mesh_obj && mesh_obj->type == OB_MESH && prim)) {
1013 return;
1014 }
1015
1016 if (prim.IsInstanceProxy()) {
1017 /* Attempting to create a UsdSkelBindingAPI for
1018 * instance proxies generates USD errors. */
1019 return;
1020 }
1021
1022 pxr::UsdSkelBindingAPI skel_api(prim);
1023
1024 pxr::UsdSkelSkeleton skel = skel_api.GetInheritedSkeleton();
1025
1026 if (!skel) {
1027 return;
1028 }
1029
1030 /* Get the joint identifiers from the skeleton. We will
1031 * need these to construct deform groups. */
1032 pxr::VtArray<pxr::TfToken> joints;
1033
1034 if (skel_api.GetJointsAttr().HasAuthoredValue()) {
1035 skel_api.GetJointsAttr().Get(&joints);
1036 }
1037 else if (skel.GetJointsAttr().HasAuthoredValue()) {
1038 skel.GetJointsAttr().Get(&joints);
1039 }
1040
1041 if (joints.empty()) {
1042 return;
1043 }
1044
1045 /* Get the joint indices, which specify which joints influence a given point. */
1046 pxr::UsdGeomPrimvar joint_indices_primvar = skel_api.GetJointIndicesPrimvar();
1047 if (!(joint_indices_primvar && joint_indices_primvar.HasAuthoredValue())) {
1048 return;
1049 }
1050
1051 /* Get the weights, which specify the weight of a joint on a given point. */
1052 pxr::UsdGeomPrimvar joint_weights_primvar = skel_api.GetJointWeightsPrimvar();
1053 if (!(joint_weights_primvar && joint_weights_primvar.HasAuthoredValue())) {
1054 return;
1055 }
1056
1057 /* Element size specifies the number of joints that might influence a given point.
1058 * This is the stride we take when accessing the indices and weights for a given point. */
1059 int joint_indices_elem_size = joint_indices_primvar.GetElementSize();
1060 int joint_weights_elem_size = joint_weights_primvar.GetElementSize();
1061
1062 /* We expect the element counts to match. */
1063 if (joint_indices_elem_size != joint_weights_elem_size) {
1066 "%s: Joint weights and joint indices element size mismatch for prim %s",
1067 __func__,
1068 prim.GetPath().GetAsString().c_str());
1069 return;
1070 }
1071
1072 /* Get the joint indices and weights. */
1073 pxr::VtIntArray joint_indices;
1074 joint_indices_primvar.ComputeFlattened(&joint_indices);
1075
1076 pxr::VtFloatArray joint_weights;
1077 joint_weights_primvar.ComputeFlattened(&joint_weights);
1078
1079 if (joint_indices.empty() || joint_weights.empty()) {
1080 return;
1081 }
1082
1083 if (joint_indices.size() != joint_weights.size()) {
1086 "%s: Joint weights and joint indices size mismatch for prim %s",
1087 __func__,
1088 prim.GetPath().GetAsString().c_str());
1089 return;
1090 }
1091
1092 Mesh *mesh = static_cast<Mesh *>(mesh_obj->data);
1093
1094 const pxr::TfToken interp = joint_weights_primvar.GetInterpolation();
1095
1096 /* Sanity check: we expect only vertex or constant interpolation. */
1097 if (!ELEM(interp, pxr::UsdGeomTokens->vertex, pxr::UsdGeomTokens->constant)) {
1100 "%s: Unexpected joint weights interpolation type %s for prim %s",
1101 __func__,
1102 interp.GetString().c_str(),
1103 prim.GetPath().GetAsString().c_str());
1104 return;
1105 }
1106
1107 /* Sanity check: make sure we have the expected number of values for the interpolation type. */
1108 if (interp == pxr::UsdGeomTokens->vertex &&
1109 joint_weights.size() != mesh->verts_num * joint_weights_elem_size)
1110 {
1113 "%s: Joint weights of unexpected size for vertex interpolation for prim %s",
1114 __func__,
1115 prim.GetPath().GetAsString().c_str());
1116 return;
1117 }
1118
1119 if (interp == pxr::UsdGeomTokens->constant && joint_weights.size() != joint_weights_elem_size) {
1122 "%s: Joint weights of unexpected size for constant interpolation for prim %s",
1123 __func__,
1124 prim.GetPath().GetAsString().c_str());
1125 return;
1126 }
1127
1128 /* Determine which joint indices are used for skinning this prim. */
1129 blender::Vector<int> used_indices;
1130 for (int index : joint_indices.AsConst()) {
1131 if (std::find(used_indices.begin(), used_indices.end(), index) == used_indices.end()) {
1132 /* We haven't accounted for this index yet. */
1133 if (index < 0 || index >= joints.size()) {
1134 CLOG_ERROR(&LOG, "Out of bound joint index %d for mesh %s", index, mesh_obj->id.name + 2);
1135 return;
1136 }
1137 used_indices.append(index);
1138 }
1139 }
1140
1141 if (used_indices.is_empty()) {
1142 return;
1143 }
1144
1145 if (BKE_object_defgroup_data_create(static_cast<ID *>(mesh_obj->data)) == nullptr) {
1148 "%s: Error creating deform group data for mesh %s",
1149 __func__,
1150 mesh_obj->id.name + 2);
1151 return;
1152 }
1153
1154 /* Add the armature modifier, if one doesn't exist. */
1157 BLI_addtail(&mesh_obj->modifiers, md);
1158 BKE_modifiers_persistent_uid_init(*mesh_obj, *md);
1159 }
1160
1161 /* Create a deform group per joint. */
1162 blender::Vector<bDeformGroup *> joint_def_grps(joints.size(), nullptr);
1163
1164 for (int idx : used_indices) {
1165 std::string joint_name = pxr::SdfPath(joints.AsConst()[idx]).GetName();
1166 if (!BKE_object_defgroup_find_name(mesh_obj, joint_name.c_str())) {
1167 bDeformGroup *def_grp = BKE_object_defgroup_add_name(mesh_obj, joint_name.c_str());
1168 joint_def_grps[idx] = def_grp;
1169 }
1170 }
1171
1172 /* Set the deform group verts and weights. */
1173 for (int i = 0; i < mesh->verts_num; ++i) {
1174 /* Offset into the weights array, which is
1175 * always 0 for constant interpolation. */
1176 int offset = 0;
1177 if (interp == pxr::UsdGeomTokens->vertex) {
1178 offset = i * joint_weights_elem_size;
1179 }
1180 for (int j = 0; j < joint_weights_elem_size; ++j) {
1181 const int k = offset + j;
1182 const float w = joint_weights.AsConst()[k];
1183 if (w < .00001) {
1184 /* No deform group if zero weight. */
1185 continue;
1186 }
1187 const int joint_idx = joint_indices.AsConst()[k];
1188 if (bDeformGroup *def_grp = joint_def_grps[joint_idx]) {
1190 }
1191 }
1192 }
1193}
1194
1195void skel_export_chaser(pxr::UsdStageRefPtr stage,
1196 const ObjExportMap &armature_export_map,
1197 const ObjExportMap &skinned_mesh_export_map,
1198 const ObjExportMap &shape_key_mesh_export_map,
1199 const Depsgraph *depsgraph)
1200{
1201 /* We may need to compute the world transforms of certain primitives when
1202 * setting skinning data. Using a shared transform cache can make computing
1203 * the transforms more efficient. */
1204 pxr::UsdGeomXformCache xf_cache(1.0);
1206 stage, armature_export_map, skinned_mesh_export_map, xf_cache, depsgraph);
1207 shape_key_export_chaser(stage, shape_key_mesh_export_map);
1208}
1209
1210void skinned_mesh_export_chaser(pxr::UsdStageRefPtr stage,
1211 const ObjExportMap &armature_export_map,
1212 const ObjExportMap &skinned_mesh_export_map,
1213 pxr::UsdGeomXformCache &xf_cache,
1214 const Depsgraph *depsgraph)
1215{
1216 /* Finish creating skinned mesh bindings. */
1217 for (const auto &item : skinned_mesh_export_map.items()) {
1218 const Object *mesh_obj = item.key;
1219 const pxr::SdfPath &mesh_path = item.value;
1220
1221 /* Get the mesh prim from the stage. */
1222 pxr::UsdPrim mesh_prim = stage->GetPrimAtPath(mesh_path);
1223 if (!mesh_prim) {
1224 CLOG_WARN(&LOG,
1225 "Invalid export map prim path %s for mesh object %s",
1226 mesh_path.GetAsString().c_str(),
1227 mesh_obj->id.name + 2);
1228 continue;
1229 }
1230
1231 /* Get the armature bound to the mesh's armature modifier. */
1232 const Object *arm_obj = get_armature_modifier_obj(*mesh_obj, depsgraph);
1233 if (!arm_obj) {
1234 CLOG_WARN(&LOG, "Invalid armature modifier for skinned mesh %s", mesh_obj->id.name + 2);
1235 continue;
1236 }
1237 /* Look up the USD skeleton corresponding to the armature object. */
1238 const pxr::SdfPath *path = armature_export_map.lookup_ptr(arm_obj);
1239 if (!path) {
1240 CLOG_WARN(&LOG, "No export map entry for armature object %s", mesh_obj->id.name + 2);
1241 continue;
1242 }
1243 /* Get the skeleton prim. */
1244 pxr::UsdPrim skel_prim = stage->GetPrimAtPath(*path);
1245 pxr::UsdSkelSkeleton skel(skel_prim);
1246 if (!skel) {
1247 CLOG_WARN(&LOG, "Invalid USD skeleton for armature object %s", arm_obj->id.name + 2);
1248 continue;
1249 }
1250
1251 add_skinned_mesh_bindings(skel, mesh_prim, xf_cache);
1252 }
1253}
1254
1255void shape_key_export_chaser(pxr::UsdStageRefPtr stage,
1256 const ObjExportMap &shape_key_mesh_export_map)
1257{
1259
1260 /* We will keep track of the mesh primitives to clean up the temporary
1261 * weights attribute at the end. */
1262 Vector<pxr::UsdPrim> mesh_prims;
1263
1264 /* Finish creating blend shape bindings. */
1265 for (const auto &item : shape_key_mesh_export_map.items()) {
1266 const Object *mesh_obj = item.key;
1267 const pxr::SdfPath &mesh_path = item.value;
1268
1269 /* Get the mesh prim from the stage. */
1270 pxr::UsdPrim mesh_prim = stage->GetPrimAtPath(mesh_path);
1271 if (!mesh_prim) {
1272 CLOG_WARN(&LOG,
1273 "Invalid export map prim path %s for mesh object %s",
1274 mesh_path.GetAsString().c_str(),
1275 mesh_obj->id.name + 2);
1276 continue;
1277 }
1278
1279 /* Keep track of all the mesh primitives with blend shapes, for cleanup below. */
1280 mesh_prims.append(mesh_prim);
1281
1282 pxr::UsdSkelBindingAPI skel_api = pxr::UsdSkelBindingAPI::Apply(mesh_prim);
1283
1284 if (!skel_api) {
1285 CLOG_WARN(&LOG,
1286 "Couldn't apply UsdSkelBindingAPI to prim %s",
1287 mesh_prim.GetPath().GetAsString().c_str());
1288 return;
1289 }
1290
1291 pxr::UsdSkelSkeleton skel;
1292 if (skel_api.GetSkeleton(&skel)) {
1293 /* We have a bound skeleton, so we add it to the map. */
1294 pxr::SdfPathSet *mesh_paths = skel_to_mesh.lookup_ptr(skel.GetPath());
1295 if (!mesh_paths) {
1296 skel_to_mesh.add_new(skel.GetPath(), pxr::SdfPathSet());
1297 mesh_paths = skel_to_mesh.lookup_ptr(skel.GetPath());
1298 }
1299 if (mesh_paths) {
1300 mesh_paths->insert(mesh_prim.GetPath());
1301 }
1302 continue;
1303 }
1304
1305 /* The mesh is not bound to a skeleton, so we must create one for it. */
1306 ensure_blend_shape_skeleton(stage, mesh_prim);
1307 }
1308
1309 if (skel_to_mesh.is_empty()) {
1310 return;
1311 }
1312
1313 for (const auto &item : skel_to_mesh.items()) {
1314 remap_blend_shape_anim(stage, item.key, item.value);
1315 }
1316
1317 /* Finally, delete the temp blendshape weights attributes. */
1318 for (const pxr::UsdPrim &prim : mesh_prims) {
1319 pxr::UsdGeomPrimvarsAPI(prim).RemovePrimvar(TempBlendShapeWeightsPrimvarName);
1320 }
1321}
1322
1323void export_deform_verts(const Mesh *mesh,
1324 const pxr::UsdSkelBindingAPI &skel_api,
1325 const Span<StringRef> bone_names)
1326{
1327 BLI_assert(mesh);
1328 BLI_assert(skel_api);
1329
1330 /* Map a deform vertex group index to the
1331 * index of the corresponding joint. I.e.,
1332 * joint_index[n] is the joint index of the
1333 * n-th vertex group. */
1334 Vector<int> joint_index;
1335
1336 /* Build the index mapping. */
1337 LISTBASE_FOREACH (const bDeformGroup *, def, &mesh->vertex_group_names) {
1338 int bone_idx = -1;
1339 /* For now, n-squared search is acceptable. */
1340 for (int i = 0; i < bone_names.size(); ++i) {
1341 if (bone_names[i] == def->name) {
1342 bone_idx = i;
1343 break;
1344 }
1345 }
1346
1347 joint_index.append(bone_idx);
1348 }
1349
1350 if (joint_index.is_empty()) {
1351 return;
1352 }
1353
1354 const Span<MDeformVert> dverts = mesh->deform_verts();
1355
1356 int max_totweight = 1;
1357 for (const int i : dverts.index_range()) {
1358 const MDeformVert &vert = dverts[i];
1359 max_totweight = std::max(vert.totweight, max_totweight);
1360 }
1361
1362 /* elem_size will specify the number of
1363 * joints that can influence a given point. */
1364 const int element_size = max_totweight;
1365 int num_points = mesh->verts_num;
1366
1367 pxr::VtArray<int> joint_indices(num_points * element_size, 0);
1368 pxr::VtArray<float> joint_weights(num_points * element_size, 0.0f);
1369
1370 /* Current offset into the indices and weights arrays. */
1371 int offset = 0;
1372
1373 for (const int i : dverts.index_range()) {
1374 const MDeformVert &vert = dverts[i];
1375
1376 for (int j = 0; j < element_size; ++j, ++offset) {
1377
1378 if (offset >= joint_indices.size()) {
1380 return;
1381 }
1382
1383 if (j >= vert.totweight) {
1384 continue;
1385 }
1386
1387 int def_nr = int(vert.dw[j].def_nr);
1388
1389 if (def_nr >= joint_index.size()) {
1391 continue;
1392 }
1393
1394 if (joint_index[def_nr] == -1) {
1395 continue;
1396 }
1397
1398 joint_indices[offset] = joint_index[def_nr];
1399 joint_weights[offset] = vert.dw[j].weight;
1400 }
1401 }
1402
1403 pxr::UsdSkelNormalizeWeights(joint_weights, element_size);
1404
1405 skel_api.CreateJointIndicesPrimvar(false, element_size).GetAttr().Set(joint_indices);
1406 skel_api.CreateJointWeightsPrimvar(false, element_size).GetAttr().Set(joint_weights);
1407}
1408
1409} // 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