From 8d42c2f3ea4b37c6a538b2b01e4d00f98ca96100 Mon Sep 17 00:00:00 2001 From: Gaia Clary Date: Sat, 29 Nov 2014 13:24:26 +0100 Subject: Improved Collada Armature Import Collada does not support Bone length. It has only Joints. This patch tries to improve the necessary "guesswork" for importing Rigs. Differential Revision: https://developer.blender.org/D920 --- source/blender/collada/ArmatureImporter.cpp | 331 +++++++++++++++++++++------- source/blender/collada/ArmatureImporter.h | 44 +++- source/blender/collada/DocumentImporter.cpp | 2 +- source/blender/collada/ImportSettings.h | 3 +- source/blender/collada/collada.cpp | 6 +- source/blender/collada/collada.h | 4 +- source/blender/editors/io/io_collada.c | 42 +++- 7 files changed, 328 insertions(+), 104 deletions(-) (limited to 'source') diff --git a/source/blender/collada/ArmatureImporter.cpp b/source/blender/collada/ArmatureImporter.cpp index 033145b33d1..db24bf6d0cf 100644 --- a/source/blender/collada/ArmatureImporter.cpp +++ b/source/blender/collada/ArmatureImporter.cpp @@ -37,6 +37,7 @@ #include "BKE_object.h" #include "BKE_armature.h" #include "BLI_string.h" +#include "BLI_listbase.h" #include "ED_armature.h" #include "ArmatureImporter.h" @@ -49,7 +50,23 @@ static const char *bc_get_joint_name(T *node) return id.size() ? id.c_str() : node->getOriginalId().c_str(); } -ArmatureImporter::ArmatureImporter(UnitConverter *conv, MeshImporterBase *mesh, Scene *sce) : +static EditBone *get_edit_bone(bArmature * armature, char *name) { + EditBone *eBone; + + eBone = (EditBone *)armature->edbo->first; + for (eBone; eBone; eBone = eBone->next) { + if (!strcmp(name, eBone->name)) + return eBone; + } + + return NULL; + +} + + + +ArmatureImporter::ArmatureImporter(UnitConverter *conv, MeshImporterBase *mesh, Scene *sce, const ImportSettings *import_settings) : + import_settings(import_settings), unit_converter(conv), TransformReader(conv), scene(sce), @@ -57,6 +74,15 @@ ArmatureImporter::ArmatureImporter(UnitConverter *conv, MeshImporterBase *mesh, mesh_importer(mesh) { } +void ArmatureImporter::clear_extended_boneset() +{ + for (std::map::iterator it = extended_bones.begin(); it != extended_bones.end(); ++it) { + if (it->second != NULL) + delete it->second; + } + extended_bones.clear(); +} + ArmatureImporter::~ArmatureImporter() { // free skin controller data if we forget to do this earlier @@ -64,6 +90,7 @@ ArmatureImporter::~ArmatureImporter() for (it = skin_by_data_uid.begin(); it != skin_by_data_uid.end(); it++) { it->second.free(); } + clear_extended_boneset(); } #if 0 @@ -83,16 +110,17 @@ JointData *ArmatureImporter::get_joint_data(COLLADAFW::Node *node); } #endif -void ArmatureImporter::create_bone(SkinInfo *skin, COLLADAFW::Node *node, EditBone *parent, int totchild, +int ArmatureImporter::create_bone(SkinInfo *skin, COLLADAFW::Node *node, EditBone *parent, int totchild, float parent_mat[4][4], bArmature *arm) { float mat[4][4]; float joint_inv_bind_mat[4][4]; + int chain_length = 0; //Checking if bone is already made. std::vector::iterator it; it = std::find(finished_joints.begin(), finished_joints.end(), node); - if (it != finished_joints.end()) return; + if (it != finished_joints.end()) return chain_length; // JointData* jd = get_joint_data(node); @@ -143,96 +171,150 @@ void ArmatureImporter::create_bone(SkinInfo *skin, COLLADAFW::Node *node, EditBo // set tail, don't set it to head because 0-length bones are not allowed add_v3_v3v3(bone->tail, bone->head, vec); - // set parent tail + /* find smallest bone length in armature (used later for leaf bone length) */ if (parent) { - // XXX increase this to prevent "very" small bones? - const float epsilon = 0.000001f; - - // derive leaf bone length + /* guess reasonable leaf bone length */ float length = len_v3v3(parent->head, bone->head); - if ((length < leaf_bone_length || totbone == 0) && length > epsilon) { + if ((length < leaf_bone_length || totbone == 0) && length > MINIMUM_BONE_LENGTH) { leaf_bone_length = length; } - - if (totchild == 1) { - copy_v3_v3(parent->tail, bone->head); - - // not setting BONE_CONNECTED because this would lock child bone location with respect to parent - bone->flag |= BONE_CONNECTED; - - - // treat zero-sized bone like a leaf bone - if (length <= epsilon) { - add_leaf_bone(parent_mat, parent, node); - } - } - } COLLADAFW::NodePointerArray& children = node->getChildNodes(); + + BoneExtended &be = add_bone_extended(bone, node); + be.set_leaf_bone(true); + for (unsigned int i = 0; i < children.getCount(); i++) { - create_bone(skin, children[i], bone, children.getCount(), mat, arm); + int cl = create_bone(skin, children[i], bone, children.getCount(), mat, arm); + if (cl > chain_length) + chain_length = cl; } - // in second case it's not a leaf bone, but we handle it the same way - if (!children.getCount() || children.getCount() > 1) { - add_leaf_bone(mat, bone, node); - } bone->length = len_v3v3(bone->head, bone->tail); joint_by_uid[node->getUniqueId()] = node; finished_joints.push_back(node); + + be.set_chain_length(chain_length + 1); + + return chain_length + 1; } -void ArmatureImporter::add_leaf_bone(float mat[4][4], EditBone *bone, COLLADAFW::Node *node) +/** + * Collada only knows Joints, hence bones at the end of a bone chain + * don't have a defined length. This function guesses reasonable + * tail locations for the affected bones (nodes which don't have any connected child) + * Hint: The extended_bones set gets populated in ArmatureImporter::create_bone +**/ +void ArmatureImporter::fix_bone_orientation(bArmature *armature, Bone *bone) { - LeafBone leaf; + /* armature has no bones */ + if (bone == NULL) + return; - leaf.bone = bone; - copy_m4_m4(leaf.mat, mat); - BLI_strncpy(leaf.name, bone->name, sizeof(leaf.name)); - - TagsMap::iterator etit; - ExtraTags *et = 0; - etit = uid_tags_map.find(node->getUniqueId().toAscii()); - if (etit != uid_tags_map.end()) { - et = etit->second; - //else return; + BoneExtended *be = extended_bones[bone->name]; + if (be != NULL && be->is_leaf_bone() ) { + /* Collada only knows Joints, Here we guess a reasonable leaf bone length */ + float leaf_length = (leaf_bone_length == FLT_MAX) ? 1.0 : leaf_bone_length; - float x, y, z; - et->setData("tip_x", &x); - et->setData("tip_y", &y); - et->setData("tip_z", &z); - float vec[3] = {x, y, z}; - copy_v3_v3(leaf.bone->tail, leaf.bone->head); - add_v3_v3v3(leaf.bone->tail, leaf.bone->head, vec); - } - else { - leaf_bones.push_back(leaf); + EditBone *ebone = get_edit_bone(armature, bone->name); + + float vec[3]; + if (ebone->parent != NULL) { + EditBone *parent = ebone->parent; + sub_v3_v3v3(vec, ebone->head, parent->tail); + if (len_squared_v3(vec) < MINIMUM_BONE_LENGTH) + { + sub_v3_v3v3(vec, parent->tail, parent->head); + } + } + else { + vec[2] = 0.1f; + sub_v3_v3v3(vec, ebone->tail, ebone->head); + } + normalize_v3_v3(vec, vec); + mul_v3_fl(vec, leaf_length); + add_v3_v3v3(ebone->tail, ebone->head, vec); } -} -void ArmatureImporter::fix_leaf_bones( ) -{ - // Collada only knows Joints, Here we guess a reasonable - // leaf bone length - float leaf_length = (leaf_bone_length == FLT_MAX) ? 1.0:leaf_bone_length; + for (Bone *child = (Bone *)bone->childbase.first; child; child = child->next) { + fix_bone_orientation(armature, child); + } - // just setting tail for leaf bones here - std::vector::iterator it; - for (it = leaf_bones.begin(); it != leaf_bones.end(); it++) { +} - LeafBone& leaf = *it; - // pointing up - float vec[3] = {0.0f, 0.0f, 0.1f}; - - sub_v3_v3v3(vec, leaf.bone->tail , leaf.bone->head); - mul_v3_fl(vec, leaf_length); - add_v3_v3v3(leaf.bone->tail, leaf.bone->head , vec); +void ArmatureImporter::connect_bone_chains(bArmature *armature, Bone *parentbone, int clip) +{ + BoneExtended *dominant_child = NULL; + int maxlen = 0; + Bone *child = (Bone *)parentbone->childbase.first; + if (child && (import_settings->find_chains || child->next==NULL) ) + { + for (child; child; child = child->next) { + BoneExtended *be = extended_bones[child->name]; + if (be != NULL) { + if (be->get_chain_length() <= clip) { + if (be->get_chain_length() > maxlen) { + dominant_child = be; + maxlen = be->get_chain_length(); + } + else if (be->get_chain_length() == maxlen) { + dominant_child = NULL; + } + } + } + } + } + BoneExtended *pbe = extended_bones[parentbone->name]; + if (dominant_child != NULL) { + /* Found a valid chain. Now connect current bone with that chain.*/ + EditBone *pebone = get_edit_bone(armature, parentbone->name); + EditBone *cebone = get_edit_bone(armature, dominant_child->get_name()); + if (pebone && !(cebone->flag & BONE_CONNECTED)) { + + float vec[3]; + sub_v3_v3v3(vec, cebone->head, pebone->head); + + /* + * It is possible that the child's head is located on the parents head. + * When this happens, then moving the parent's tail to the child's head + * would result in a zero sized bone and Blender would silently remove the bone. + * So we move the tail only when the resulting bone has a minimum length: + */ + + if (len_squared_v3(vec) > MINIMUM_BONE_LENGTH) + { + pebone->tail[0] = cebone->head[0]; + pebone->tail[1] = cebone->head[1]; + pebone->tail[2] = cebone->head[2]; + + if (pbe && pbe->get_chain_length() >= this->import_settings->min_chain_length) { + cebone->flag |= BONE_CONNECTED; + printf("Connecting chain: parent %s --> %s (child)\n", pebone->name, cebone->name); + pbe->set_leaf_bone(false); + } + } + } + for (Bone *child = (Bone *)parentbone->childbase.first; child; child = child->next) { + ArmatureImporter::connect_bone_chains(armature, child, UNLIMITED_CHAIN_MAX); + } + } + else if (maxlen>1 && maxlen > this->import_settings->min_chain_length) { + /* Try again with smaller chain length */ + ArmatureImporter::connect_bone_chains(armature, parentbone, maxlen - 1); + } + else { + /* can't connect this Bone. Proceed with children ... */ + if (pbe) pbe->set_leaf_bone(true); + for (Bone *child = (Bone *)parentbone->childbase.first; child; child = child->next) { + ArmatureImporter::connect_bone_chains(armature, child, UNLIMITED_CHAIN_MAX); + } } + } #if 0 @@ -351,21 +433,26 @@ void ArmatureImporter::create_armature_bones( ) continue; } + clear_extended_boneset(); + ED_armature_to_edit(armature); create_bone(NULL, *ri , NULL, (*ri)->getChildNodes().getCount(), NULL, armature); - //leaf bone tails are derived from the matrix, so no need of this. - fix_leaf_bones(); + /* exit armature edit mode to populate the Armature object */ + ED_armature_from_edit(armature); + ED_armature_edit_free(armature); + + /* and step back to edit mode to fix the leaf nodes */ + ED_armature_to_edit(armature); + + connect_bone_chains(armature, (Bone *)armature->bonebase.first, UNLIMITED_CHAIN_MAX); + fix_bone_orientation(armature, (Bone *)armature->bonebase.first); // exit armature edit mode unskinned_armature_map[(*ri)->getUniqueId()] = ob_arm; ED_armature_from_edit(armature); - - //This serves no purpose, as pose is automatically reset later, in BKE_where_is_bone() - //set_pose(ob_arm, *ri, NULL, NULL); - ED_armature_edit_free(armature); DAG_id_tag_update(&ob_arm->id, OB_RECALC_OB | OB_RECALC_DATA); } @@ -459,9 +546,11 @@ void ArmatureImporter::create_armature_bones(SkinInfo& skin) } // enter armature edit mode - ED_armature_to_edit((bArmature *)ob_arm->data); + bArmature * armature = (bArmature *)ob_arm->data; + ED_armature_to_edit(armature); + + clear_extended_boneset(); - leaf_bones.clear(); totbone = 0; // bone_direction_row = 1; // TODO: don't default to Y but use asset and based on it decide on default row leaf_bone_length = FLT_MAX; @@ -480,20 +569,27 @@ void ArmatureImporter::create_armature_bones(SkinInfo& skin) // since root_joints may contain joints for multiple controllers, we need to filter if (skin.uses_joint_or_descendant(*ri)) { - create_bone(&skin, *ri, NULL, (*ri)->getChildNodes().getCount(), NULL, (bArmature *)ob_arm->data); + create_bone(&skin, *ri, NULL, (*ri)->getChildNodes().getCount(), NULL, armature); if (joint_parent_map.find((*ri)->getUniqueId()) != joint_parent_map.end() && !skin.get_parent()) skin.set_parent(joint_parent_map[(*ri)->getUniqueId()]); } } - fix_leaf_bones(); + /* exit armature edit mode to populate the Armature object */ + ED_armature_from_edit(armature); + ED_armature_edit_free(armature); + + /* and step back to edit mode to fix the leaf nodes */ + ED_armature_to_edit(armature); + + connect_bone_chains(armature, (Bone *)armature->bonebase.first, UNLIMITED_CHAIN_MAX); + fix_bone_orientation(armature, (Bone *)armature->bonebase.first); // exit armature edit mode - ED_armature_from_edit((bArmature *)ob_arm->data); - ED_armature_edit_free((bArmature *)ob_arm->data); + ED_armature_from_edit(armature); + ED_armature_edit_free(armature); DAG_id_tag_update(&ob_arm->id, OB_RECALC_OB | OB_RECALC_DATA); - } void ArmatureImporter::set_pose(Object *ob_arm, COLLADAFW::Node *root_node, const char *parentname, float parent_mat[4][4]) @@ -538,10 +634,11 @@ void ArmatureImporter::set_pose(Object *ob_arm, COLLADAFW::Node *root_node, con } - -// root - if this joint is the top joint in hierarchy, if a joint -// is a child of a node (not joint), root should be true since -// this is where we build armature bones from +/** + * root - if this joint is the top joint in hierarchy, if a joint + * is a child of a node (not joint), root should be true since + * this is where we build armature bones from + **/ void ArmatureImporter::add_root_joint(COLLADAFW::Node *node, Object *parent) { root_joints.push_back(node); @@ -785,3 +882,71 @@ bool ArmatureImporter::get_joint_bind_mat(float m[4][4], COLLADAFW::Node *joint) return found; } + + +/** + * BoneExtended is a helper class needed for the Bone chain finder + * See ArmatureImporter::fix_bone_orientation() + * and ArmatureImporter::connect_bone_chains() + **/ + +BoneExtended::BoneExtended(EditBone *aBone) +{ + this->set_name(aBone->name); + this->chain_length = 0; + this->is_leaf = false; +} + +char *BoneExtended::get_name() +{ + return name; +} + +void BoneExtended::set_name(char *aName) +{ + BLI_strncpy(name, aName, MAXBONENAME); +} + +int BoneExtended::get_chain_length() +{ + return chain_length; +} + +void BoneExtended::set_chain_length(const int aLength) +{ + chain_length = aLength; +} + + +void BoneExtended::set_leaf_bone(bool state) +{ + is_leaf = state; +} + +bool BoneExtended::is_leaf_bone() +{ + return is_leaf; +} + +BoneExtended &ArmatureImporter::add_bone_extended(EditBone *bone, COLLADAFW::Node *node) +{ + + TagsMap::iterator etit; + ExtraTags *et = 0; + etit = uid_tags_map.find(node->getUniqueId().toAscii()); + if (etit != uid_tags_map.end()) { + float x, y, z; + + et = etit->second; + et->setData("tip_x", &x); + et->setData("tip_y", &y); + et->setData("tip_z", &z); + float vec[3] = { x, y, z }; + copy_v3_v3(bone->tail, bone->head); + add_v3_v3v3(bone->tail, bone->head, vec); + } + + BoneExtended *be = new BoneExtended(bone); + extended_bones[bone->name] = be; + return *be; +} diff --git a/source/blender/collada/ArmatureImporter.h b/source/blender/collada/ArmatureImporter.h index beeac85cc4d..e09906e6c92 100644 --- a/source/blender/collada/ArmatureImporter.h +++ b/source/blender/collada/ArmatureImporter.h @@ -54,23 +54,41 @@ extern "C" { #include "collada_internal.h" #include "collada_utils.h" +#include "ImportSettings.h" + +#define UNLIMITED_CHAIN_MAX INT_MAX +#define MINIMUM_BONE_LENGTH 0.000001f + +class BoneExtended { + +private: + char name[MAXBONENAME]; + int chain_length; + bool is_leaf; + +public: + + BoneExtended(EditBone *aBone); + char *get_name(); + int get_chain_length(); + + void set_name(char *aName); + void set_chain_length(const int aLength); + void set_leaf_bone(bool state); + bool is_leaf_bone(); +}; class ArmatureImporter : private TransformReader { private: Scene *scene; UnitConverter *unit_converter; + const ImportSettings *import_settings; // std::map joint_index_to_joint_info_map; // std::map joint_id_to_joint_index_map; - struct LeafBone { - // COLLADAFW::Node *node; - EditBone *bone; - char name[32]; - float mat[4][4]; // bone matrix, derived from inv_bind_mat - }; - std::vector leaf_bones; + std::map extended_bones; // int bone_direction_row; // XXX not used float leaf_bone_length; int totbone; @@ -106,13 +124,15 @@ private: JointData *get_joint_data(COLLADAFW::Node *node); #endif - void create_bone(SkinInfo* skin, COLLADAFW::Node *node, EditBone *parent, int totchild, + int create_bone(SkinInfo* skin, COLLADAFW::Node *node, EditBone *parent, int totchild, float parent_mat[4][4], bArmature *arm); - void add_leaf_bone(float mat[4][4], EditBone *bone, COLLADAFW::Node * node); + BoneExtended &add_bone_extended(EditBone *bone, COLLADAFW::Node * node); + void clear_extended_boneset(); + + void fix_bone_orientation(bArmature *armature, Bone *bone); + void connect_bone_chains(bArmature *armature, Bone *bone, const int max_chain_length); - void fix_leaf_bones(); - void set_pose( Object *ob_arm, COLLADAFW::Node *root_node, const char *parentname, float parent_mat[4][4]); @@ -137,7 +157,7 @@ private: TagsMap uid_tags_map; public: - ArmatureImporter(UnitConverter *conv, MeshImporterBase *mesh, Scene *sce); + ArmatureImporter(UnitConverter *conv, MeshImporterBase *mesh, Scene *sce, const ImportSettings *import_settings); ~ArmatureImporter(); void add_root_joint(COLLADAFW::Node *node, Object *parent); diff --git a/source/blender/collada/DocumentImporter.cpp b/source/blender/collada/DocumentImporter.cpp index 8101e579098..20ef45b6b71 100644 --- a/source/blender/collada/DocumentImporter.cpp +++ b/source/blender/collada/DocumentImporter.cpp @@ -104,7 +104,7 @@ DocumentImporter::DocumentImporter(bContext *C, const ImportSettings *import_set import_settings(import_settings), mImportStage(General), mContext(C), - armature_importer(&unit_converter, &mesh_importer, CTX_data_scene(C)), + armature_importer(&unit_converter, &mesh_importer, CTX_data_scene(C), import_settings), mesh_importer(&unit_converter, &armature_importer, CTX_data_scene(C)), anim_importer(&unit_converter, &armature_importer, CTX_data_scene(C)) { diff --git a/source/blender/collada/ImportSettings.h b/source/blender/collada/ImportSettings.h index 3f3a9fb354e..f2185b13c50 100644 --- a/source/blender/collada/ImportSettings.h +++ b/source/blender/collada/ImportSettings.h @@ -32,7 +32,8 @@ struct ImportSettings { public: bool import_units; - + bool find_chains; + int min_chain_length; char *filepath; }; diff --git a/source/blender/collada/collada.cpp b/source/blender/collada/collada.cpp index ffbbb8623ac..826d634b8f1 100644 --- a/source/blender/collada/collada.cpp +++ b/source/blender/collada/collada.cpp @@ -44,13 +44,17 @@ extern "C" int collada_import(bContext *C, const char *filepath, - int import_units) + int import_units, + int find_chains, + int min_chain_length) { ImportSettings import_settings; import_settings.filepath = (char *)filepath; import_settings.import_units = import_units != 0; + import_settings.find_chains = find_chains != 0; + import_settings.min_chain_length = min_chain_length; DocumentImporter imp(C, &import_settings); if (imp.import()) return 1; diff --git a/source/blender/collada/collada.h b/source/blender/collada/collada.h index 524c704cdee..0a2713b1db5 100644 --- a/source/blender/collada/collada.h +++ b/source/blender/collada/collada.h @@ -55,7 +55,9 @@ struct Scene; */ int collada_import(struct bContext *C, const char *filepath, - int import_units); + int import_units, + int find_chains, + int min_chain_length); int collada_export(struct Scene *sce, const char *filepath, diff --git a/source/blender/editors/io/io_collada.c b/source/blender/editors/io/io_collada.c index bbf4447dd72..b45f3ccde2a 100644 --- a/source/blender/editors/io/io_collada.c +++ b/source/blender/editors/io/io_collada.c @@ -365,6 +365,8 @@ static int wm_collada_import_exec(bContext *C, wmOperator *op) { char filename[FILE_MAX]; int import_units; + int find_chains; + int min_chain_length; if (!RNA_struct_property_is_set(op->ptr, "filepath")) { BKE_report(op->reports, RPT_ERROR, "No filename given"); @@ -372,10 +374,15 @@ static int wm_collada_import_exec(bContext *C, wmOperator *op) } /* Options panel */ - import_units = RNA_boolean_get(op->ptr, "import_units"); + import_units = RNA_boolean_get(op->ptr, "import_units"); + find_chains = RNA_boolean_get(op->ptr, "find_chains"); + min_chain_length = RNA_int_get(op->ptr, "min_chain_length"); RNA_string_get(op->ptr, "filepath", filename); - if (collada_import(C, filename, import_units)) { + if (collada_import(C, filename, + import_units, + find_chains, + min_chain_length)) { return OPERATOR_FINISHED; } else { @@ -395,6 +402,17 @@ static void uiCollada_importSettings(uiLayout *layout, PointerRNA *imfptr) row = uiLayoutRow(box, false); uiItemR(row, imfptr, "import_units", 0, NULL, ICON_NONE); + + box = uiLayoutBox(layout); + row = uiLayoutRow(box, false); + uiItemL(row, IFACE_("Armature Options:"), ICON_MESH_DATA); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "find_chains", 0, NULL, ICON_NONE); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "min_chain_length", 0, NULL, ICON_NONE); + } static void wm_collada_import_draw(bContext *UNUSED(C), wmOperator *op) @@ -423,9 +441,23 @@ void WM_OT_collada_import(wmOperatorType *ot) WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY); RNA_def_boolean(ot->srna, - "import_units", 0, "Import Units", - "If disabled match import to Blender's current Unit settings, " - "otherwise use the settings from the Imported scene"); + "import_units", 0, "Import Units", + "If disabled match import to Blender's current Unit settings, " + "otherwise use the settings from the Imported scene"); + + RNA_def_boolean(ot->srna, + "find_chains", 0, "Find Bone Chains", + "Find best matching Bone Chains and ensure bones in chain are connected"); + + RNA_def_int(ot->srna, + "min_chain_length", + 0, + 0, + INT_MAX, + "Minimum Chain Length", + "When searching Bone Chains disregard chains of length below this value", + 0, + INT_MAX); } #endif -- cgit v1.2.3