Blender V4.5
ArmatureExporter.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 "COLLADASWInstanceController.h"
10
11#include "BKE_armature.hh"
12
13#include "ED_armature.hh"
14
15#include "BLI_listbase.h"
16#include "BLI_math_matrix.h"
17
18#include "ArmatureExporter.h"
19#include "SceneExporter.h"
20
21#include "collada_utils.h"
22
23void ArmatureExporter::add_bone_collections(Object *ob_arm, COLLADASW::Node &node)
24{
25 bArmature *armature = (bArmature *)ob_arm->data;
26
27 /* Because our importer assumes that "extras" tags have a unique name, it's not possible to
28 * export a `<bonecollection>` element per bone collection. This is why all the names are stored
29 * in one element, newline-separated. */
30
31 std::stringstream collection_stream;
32 std::stringstream visible_stream;
33 for (const BoneCollection *bcoll : armature->collections_span()) {
34 collection_stream << bcoll->name << "\n";
35
36 if (bcoll->flags & BONE_COLLECTION_VISIBLE) {
37 visible_stream << bcoll->name << "\n";
38 }
39 }
40
41 std::string collection_names = collection_stream.str();
42 if (collection_names.length() > 1) {
43 collection_names.pop_back(); /* Pop off the last `\n`. */
44 node.addExtraTechniqueParameter("blender", "collections", collection_names);
45 }
46
47 std::string visible_names = visible_stream.str();
48 if (visible_names.length() > 1) {
49 visible_names.pop_back(); /* Pop off the last `\n`. */
50 node.addExtraTechniqueParameter("blender", "visible_collections", visible_names);
51 }
52
53 if (armature->runtime.active_collection) {
54 node.addExtraTechniqueParameter(
55 "blender", "active_collection", std::string(armature->active_collection_name));
56 }
57}
58
60 ViewLayer *view_layer,
61 SceneExporter *se,
62 std::vector<Object *> &child_objects)
63
64{
65 /* write bone nodes */
66
67 bArmature *armature = (bArmature *)ob_arm->data;
68 bool is_edited = armature->edbo != nullptr;
69
70 if (!is_edited) {
71 ED_armature_to_edit(armature);
72 }
73
74 LISTBASE_FOREACH (Bone *, bone, &armature->bonebase) {
75 add_bone_node(bone, ob_arm, se, child_objects);
76 }
77
78 if (!is_edited) {
79 ED_armature_edit_free(armature);
80 }
81}
82
83void ArmatureExporter::write_bone_URLs(COLLADASW::InstanceController &ins,
84 Object *ob_arm,
85 Bone *bone)
86{
87 if (bc_is_root_bone(bone, this->export_settings.get_deform_bones_only())) {
88 std::string joint_id = translate_id(id_name(ob_arm) + "_" + bone->name);
89 ins.addSkeleton(COLLADABU::URI(COLLADABU::Utils::EMPTY_STRING, joint_id));
90 }
91 else {
92 LISTBASE_FOREACH (Bone *, child, &bone->childbase) {
93 write_bone_URLs(ins, ob_arm, child);
94 }
95 }
96}
97
99{
100 Object *ob_arm = bc_get_assigned_armature(ob);
101 bArmature *arm = (bArmature *)ob_arm->data;
102
103 const std::string &controller_id = get_controller_id(ob_arm, ob);
104
105 COLLADASW::InstanceController ins(mSW);
106 ins.setUrl(COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, controller_id));
107
108 Mesh *mesh = (Mesh *)ob->data;
109 if (mesh->deform_verts().is_empty()) {
110 return false;
111 }
112
113 /* write root bone URLs */
114 LISTBASE_FOREACH (Bone *, bone, &arm->bonebase) {
115 write_bone_URLs(ins, ob_arm, bone);
116 }
117
119 ins.getBindMaterial(), ob, this->export_settings.get_active_uv_only());
120
121 ins.add();
122 return true;
123}
124
125#if 0
126void ArmatureExporter::operator()(Object *ob)
127{
128 Object *ob_arm = bc_get_assigned_armature(ob);
129}
130
131bool ArmatureExporter::already_written(Object *ob_arm)
132{
133 return std::find(written_armatures.begin(), written_armatures.end(), ob_arm) !=
134 written_armatures.end();
135}
136
137void ArmatureExporter::wrote(Object *ob_arm)
138{
139 written_armatures.push_back(ob_arm);
140}
141
142void ArmatureExporter::find_objects_using_armature(Object *ob_arm,
143 std::vector<Object *> &objects,
144 Scene *sce)
145{
146 objects.clear();
147
148 Base *base = (Base *)sce->base.first;
149 while (base) {
150 Object *ob = base->object;
151
152 if (ob->type == OB_MESH && get_assigned_armature(ob) == ob_arm) {
153 objects.push_back(ob);
154 }
155
156 base = base->next;
157 }
158}
159#endif
160
161void ArmatureExporter::add_bone_node(Bone *bone,
162 Object *ob_arm,
163 SceneExporter *se,
164 std::vector<Object *> &child_objects)
165{
166 if (can_export(bone)) {
167 std::string node_id = translate_id(id_name(ob_arm) + "_" + bone->name);
168 std::string node_name = std::string(bone->name);
169 std::string node_sid = get_joint_sid(bone);
170
171 COLLADASW::Node node(mSW);
172
173 node.setType(COLLADASW::Node::JOINT);
174 node.setNodeId(node_id);
175 node.setNodeName(node_name);
176 node.setNodeSid(node_sid);
177
178 if (this->export_settings.get_use_blender_profile()) {
179 if (!is_export_root(bone)) {
180 if (bone->flag & BONE_CONNECTED) {
181 node.addExtraTechniqueParameter("blender", "connect", true);
182 }
183 }
184
185 std::string collection_names;
186 LISTBASE_FOREACH (const BoneCollectionReference *, bcoll_ref, &bone->runtime.collections) {
187 collection_names += std::string(bcoll_ref->bcoll->name) + "\n";
188 }
189 if (collection_names.length() > 1) {
190 collection_names.pop_back(); /* Pop off the last `\n`. */
191 node.addExtraTechniqueParameter("blender", "", collection_names, "", "collections");
192 }
193
194 bArmature *armature = (bArmature *)ob_arm->data;
195 EditBone *ebone = bc_get_edit_bone(armature, bone->name);
196 if (ebone && ebone->roll != 0) {
197 node.addExtraTechniqueParameter("blender", "roll", ebone->roll);
198 }
199 if (bc_is_leaf_bone(bone)) {
200 Vector head, tail;
201 const BCMatrix &global_transform = this->export_settings.get_global_transform();
202 if (this->export_settings.get_apply_global_orientation()) {
203 bc_add_global_transform(head, bone->arm_head, global_transform);
204 bc_add_global_transform(tail, bone->arm_tail, global_transform);
205 }
206 else {
207 copy_v3_v3(head, bone->arm_head);
208 copy_v3_v3(tail, bone->arm_tail);
209 }
210 node.addExtraTechniqueParameter("blender", "tip_x", tail[0] - head[0]);
211 node.addExtraTechniqueParameter("blender", "tip_y", tail[1] - head[1]);
212 node.addExtraTechniqueParameter("blender", "tip_z", tail[2] - head[2]);
213 }
214 }
215
216 node.start();
217
218 add_bone_transform(ob_arm, bone, node);
219
220 /* Write nodes of child-objects, remove written objects from list. */
221 std::vector<Object *>::iterator iter = child_objects.begin();
222
223 while (iter != child_objects.end()) {
224 Object *ob = *iter;
225 if (ob->partype == PARBONE && STREQ(ob->parsubstr, bone->name)) {
226 float backup_parinv[4][4];
227 copy_m4_m4(backup_parinv, ob->parentinv);
228
229 /* Crude, temporary change to parentinv
230 * so transform gets exported correctly. */
231
232 /* Add bone tail- translation... don't know why
233 * bone parenting is against the tail of a bone
234 * and not its head, seems arbitrary. */
235 ob->parentinv[3][1] += bone->length;
236
237 /* OPEN_SIM_COMPATIBILITY
238 * TODO: when such objects are animated as
239 * single matrix the tweak must be applied
240 * to the result. */
241 if (export_settings.get_open_sim()) {
242 /* Tweak objects parent-inverse to match compatibility. */
243 float temp[4][4];
244
245 copy_m4_m4(temp, bone->arm_mat);
246 temp[3][0] = temp[3][1] = temp[3][2] = 0.0f;
247
248 mul_m4_m4m4(ob->parentinv, temp, ob->parentinv);
249 }
250
251 se->writeNode(ob);
252 copy_m4_m4(ob->parentinv, backup_parinv);
253 iter = child_objects.erase(iter);
254 }
255 else {
256 iter++;
257 }
258 }
259
260 LISTBASE_FOREACH (Bone *, child, &bone->childbase) {
261 add_bone_node(child, ob_arm, se, child_objects);
262 }
263 node.end();
264 }
265 else {
266 LISTBASE_FOREACH (Bone *, child, &bone->childbase) {
267 add_bone_node(child, ob_arm, se, child_objects);
268 }
269 }
270}
271
272bool ArmatureExporter::is_export_root(Bone *bone)
273{
274 Bone *entry = bone->parent;
275 while (entry) {
276 if (can_export(entry)) {
277 return false;
278 }
279 entry = entry->parent;
280 }
281 return can_export(bone);
282}
283
284void ArmatureExporter::add_bone_transform(Object *ob_arm, Bone *bone, COLLADASW::Node &node)
285{
286 // bPoseChannel *pchan = BKE_pose_channel_find_name(ob_arm->pose, bone->name);
287
288 float mat[4][4];
289 float bone_rest_mat[4][4]; /* derived from bone->arm_mat */
290 float parent_rest_mat[4][4]; /* derived from bone->parent->arm_mat */
291
292 bool has_restmat = bc_get_property_matrix(bone, "rest_mat", mat);
293
294 if (!has_restmat) {
295
296 /* Have no rest-pose matrix stored, try old style <= Blender 2.78. */
297
298 bc_create_restpose_mat(this->export_settings, bone, bone_rest_mat, bone->arm_mat, true);
299
300 if (is_export_root(bone)) {
301 copy_m4_m4(mat, bone_rest_mat);
302 }
303 else {
304 Matrix parent_inverse;
306 this->export_settings, bone->parent, parent_rest_mat, bone->parent->arm_mat, true);
307
308 invert_m4_m4(parent_inverse, parent_rest_mat);
309 mul_m4_m4m4(mat, parent_inverse, bone_rest_mat);
310 }
311
312 /* OPEN_SIM_COMPATIBILITY */
313
314 if (export_settings.get_open_sim()) {
315 /* Remove rotations vs armature from transform
316 * parent_rest_rot * mat * irest_rot */
317 Matrix workmat;
318 copy_m4_m4(workmat, bone_rest_mat);
319
320 workmat[3][0] = workmat[3][1] = workmat[3][2] = 0.0f;
321 invert_m4(workmat);
322
323 mul_m4_m4m4(mat, mat, workmat);
324
325 if (!is_export_root(bone)) {
326 copy_m4_m4(workmat, parent_rest_mat);
327 workmat[3][0] = workmat[3][1] = workmat[3][2] = 0.0f;
328
329 mul_m4_m4m4(mat, workmat, mat);
330 }
331 }
332 }
333
334 if (this->export_settings.get_limit_precision()) {
336 }
337
338 TransformWriter::add_joint_transform(node, mat, nullptr, this->export_settings, has_restmat);
339}
340
341std::string ArmatureExporter::get_controller_id(Object *ob_arm, Object *ob)
342{
343 return translate_id(id_name(ob_arm)) + "_" + translate_id(id_name(ob)) +
344 SKIN_CONTROLLER_ID_SUFFIX;
345}
#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 copy_m4_m4(float m1[4][4], const float m2[4][4])
bool invert_m4_m4(float inverse[4][4], const float mat[4][4])
bool invert_m4(float mat[4][4])
MINLINE void copy_v3_v3(float r[3], const float a[3])
#define STREQ(a, b)
float[3] Vector
float[4][4] Matrix
struct BoneCollectionReference BoneCollectionReference
struct Bone Bone
@ BONE_COLLECTION_VISIBLE
@ BONE_CONNECTED
struct bArmature bArmature
struct Base Base
@ OB_MESH
struct Object Object
@ PARBONE
void ED_armature_edit_free(bArmature *arm)
void ED_armature_to_edit(bArmature *arm)
void add_bone_collections(Object *ob_arm, COLLADASW::Node &node)
bool add_instance_controller(Object *ob)
void add_armature_bones(Object *ob_arm, ViewLayer *view_layer, SceneExporter *se, std::vector< Object * > &child_objects)
static void sanitize(Matrix &matrix, int precision)
Definition BCMath.cpp:140
void add_material_bindings(COLLADASW::BindMaterial &bind_material, Object *ob, bool active_uv_only)
void add_joint_transform(COLLADASW::Node &node, float mat[4][4], float parent_mat[4][4], BCExportSettings &export_settings, bool has_restmat)
std::string get_joint_sid(Bone *bone)
std::string translate_id(const char *idString)
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)
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)
EditBone * bc_get_edit_bone(bArmature *armature, const char *name)
bool bc_get_property_matrix(Bone *bone, const std::string &key, float mat[4][4])
bool bc_is_leaf_bone(Bone *bone)
constexpr int LIMITTED_PRECISION
#define this
Bone_Runtime runtime
struct Bone * parent
float arm_head[3]
char name[64]
float arm_tail[3]
float arm_mat[4][4]
ListBase childbase
float parentinv[4][4]
char parsubstr[64]
struct BoneCollection * active_collection
char active_collection_name[64]
ListBase * edbo
struct bArmature_Runtime runtime