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