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