From d06eb49b2f282d56e983ffc019db347dafa8d3fe Mon Sep 17 00:00:00 2001 From: Arystanbek Dyussenov Date: Sat, 25 Jul 2009 16:10:17 +0000 Subject: [PATCH] COLLADA importer: basic armature import. Since in DAE only a matrix is stored per bone, some tricks have to be applied to build an armature from it: - direction for "leaf" bones is hard-coded to Y vector. This will be changed to bone matrix orientation, somehow code will need to derive the bone "forward" axis - for "leaf" bones length cannot be determined, currently it is hard-coded (second screenshot), but I think changing it to the average length of all non-leaf bones is ok - parent bones get their tail at last child's head, this is not good (first screenshot). For bones with more than one child Chingiz suggested not to link it with any child, I think it's a good idea. Length will be the same as for leaf bones. Screenshots: http://img232.imageshack.us/img232/8905/screenfhy.png http://img406.imageshack.us/img406/3004/tobaggan.png --- source/blender/collada/DocumentImporter.cpp | 267 ++++++++++++++++--- source/blender/editors/include/ED_armature.h | 1 + 2 files changed, 230 insertions(+), 38 deletions(-) diff --git a/source/blender/collada/DocumentImporter.cpp b/source/blender/collada/DocumentImporter.cpp index c113af136d1..efb0037ebce 100644 --- a/source/blender/collada/DocumentImporter.cpp +++ b/source/blender/collada/DocumentImporter.cpp @@ -34,11 +34,13 @@ // TODO move "extern C" into header files extern "C" { +#include "ED_keyframing.h" +#include "ED_armature.h" + #include "BKE_main.h" #include "BKE_customdata.h" #include "BKE_library.h" #include "BKE_texture.h" -#include "ED_keyframing.h" #include "BKE_fcurve.h" #include "BKE_depsgraph.h" #include "BLI_util.h" @@ -133,8 +135,6 @@ class Writer: public COLLADAFW::IWriter private: std::string mFilename; - std::vector mVisualScenes; - bContext *mContext; std::map uid_mesh_map; // geometry unique id-to-mesh map @@ -166,6 +166,25 @@ private: // Nodes don't share AnimationLists (Arystan) std::map uid_animated_map; // AnimationList->uniqueId to AnimatedObject map + // ---------------------------------------------------------------------- + // Armature and related + + // to build armature bones form inverse bind matrices + struct JointData { + float inv_bind_mat[4][4]; // joint inverse bind matrix + Object *ob_arm; // armature object + }; + std::map joint_index_to_joint_info_map; + std::map joint_id_to_joint_index_map; + + struct ArmatureData { + bArmature *arm; + COLLADAFW::SkinController *controller; + }; + std::map controller_id_to_arm_info_map; + + std::vector root_joints; + class UnitConverter { private: @@ -271,22 +290,6 @@ public: /** This method is called after the last write* method. No other methods will be called after this.*/ virtual void finish() { - // using mVisualScenes, do: - // - write data to Objects: materials, transforms, etc. - - // TODO: import materials ( inside ) and textures - - std::vector::iterator it = mVisualScenes.begin(); - for (; it != mVisualScenes.end(); it++) { - COLLADAFW::VisualScene &visscene = *it; - - // create new blender scene - - // create Objects from s inside this - - // link each Object with a Mesh - // for each Object's there should already exist a Mesh - } } /** When this method is called, the writer must write the global document asset. @@ -439,7 +442,14 @@ public: { // XXX linking object with the first , though a node may have more of them... // maybe join multiple meshes into 1, and link object with it? not sure... - if (node->getType() != COLLADAFW::Node::NODE) return; + if (node->getType() != COLLADAFW::Node::NODE) { + + if (node->getType() == COLLADAFW::Node::JOINT) { + root_joints.push_back(node); + } + + return; + } COLLADAFW::InstanceGeometryPointerArray &geom = node->getInstanceGeometries(); COLLADAFW::InstanceCameraPointerArray &camera = node->getInstanceCameras(); @@ -489,7 +499,7 @@ public: fprintf(stderr, "Cannot find armature by geometry uid. \n"); return; }*/ - COLLADAFW::InstanceGeometry *geom = (COLLADAFW::InstanceGeometry*)controller[0]; + COLLADAFW::InstanceController *geom = (COLLADAFW::InstanceController*)controller[0]; ob = create_mesh_object(ob, sce, node, geom, true); } // XXX - this is not supported yet @@ -590,6 +600,82 @@ public: } } + JointData *get_joint_data(COLLADAFW::Node *node) + { + const COLLADAFW::UniqueId& joint_id = node->getUniqueId(); + + if (joint_id_to_joint_index_map.find(joint_id) == joint_id_to_joint_index_map.end()) { + fprintf(stderr, "Cannot find a joint index by joint id for %s.\n", + node->getOriginalId().c_str()); + return NULL; + } + + int joint_index = joint_id_to_joint_index_map[joint_id]; + + return &joint_index_to_joint_info_map[joint_index]; + } + + void create_bone(COLLADAFW::Node *node, EditBone *parent, bArmature *arm) + { + JointData* jd = get_joint_data(node); + + if (jd) { + float mat[4][4]; + + // get original world-space matrix + Mat4Invert(mat, jd->inv_bind_mat); + + // TODO rename from Node "name" attrs later + EditBone *bone = addEditBone(arm, "Bone"); + + if (parent) bone->parent = parent; + + // set head + VecCopyf(bone->head, mat[3]); + + // set tail, can't set it to head because 0-length bones are not allowed + float vec[3] = {0.0f, 0.5f, 0.0f}; + VecAddf(bone->tail, bone->head, vec); + + // set parent tail + if (parent) + VecCopyf(parent->tail, bone->head); + + COLLADAFW::NodePointerArray& children = node->getChildNodes(); + for (int i = 0; i < children.getCount(); i++) { + create_bone(children[i], bone, arm); + } + } + } + + void create_bone_branch(COLLADAFW::Node *root) + { + JointData* jd = get_joint_data(root); + if (!jd) return; + + Object *ob_arm = jd->ob_arm; + + // enter armature edit mode + ED_armature_to_edit(ob_arm); + + COLLADAFW::NodePointerArray& children = root->getChildNodes(); + for (int i = 0; i < children.getCount(); i++) { + create_bone(children[i], NULL, (bArmature*)ob_arm->data); + } + + // exit armature edit mode + ED_armature_from_edit(CTX_data_scene(mContext), ob_arm); + } + + // here we add bones to armature, having armatures previously created in writeController + void build_armatures() + { + std::vector::iterator it; + for (it = root_joints.begin(); it != root_joints.end(); it++) { + create_bone_branch(*it); + } + } + /** When this method is called, the writer must write the entire visual scene. @return The writer should return true, if writing succeeded, false otherwise.*/ virtual bool writeVisualScene ( const COLLADAFW::VisualScene* visualScene ) @@ -610,16 +696,20 @@ public: for (int i = 0; i < visualScene->getRootNodes().getCount(); i++) { COLLADAFW::Node *node = visualScene->getRootNodes()[i]; - - if (node->getType() != COLLADAFW::Node::NODE) { - continue; + const COLLADAFW::Node::NodeType& type = node->getType(); + + if (type == COLLADAFW::Node::NODE) { + write_node(node, sce); } - - write_node(node, sce); + else if (type == COLLADAFW::Node::JOINT){ + root_joints.push_back(node); + } + } + + if (root_joints.size()) { + build_armatures(); } - mVisualScenes.push_back(*visualScene); - return true; } @@ -1478,29 +1568,130 @@ public: return true; } + + // TODO move it somewhere better place + void mat4_from_dae_mat4(float out[][4], const COLLADABU::Math::Matrix4& in) { + // in DAE, matrices use columns vectors, (see comments in COLLADABUMathMatrix4.h) + // so here, to make a blender matrix, we simply swap columns and rows + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + out[i][j] = in[j][i]; + } + } + } + /** When this method is called, the writer must write the skin controller data. @return The writer should return true, if writing succeeded, false otherwise.*/ - virtual bool writeSkinControllerData( const COLLADAFW::SkinControllerData* skinControllerData ) + virtual bool writeSkinControllerData( const COLLADAFW::SkinControllerData* skin ) { + // use inverse bind matrices to construct armature + // it is safe to invert them to get the original matrices + // because if they are inverse matrices, they can be inverted + + // just do like so: + // - create armature + // - enter editmode + // - add edit bones and head/tail properties using matrices and parent-child info + // - exit edit mode + + // store join inv bind matrix to use it later in armature construction + const COLLADAFW::Matrix4Array& inv_bind_mats = skin->getInverseBindMatrices(); + int i; + for (i = 0; i < skin->getJointsCount(); i++) { + JointData jd; + mat4_from_dae_mat4(jd.inv_bind_mat, inv_bind_mats[i]); + joint_index_to_joint_info_map[i] = jd; + } + + // see COLLADAFW::validate for an example of how to use SkinControllerData + /* what should I do here? + - create armature (should I create it here or somewhere else?) + - create bones + - create vertex group for each bone? + - create MDeformVerts? - no, I don't know what mesh to modify + is it possible to create MDeformVerts & vertex groups without assigning them to a mesh or object + - set weights + - map something(armature, vgoups, dverts) to skin controller uid, so I can use it in controller + */ + /*const std::string& skin_id = skinControllerData->getOriginalId(); + size_t num_bones = skinControllerData->getJointsCount(); + bArmature *arm = add_armature((char*)skin_id.c_str()); + if (!arm) { + fprintf(stderr, "Cannot create armature. \n"); + return true; + } + for (int i = 0; i < num_bones; i++) { + // create bone + //addEditBone(arm, "my_bone"); + Bone *bone = (Bone*)MEM_callocN(sizeof(Bone), "Bone"); + BLI_strncpy(bone->name, "bone", 32); + //unique_bone_name(arm, "my_bone"); + BLI_addtail(&arm->bonebase, bone); + + bone->flag |= BONE_TIPSEL; + bone->weight= 1.0f; + bone->dist= 0.25f; + bone->xwidth= 0.1f; + bone->zwidth= 0.1f; + bone->ease1= 1.0f; + bone->ease2= 1.0f; + bone->rad_head= 0.10f; + bone->rad_tail= 0.05f; + bone->segments= 1; + bone->layer= arm->layer; + // TODO: add inverse bind matrices + i++; + } + + this->uid_controller_map[skinControllerData->getUniqueId()] = arm; + */ return true; } - /** When this method is called, the writer must write the controller. - @return The writer should return true, if writing succeeded, false otherwise.*/ + // this is called on postprocess, before writeVisualScenes virtual bool writeController( const COLLADAFW::Controller* controller ) { + // here we: + // - create armature + // - create EditBones, not setting parent-child relationships + // - store armature + + Scene *sce = CTX_data_scene(mContext); + const COLLADAFW::UniqueId& skin_id = controller->getUniqueId(); - // if skin controller + if (controller->getControllerType() == COLLADAFW::Controller::CONTROLLER_TYPE_SKIN) { - - COLLADAFW::SkinController *skin = (COLLADAFW::SkinController*)controller; - const COLLADAFW::UniqueId& geom_uid = skin->getSource(); - this->skinid_meshid_map[skin_id] = geom_uid; + + Object *ob_arm = add_object(sce, OB_ARMATURE); + + COLLADAFW::SkinController *skinco = (COLLADAFW::SkinController*)controller; + const COLLADAFW::UniqueId& id = skinco->getSkinControllerData(); + + // "Node" ids + const COLLADAFW::UniqueIdArray& joint_ids = skinco->getJoints(); + + int i; + for (i = 0; i < joint_ids.getCount(); i++) { + + // store armature pointer + JointData& jd = joint_index_to_joint_info_map[i]; + jd.ob_arm = ob_arm; + + // now we'll be able to get inv bind matrix from joint id + joint_id_to_joint_index_map[joint_ids[i]] = i; + } + /*if (uid_controller_map.find(skin_id) == uid_controller_map.end()) { + fprintf(stderr, "Cannot find armature by UID.\n"); + return true; + }*/ + //bArmature *arm = uid_controller_map[skin_id]; + // map mesh to controller's uid + //const COLLADAFW::UniqueId& geom_uid = skin->getSource(); + //this->skinid_meshid_map[skin_id] = geom_uid; } - - // if morph controller + // morph controller else { } return true; diff --git a/source/blender/editors/include/ED_armature.h b/source/blender/editors/include/ED_armature.h index 8c19f2c3ecf..594258d812b 100644 --- a/source/blender/editors/include/ED_armature.h +++ b/source/blender/editors/include/ED_armature.h @@ -109,6 +109,7 @@ EditBone *ED_armature_bone_get_mirrored(struct ListBase *edbo, EditBone *ebo); / void ED_armature_sync_selection(struct ListBase *edbo); void add_primitive_bone(struct Scene *scene, struct View3D *v3d, struct RegionView3D *rv3d); +EditBone *addEditBone(struct bArmature *arm, char *name); /* used by COLLADA importer */ void transform_armature_mirror_update(struct Object *obedit); void clear_armature(struct Scene *scene, struct Object *ob, char mode);