Blender V4.5
ControllerExporter.cpp
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2010-2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "COLLADASWBaseInputElement.h"
10#include "COLLADASWInstanceController.h"
11#include "COLLADASWPrimitves.h"
12#include "COLLADASWSource.h"
13
14#include "DNA_action_types.h"
15#include "DNA_meshdata_types.h"
16
17#include "BKE_action.hh"
18#include "BKE_armature.hh"
19#include "BKE_deform.hh"
20#include "BKE_key.hh"
21#include "BKE_lib_id.hh"
22#include "BKE_mesh.hh"
23
24#include "ED_armature.hh"
25
26#include "BLI_listbase.h"
27#include "BLI_math_matrix.h"
28
29#include "ControllerExporter.h"
30#include "GeometryExporter.h"
31
32#include "collada_utils.h"
33
35{
36 return bc_get_assigned_armature(ob) != nullptr;
37}
38
39void ControllerExporter::write_bone_URLs(COLLADASW::InstanceController &ins,
40 Object *ob_arm,
41 Bone *bone)
42{
43 if (bc_is_root_bone(bone, this->export_settings.get_deform_bones_only())) {
44 std::string node_id = translate_id(id_name(ob_arm) + "_" + bone->name);
45 ins.addSkeleton(COLLADABU::URI(COLLADABU::Utils::EMPTY_STRING, node_id));
46 }
47 else {
48 LISTBASE_FOREACH (Bone *, child, &bone->childbase) {
49 write_bone_URLs(ins, ob_arm, child);
50 }
51 }
52}
53
55{
56 Object *ob_arm = bc_get_assigned_armature(ob);
57 bArmature *arm = (bArmature *)ob_arm->data;
58
59 const std::string &controller_id = get_controller_id(ob_arm, ob);
60
61 COLLADASW::InstanceController ins(mSW);
62 ins.setUrl(COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, controller_id));
63
64 Mesh *mesh = (Mesh *)ob->data;
65 if (mesh->deform_verts().is_empty()) {
66 return false;
67 }
68
69 /* write root bone URLs */
70 LISTBASE_FOREACH (Bone *, bone, &arm->bonebase) {
71 write_bone_URLs(ins, ob_arm, bone);
72 }
73
75 ins.getBindMaterial(), ob, this->export_settings.get_active_uv_only());
76
77 ins.add();
78 return true;
79}
80
82{
83 Scene *sce = blender_context.get_scene();
84 openLibrary();
85
88 sce, *this, this->export_settings.get_export_set());
89
90 closeLibrary();
91}
92
94{
95 Object *ob_arm = bc_get_assigned_armature(ob);
96 Key *key = BKE_key_from_object(ob);
97
98 if (ob_arm) {
99 export_skin_controller(ob, ob_arm);
100 }
101 if (key && this->export_settings.get_include_shapekeys()) {
102 export_morph_controller(ob, key);
103 }
104}
105#if 0
106
107bool ArmatureExporter::already_written(Object *ob_arm)
108{
109 return std::find(written_armatures.begin(), written_armatures.end(), ob_arm) !=
110 written_armatures.end();
111}
112
113void ArmatureExporter::wrote(Object *ob_arm)
114{
115 written_armatures.push_back(ob_arm);
116}
117
118void ArmatureExporter::find_objects_using_armature(Object *ob_arm,
119 std::vector<Object *> &objects,
120 Scene *sce)
121{
122 objects.clear();
123
124 Base *base = (Base *)sce->base.first;
125 while (base) {
126 Object *ob = base->object;
127
128 if (ob->type == OB_MESH && get_assigned_armature(ob) == ob_arm) {
129 objects.push_back(ob);
130 }
131
132 base = base->next;
133 }
134}
135#endif
136
137std::string ControllerExporter::get_controller_id(Object *ob_arm, Object *ob)
138{
139 return translate_id(id_name(ob_arm)) + "_" + translate_id(id_name(ob)) +
140 SKIN_CONTROLLER_ID_SUFFIX;
141}
142
143std::string ControllerExporter::get_controller_id(Key *key, Object *ob)
144{
145 return translate_id(id_name(ob)) + MORPH_CONTROLLER_ID_SUFFIX;
146}
147
148void ControllerExporter::export_skin_controller(Object *ob, Object *ob_arm)
149{
150 /* joint names
151 * joint inverse bind matrices
152 * vertex weights */
153
154 /* input:
155 * joint names: ob -> vertex group names
156 * vertex group weights: mesh->dvert -> groups -> index, weight */
157
158 bool use_instantiation = this->export_settings.get_use_object_instantiation();
159 Mesh *mesh;
160
161 if (((Mesh *)ob->data)->deform_verts().is_empty()) {
162 return;
163 }
164
165 mesh = bc_get_mesh_copy(blender_context,
166 ob,
167 this->export_settings.get_export_mesh_type(),
168 this->export_settings.get_apply_modifiers(),
169 this->export_settings.get_triangulate());
170
171 std::string controller_name = id_name(ob_arm);
172 std::string controller_id = get_controller_id(ob_arm, ob);
173
174 openSkin(controller_id,
175 controller_name,
176 COLLADABU::URI(COLLADABU::Utils::EMPTY_STRING, get_geometry_id(ob, use_instantiation)));
177
178 add_bind_shape_mat(ob);
179
180 const ListBase *defbase = BKE_object_defgroup_list(ob);
181 std::string joints_source_id = add_joints_source(ob_arm, defbase, controller_id);
182 std::string inv_bind_mat_source_id = add_inv_bind_mats_source(ob_arm, defbase, controller_id);
183
184 std::list<int> vcounts;
185 std::list<int> joints;
186 std::list<float> weights;
187
188 {
189 int i, j;
190
191 /* def group index -> joint index */
192 std::vector<int> joint_index_by_def_index;
193 const bDeformGroup *def;
194
195 for (def = (const bDeformGroup *)defbase->first, i = 0, j = 0; def; def = def->next, i++) {
196 if (is_bone_defgroup(ob_arm, def)) {
197 joint_index_by_def_index.push_back(j++);
198 }
199 else {
200 joint_index_by_def_index.push_back(-1);
201 }
202 }
203
204 const MDeformVert *dvert = mesh->deform_verts().data();
205 int oob_counter = 0;
206 for (i = 0; i < mesh->verts_num; i++) {
207 const MDeformVert *vert = &dvert[i];
208 std::map<int, float> jw;
209
210 /* We're normalizing the weights later */
211 float sumw = 0.0f;
212
213 for (j = 0; j < vert->totweight; j++) {
214 uint idx = vert->dw[j].def_nr;
215 if (idx >= joint_index_by_def_index.size()) {
216 /* XXX: Maybe better find out where and
217 * why the Out Of Bound indexes get created? */
218 oob_counter += 1;
219 }
220 else {
221 int joint_index = joint_index_by_def_index[idx];
222 if (joint_index != -1 && vert->dw[j].weight > 0.0f) {
223 jw[joint_index] += vert->dw[j].weight;
224 sumw += vert->dw[j].weight;
225 }
226 }
227 }
228
229 if (sumw > 0.0f) {
230 float invsumw = 1.0f / sumw;
231 vcounts.push_back(jw.size());
232 for (auto &index_and_weight : jw) {
233 joints.push_back(index_and_weight.first);
234 weights.push_back(invsumw * index_and_weight.second);
235 }
236 }
237 else {
238 vcounts.push_back(0);
239#if 0
240 vcounts.push_back(1);
241 joints.push_back(-1);
242 weights.push_back(1.0f);
243#endif
244 }
245 }
246
247 if (oob_counter > 0) {
248 fprintf(stderr,
249 "Ignored %d Vertex weights which use index to non existing VGroup %zu.\n",
250 oob_counter,
251 joint_index_by_def_index.size());
252 }
253 }
254
255 std::string weights_source_id = add_weights_source(mesh, controller_id, weights);
256 add_joints_element(defbase, joints_source_id, inv_bind_mat_source_id);
257 add_vertex_weights_element(weights_source_id, joints_source_id, vcounts, joints);
258
259 BKE_id_free(nullptr, mesh);
260
261 closeSkin();
262 closeController();
263}
264
265void ControllerExporter::export_morph_controller(Object *ob, Key *key)
266{
267 bool use_instantiation = this->export_settings.get_use_object_instantiation();
268 Mesh *mesh;
269
270 mesh = bc_get_mesh_copy(blender_context,
271 ob,
272 this->export_settings.get_export_mesh_type(),
273 this->export_settings.get_apply_modifiers(),
274 this->export_settings.get_triangulate());
275
276 std::string controller_name = id_name(ob) + "-morph";
277 std::string controller_id = get_controller_id(key, ob);
278
279 openMorph(
280 controller_id,
281 controller_name,
282 COLLADABU::URI(COLLADABU::Utils::EMPTY_STRING, get_geometry_id(ob, use_instantiation)));
283
284 std::string targets_id = add_morph_targets(key, ob);
285 std::string morph_weights_id = add_morph_weights(key, ob);
286
287 COLLADASW::TargetsElement targets(mSW);
288
289 COLLADASW::InputList &input = targets.getInputList();
290
291 input.push_back(COLLADASW::Input(
292 COLLADASW::InputSemantic::MORPH_TARGET, /* constant declared in COLLADASWInputList.h */
293 COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, targets_id)));
294 input.push_back(
295 COLLADASW::Input(COLLADASW::InputSemantic::MORPH_WEIGHT,
296 COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, morph_weights_id)));
297 targets.add();
298
299 BKE_id_free(nullptr, mesh);
300
301 /* support for animations
302 * can also try the base element and param alternative */
303 add_weight_extras(key);
304 closeMorph();
305 closeController();
306}
307
308std::string ControllerExporter::add_morph_targets(Key *key, Object *ob)
309{
310 std::string source_id = translate_id(id_name(ob)) + TARGETS_SOURCE_ID_SUFFIX;
311
312 COLLADASW::IdRefSource source(mSW);
313 source.setId(source_id);
314 source.setArrayId(source_id + ARRAY_ID_SUFFIX);
315 source.setAccessorCount(key->totkey - 1);
316 source.setAccessorStride(1);
317
318 COLLADASW::SourceBase::ParameterNameList &param = source.getParameterNameList();
319 param.emplace_back("IDREF");
320
321 source.prepareToAppendValues();
322
323 KeyBlock *kb = (KeyBlock *)key->block.first;
324 /* skip the basis */
325 kb = kb->next;
326 for (; kb; kb = kb->next) {
327 std::string geom_id = get_geometry_id(ob, false) + "_morph_" + translate_id(kb->name);
328 source.appendValues(geom_id);
329 }
330
331 source.finish();
332
333 return source_id;
334}
335
336std::string ControllerExporter::add_morph_weights(Key *key, Object *ob)
337{
338 std::string source_id = translate_id(id_name(ob)) + WEIGHTS_SOURCE_ID_SUFFIX;
339
340 COLLADASW::FloatSourceF source(mSW);
341 source.setId(source_id);
342 source.setArrayId(source_id + ARRAY_ID_SUFFIX);
343 source.setAccessorCount(key->totkey - 1);
344 source.setAccessorStride(1);
345
346 COLLADASW::SourceBase::ParameterNameList &param = source.getParameterNameList();
347 param.emplace_back("MORPH_WEIGHT");
348
349 source.prepareToAppendValues();
350
351 KeyBlock *kb = (KeyBlock *)key->block.first;
352 /* skip the basis */
353 kb = kb->next;
354 for (; kb; kb = kb->next) {
355 float weight = kb->curval;
356 source.appendValues(weight);
357 }
358 source.finish();
359
360 return source_id;
361}
362
363void ControllerExporter::add_weight_extras(Key *key)
364{
365 /* can also try the base element and param alternative */
366 COLLADASW::BaseExtraTechnique extra;
367
368 KeyBlock *kb = (KeyBlock *)key->block.first;
369 /* skip the basis */
370 kb = kb->next;
371 for (; kb; kb = kb->next) {
372 /* XXX why is the weight not used here and set to 0.0?
373 * float weight = kb->curval; */
374 extra.addExtraTechniqueParameter("KHR", "morph_weights", 0.000, "MORPH_WEIGHT_TO_TARGET");
375 }
376}
377
378void ControllerExporter::add_joints_element(const ListBase *defbase,
379 const std::string &joints_source_id,
380 const std::string &inv_bind_mat_source_id)
381{
382 COLLADASW::JointsElement joints(mSW);
383 COLLADASW::InputList &input = joints.getInputList();
384
385 input.push_back(COLLADASW::Input(
386 COLLADASW::InputSemantic::JOINT, /* constant declared in COLLADASWInputList.h */
387 COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, joints_source_id)));
388 input.push_back(
389 COLLADASW::Input(COLLADASW::InputSemantic::BINDMATRIX,
390 COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, inv_bind_mat_source_id)));
391 joints.add();
392}
393
394void ControllerExporter::add_bind_shape_mat(Object *ob)
395{
396 double bind_mat[4][4];
397 float f_obmat[4][4];
398 BKE_object_matrix_local_get(ob, f_obmat);
399
400 if (export_settings.get_apply_global_orientation()) {
401 /* do nothing, rotation is going to be applied to the Data */
402 }
403 else {
404 bc_add_global_transform(f_obmat, export_settings.get_global_transform());
405 }
406
407 // UnitConverter::mat4_to_dae_double(bind_mat, ob->object_to_world().ptr());
408 UnitConverter::mat4_to_dae_double(bind_mat, f_obmat);
409 if (this->export_settings.get_limit_precision()) {
411 }
412
413 addBindShapeTransform(bind_mat);
414}
415
416std::string ControllerExporter::add_joints_source(Object *ob_arm,
417 const ListBase *defbase,
418 const std::string &controller_id)
419{
420 std::string source_id = controller_id + JOINTS_SOURCE_ID_SUFFIX;
421
422 int totjoint = 0;
423 LISTBASE_FOREACH (bDeformGroup *, def, defbase) {
424 if (is_bone_defgroup(ob_arm, def)) {
425 totjoint++;
426 }
427 }
428
429 COLLADASW::NameSource source(mSW);
430 source.setId(source_id);
431 source.setArrayId(source_id + ARRAY_ID_SUFFIX);
432 source.setAccessorCount(totjoint);
433 source.setAccessorStride(1);
434
435 COLLADASW::SourceBase::ParameterNameList &param = source.getParameterNameList();
436 param.emplace_back("JOINT");
437
438 source.prepareToAppendValues();
439
440 LISTBASE_FOREACH (bDeformGroup *, def, defbase) {
441 Bone *bone = get_bone_from_defgroup(ob_arm, def);
442 if (bone) {
443 source.appendValues(get_joint_sid(bone));
444 }
445 }
446
447 source.finish();
448
449 return source_id;
450}
451
452std::string ControllerExporter::add_inv_bind_mats_source(Object *ob_arm,
453 const ListBase *defbase,
454 const std::string &controller_id)
455{
456 std::string source_id = controller_id + BIND_POSES_SOURCE_ID_SUFFIX;
457
458 int totjoint = 0;
459 LISTBASE_FOREACH (bDeformGroup *, def, defbase) {
460 if (is_bone_defgroup(ob_arm, def)) {
461 totjoint++;
462 }
463 }
464
465 COLLADASW::FloatSourceF source(mSW);
466 source.setId(source_id);
467 source.setArrayId(source_id + ARRAY_ID_SUFFIX);
468 source.setAccessorCount(totjoint); // BLI_listbase_count(defbase));
469 source.setAccessorStride(16);
470
471 source.setParameterTypeName(&COLLADASW::CSWC::CSW_VALUE_TYPE_FLOAT4x4);
472 COLLADASW::SourceBase::ParameterNameList &param = source.getParameterNameList();
473 param.emplace_back("TRANSFORM");
474
475 source.prepareToAppendValues();
476
477 bPose *pose = ob_arm->pose;
478 bArmature *arm = (bArmature *)ob_arm->data;
479
480 int flag = arm->flag;
481
482 /* put armature in rest position */
483 if (!(arm->flag & ARM_RESTPOS)) {
484 Depsgraph *depsgraph = blender_context.get_depsgraph();
485 Scene *scene = blender_context.get_scene();
486
487 arm->flag |= ARM_RESTPOS;
488 BKE_pose_where_is(depsgraph, scene, ob_arm);
489 }
490
491 LISTBASE_FOREACH (bDeformGroup *, def, defbase) {
492 if (is_bone_defgroup(ob_arm, def)) {
493 bPoseChannel *pchan = BKE_pose_channel_find_name(pose, def->name);
494
495 float mat[4][4];
496 float world[4][4];
497 float inv_bind_mat[4][4];
498
499 float bind_mat[4][4]; /* derived from bone->arm_mat */
500
501 bool has_bindmat = bc_get_property_matrix(pchan->bone, "bind_mat", bind_mat);
502
503 if (!has_bindmat) {
504
505 /* Have no bind matrix stored, try old style <= Blender 2.78 */
506
508 this->export_settings, pchan->bone, bind_mat, pchan->bone->arm_mat, true);
509
510 /* SL/OPEN_SIM COMPATIBILITY */
511 if (export_settings.get_open_sim()) {
512 float loc[3];
513 float rot[3] = {0, 0, 0};
514 float scale[3];
515 bc_decompose(bind_mat, loc, nullptr, nullptr, scale);
516
517 /* Only translations, no rotation vs armature */
518 loc_eulO_size_to_mat4(bind_mat, loc, rot, scale, 6);
519 }
520 }
521
522 /* make world-space matrix (bind_mat is armature-space) */
523 mul_m4_m4m4(world, ob_arm->object_to_world().ptr(), bind_mat);
524
525 if (!has_bindmat) {
526 if (export_settings.get_apply_global_orientation()) {
527 bc_apply_global_transform(world, export_settings.get_global_transform());
528 }
529 }
530
531 invert_m4_m4(mat, world);
532 UnitConverter::mat4_to_dae(inv_bind_mat, mat);
533 if (this->export_settings.get_limit_precision()) {
535 }
536 source.appendValues(inv_bind_mat);
537 }
538 }
539
540 /* back from rest position */
541 if (!(flag & ARM_RESTPOS)) {
542 Depsgraph *depsgraph = blender_context.get_depsgraph();
543 Scene *scene = blender_context.get_scene();
544 arm->flag = flag;
545 BKE_pose_where_is(depsgraph, scene, ob_arm);
546 }
547
548 source.finish();
549
550 return source_id;
551}
552
553Bone *ControllerExporter::get_bone_from_defgroup(Object *ob_arm, const bDeformGroup *def)
554{
555 bPoseChannel *pchan = BKE_pose_channel_find_name(ob_arm->pose, def->name);
556 return pchan ? pchan->bone : nullptr;
557}
558
559bool ControllerExporter::is_bone_defgroup(Object *ob_arm, const bDeformGroup *def)
560{
561 return get_bone_from_defgroup(ob_arm, def) != nullptr;
562}
563
564std::string ControllerExporter::add_weights_source(Mesh *mesh,
565 const std::string &controller_id,
566 const std::list<float> &weights)
567{
568 std::string source_id = controller_id + WEIGHTS_SOURCE_ID_SUFFIX;
569
570 COLLADASW::FloatSourceF source(mSW);
571 source.setId(source_id);
572 source.setArrayId(source_id + ARRAY_ID_SUFFIX);
573 source.setAccessorCount(weights.size());
574 source.setAccessorStride(1);
575
576 COLLADASW::SourceBase::ParameterNameList &param = source.getParameterNameList();
577 param.emplace_back("WEIGHT");
578
579 source.prepareToAppendValues();
580
581 for (float weight : weights) {
582 source.appendValues(weight);
583 }
584
585 source.finish();
586
587 return source_id;
588}
589
590void ControllerExporter::add_vertex_weights_element(const std::string &weights_source_id,
591 const std::string &joints_source_id,
592 const std::list<int> &vcounts,
593 const std::list<int> &joints)
594{
595 COLLADASW::VertexWeightsElement weightselem(mSW);
596 COLLADASW::InputList &input = weightselem.getInputList();
597
598 int offset = 0;
599 input.push_back(COLLADASW::Input(
600 COLLADASW::InputSemantic::JOINT, /* constant declared in COLLADASWInputList.h */
601 COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, joints_source_id),
602 offset++));
603 input.push_back(
604 COLLADASW::Input(COLLADASW::InputSemantic::WEIGHT,
605 COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, weights_source_id),
606 offset++));
607
608 weightselem.setCount(vcounts.size());
609
610 /* write number of deformers per vertex */
611 COLLADASW::PrimitivesBase::VCountList vcountlist;
612
613 vcountlist.resize(vcounts.size());
614 std::copy(vcounts.begin(), vcounts.end(), vcountlist.begin());
615
616 weightselem.prepareToAppendVCountValues();
617 weightselem.appendVertexCount(vcountlist);
618
619 weightselem.CloseVCountAndOpenVElement();
620
621 /* write deformer index - weight index pairs */
622 int weight_index = 0;
623 for (int joint_index : joints) {
624 weightselem.appendValues(joint_index, weight_index++);
625 }
626
627 weightselem.finish();
628}
Blender kernel action and pose functionality.
bPoseChannel * BKE_pose_channel_find_name(const bPose *pose, const char *name)
void BKE_pose_where_is(Depsgraph *depsgraph, Scene *scene, Object *ob)
Definition armature.cc:3051
support for deformation groups and hooks.
const ListBase * BKE_object_defgroup_list(const Object *ob)
Definition deform.cc:574
Key * BKE_key_from_object(Object *ob)
Definition key.cc:1824
void BKE_id_free(Main *bmain, void *idv)
void BKE_object_matrix_local_get(Object *ob, float r_mat[4][4])
#define LISTBASE_FOREACH(type, var, list)
void mul_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4])
void loc_eulO_size_to_mat4(float R[4][4], const float loc[3], const float eul[3], const float size[3], short order)
bool invert_m4_m4(float inverse[4][4], const float mat[4][4])
unsigned int uint
struct bPoseChannel bPoseChannel
struct bPose bPose
struct Bone Bone
@ ARM_RESTPOS
struct bArmature bArmature
struct KeyBlock KeyBlock
struct Base Base
struct ListBase ListBase
struct Mesh Mesh
struct MDeformVert MDeformVert
@ OB_MESH
struct bDeformGroup bDeformGroup
struct Object Object
struct Scene Scene
BPy_StructRNA * depsgraph
static void sanitize(Matrix &matrix, int precision)
Definition BCMath.cpp:140
ControllerExporter(BlenderContext &blender_context, COLLADASW::StreamWriter *sw, BCExportSettings &export_settings)
bool add_instance_controller(Object *ob)
void operator()(Object *ob)
bool is_skinned_mesh(Object *ob)
void add_material_bindings(COLLADASW::BindMaterial &bind_material, Object *ob, bool active_uv_only)
static void mat4_to_dae(float out[4][4], float in[4][4])
static void mat4_to_dae_double(double out[4][4], float in[4][4])
std::string get_joint_sid(Bone *bone)
std::string translate_id(const char *idString)
std::string get_geometry_id(Object *ob)
std::string id_name(void *id)
bool bc_is_root_bone(Bone *aBone, bool deform_bones_only)
void bc_add_global_transform(Matrix &to_mat, const Matrix &from_mat, const BCMatrix &global_transform, const bool invert)
void bc_apply_global_transform(Matrix &to_mat, const BCMatrix &global_transform, const bool invert)
Object * bc_get_assigned_armature(Object *ob)
void bc_create_restpose_mat(BCExportSettings &export_settings, Bone *bone, float to_mat[4][4], float from_mat[4][4], bool use_local_space)
Mesh * bc_get_mesh_copy(BlenderContext &blender_context, Object *ob, BC_export_mesh_type export_mesh_type, bool apply_modifiers, bool triangulate)
void bc_decompose(float mat[4][4], float *loc, float eul[3], float quat[4], float *size)
bool bc_get_property_matrix(Bone *bone, const std::string &key, float mat[4][4])
constexpr int LIMITTED_PRECISION
#define rot(x, k)
#define input
#define this
MatBase< T, NumCol, NumRow > scale(const MatBase< T, NumCol, NumRow > &mat, const VectorT &scale)
char name[64]
float arm_mat[4][4]
ListBase childbase
void forEachMeshObjectInExportSet(Scene *sce, Functor &f, LinkNode *export_set)
int totkey
ListBase block
void * first
struct MDeformWeight * dw
unsigned int def_nr
int verts_num
struct bPose * pose
struct bDeformGroup * next
struct Bone * bone
i
Definition text_draw.cc:230
uint8_t flag
Definition wm_window.cc:139