Collada export: changes to joints/weights in skincontroller
authorJuha Mäki-Kanto <ih5235252@gmail.com>
Sat, 18 Feb 2012 16:20:24 +0000 (16:20 +0000)
committerJuha Mäki-Kanto <ih5235252@gmail.com>
Sat, 18 Feb 2012 16:20:24 +0000 (16:20 +0000)
- Collecting joints/weights in one place, it's easier to exclude zero weights or vertexgroups with no matching bone than trying to match same logic in multiple places.
- Still not exporting -1 joints for vertices without weights, but also not outputting -1 joint + weight for each vertexgroup without a matching bone.
- The exported weights are now normalized.

Last I tested this patch stopped 3ds Max crashing on import of file from #29465 (opencollada / internal .dae).

source/blender/collada/ArmatureExporter.cpp
source/blender/collada/ArmatureExporter.h

index 0e89f2db74bed49f21b836fa5c4856692f7bf6c3..277b1896397af7619a9313fd3d503e72e94a67e5 100644 (file)
@@ -296,10 +296,64 @@ void ArmatureExporter::export_controller(Object* ob, Object *ob_arm)
 
        std::string joints_source_id = add_joints_source(ob_arm, &ob->defbase, controller_id);
        std::string inv_bind_mat_source_id = add_inv_bind_mats_source(ob_arm, &ob->defbase, controller_id);
-       std::string weights_source_id = add_weights_source(me, controller_id);
 
+       std::list<int> vcounts;
+       std::list<int> joints;
+       std::list<float> weights;
+
+       {
+               int i, j;
+
+               // def group index -> joint index
+               std::vector<int> joint_index_by_def_index;
+               bDeformGroup *def;
+
+               for (def = (bDeformGroup*)ob->defbase.first, i = 0, j = 0; def; def = def->next, i++) {
+                       if (is_bone_defgroup(ob_arm, def))
+                               joint_index_by_def_index.push_back(j++);
+                       else
+                               joint_index_by_def_index.push_back(-1);
+               }
+
+               for (i = 0; i < me->totvert; i++) {
+                       MDeformVert *vert = &me->dvert[i];
+                       std::map<int, float> jw;
+
+                       // We're normalizing the weights later
+                       float sumw = 0.0f;
+
+                       for (j = 0; j < vert->totweight; j++) {
+                               int joint_index = joint_index_by_def_index[vert->dw[j].def_nr];
+                               if(joint_index != -1 && vert->dw[j].weight > 0.0f)
+                               {
+                                       jw[joint_index] += vert->dw[j].weight;
+                                       sumw += vert->dw[j].weight;
+                               }
+                       }
+
+                       if(sumw > 0.0f)
+                       {
+                               float invsumw = 1.0f/sumw;
+                               vcounts.push_back(jw.size());
+                               for(std::map<int, float>::iterator m = jw.begin(); m != jw.end(); ++m)
+                               {
+                                       joints.push_back((*m).first);
+                                       weights.push_back(invsumw*(*m).second);
+                               }
+                       }
+                       else
+                       {
+                               vcounts.push_back(0);
+                               /*vcounts.push_back(1);
+                               joints.push_back(-1);
+                               weights.push_back(1.0f);*/
+                       }
+               }
+       }
+
+       std::string weights_source_id = add_weights_source(me, controller_id, weights);
        add_joints_element(&ob->defbase, joints_source_id, inv_bind_mat_source_id);
-       add_vertex_weights_element(weights_source_id, joints_source_id, me, ob_arm, &ob->defbase);
+       add_vertex_weights_element(weights_source_id, joints_source_id, vcounts, joints);
 
        closeSkin();
        closeController();
@@ -445,21 +499,14 @@ bool ArmatureExporter::is_bone_defgroup(Object *ob_arm, bDeformGroup* def)
        return get_bone_from_defgroup(ob_arm, def) != NULL;
 }
 
-std::string ArmatureExporter::add_weights_source(Mesh *me, const std::string& controller_id)
+std::string ArmatureExporter::add_weights_source(Mesh *me, const std::string& controller_id, const std::list<float>& weights)
 {
        std::string source_id = controller_id + WEIGHTS_SOURCE_ID_SUFFIX;
 
-       int i;
-       int totweight = 0;
-
-       for (i = 0; i < me->totvert; i++) {
-               totweight += me->dvert[i].totweight;
-       }
-
        COLLADASW::FloatSourceF source(mSW);
        source.setId(source_id);
        source.setArrayId(source_id + ARRAY_ID_SUFFIX);
-       source.setAccessorCount(totweight);
+       source.setAccessorCount(weights.size());
        source.setAccessorStride(1);
        
        COLLADASW::SourceBase::ParameterNameList &param = source.getParameterNameList();
@@ -467,13 +514,8 @@ std::string ArmatureExporter::add_weights_source(Mesh *me, const std::string& co
 
        source.prepareToAppendValues();
 
-       // NOTE: COLLADA spec says weights should be normalized
-
-       for (i = 0; i < me->totvert; i++) {
-               MDeformVert *vert = &me->dvert[i];
-               for (int j = 0; j < vert->totweight; j++) {
-                       source.appendValues(vert->dw[j].weight);
-               }
+       for(std::list<float>::const_iterator i = weights.begin(); i != weights.end(); ++i) {
+               source.appendValues(*i);
        }
 
        source.finish();
@@ -481,11 +523,12 @@ std::string ArmatureExporter::add_weights_source(Mesh *me, const std::string& co
        return source_id;
 }
 
-void ArmatureExporter::add_vertex_weights_element(const std::string& weights_source_id, const std::string& joints_source_id, Mesh *me,
-                                                               Object *ob_arm, ListBase *defbase)
+void ArmatureExporter::add_vertex_weights_element(const std::string& weights_source_id, const std::string& joints_source_id,
+                                                                                                 const std::list<int>& vcounts,
+                                                                                                 const std::list<int>& joints)
 {
-       COLLADASW::VertexWeightsElement weights(mSW);
-       COLLADASW::InputList &input = weights.getInputList();
+       COLLADASW::VertexWeightsElement weightselem(mSW);
+       COLLADASW::InputList &input = weightselem.getInputList();
 
        int offset = 0;
        input.push_back(COLLADASW::Input(COLLADASW::InputSemantic::JOINT, // constant declared in COLLADASWInputList.h
@@ -493,40 +536,25 @@ void ArmatureExporter::add_vertex_weights_element(const std::string& weights_sou
        input.push_back(COLLADASW::Input(COLLADASW::InputSemantic::WEIGHT,
                                                                         COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, weights_source_id), offset++));
 
-       weights.setCount(me->totvert);
+       weightselem.setCount(vcounts.size());
 
        // write number of deformers per vertex
-       COLLADASW::PrimitivesBase::VCountList vcount;
-       int i;
-       for (i = 0; i < me->totvert; i++) {
-               vcount.push_back(me->dvert[i].totweight);
-       }
+       COLLADASW::PrimitivesBase::VCountList vcountlist;
 
-       weights.prepareToAppendVCountValues();
-       weights.appendVertexCount(vcount);
+       vcountlist.resize(vcounts.size());
+       std::copy(vcounts.begin(), vcounts.end(), vcountlist.begin());
 
-       // def group index -> joint index
-       std::map<int, int> joint_index_by_def_index;
-       bDeformGroup *def;
-       int j;
-       for (def = (bDeformGroup*)defbase->first, i = 0, j = 0; def; def = def->next, i++) {
-               if (is_bone_defgroup(ob_arm, def))
-                       joint_index_by_def_index[i] = j++;
-               else
-                       joint_index_by_def_index[i] = -1;
-       }
+       weightselem.prepareToAppendVCountValues();
+       weightselem.appendVertexCount(vcountlist);
 
-       weights.CloseVCountAndOpenVElement();
+       weightselem.CloseVCountAndOpenVElement();
 
        // write deformer index - weight index pairs
        int weight_index = 0;
-       for (i = 0; i < me->totvert; i++) {
-               MDeformVert *dvert = &me->dvert[i];
-               for (int j = 0; j < dvert->totweight; j++) {
-                       weights.appendValues(joint_index_by_def_index[dvert->dw[j].def_nr]);
-                       weights.appendValues(weight_index++);
-               }
+       for(std::list<int>::const_iterator i = joints.begin(); i != joints.end(); ++i)
+       {
+               weightselem.appendValues(*i, weight_index++);
        }
 
-       weights.finish();
+       weightselem.finish();
 }
index 925e65c9b69fa3b8752d56bb904899dd33d07029..9d64664fa63ff03213d7e233ccf78b321a02af4e 100644 (file)
@@ -111,10 +111,11 @@ private:
 
        bool is_bone_defgroup(Object *ob_arm, bDeformGroup* def);
 
-       std::string add_weights_source(Mesh *me, const std::string& controller_id);
+       std::string add_weights_source(Mesh *me, const std::string& controller_id,
+                                                                  const std::list<float>& weights);
 
-       void add_vertex_weights_element(const std::string& weights_source_id, const std::string& joints_source_id, Mesh *me,
-                                                                       Object *ob_arm, ListBase *defbase);
+       void add_vertex_weights_element(const std::string& weights_source_id, const std::string& joints_source_id,
+                                                                       const std::list<int>& vcount, const std::list<int>& joints);
 };
 
 #endif