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