static void mergeBones( BoneList &a, BoneList& b ) {
   for (size_t i=0; i<b.size(); ++i) {
      int c = b[i];
      BoneList::iterator it = find(a.begin(), a.end(), c);
      if ( a.end() == it ) {
         a.push_back( c );
      }
   }
}
void ColoredPolygon::BindToBone(BoneAnimated *bone)
{
    BoneList bones;
    bones.push_back(bone);
    bone->GetBoneList(bones);
    for (uint j = 0; j < bones.size(); ++j)
    {
        bones[j]->FixMatrix();
    }

    float signSq = Math::SignedSquare(_dots);

    std::vector<uint> skipped;

    for (uint i = 0; i < _dots.size(); ++i)
    {
        FPoint &a = _dots[(i + _dots.size() - 1) % _dots.size()].pos;
        FPoint &b = _dots[i].pos;
        FPoint &c = _dots[(i + 1) % _dots.size()].pos;

        std::vector<std::pair<BoneAnimated *, float> > boneDistance;
        for (uint j = 0; j < bones.size(); ++j)
        {
            BoneAnimated *d = bones[j];

            FPoint s(0, 0);
            FPoint e(d->GetLength(), 0);
            d->GetMatrix().Mul(s);
            d->GetMatrix().Mul(e);
            FPoint center = 0.5f * (s + e);

            if (Math::VMul(a - center, b - center) * signSq >= 0.f || Math::VMul(b - center, c - center) * signSq >= 0.f)
            {
                if ((d->GetParent() == NULL && (s - b).Length() < d->GetLength() / 2)
                        || (!d->HasChild() && (e - b).Length() < d->GetLength() / 2))
                {
                    boneDistance.push_back(
                        std::make_pair<BoneAnimated *, float>
                        (d, 0.f)
                    );
                }
                else
                {
                    boneDistance.push_back(std::make_pair<BoneAnimated *, float>(d, Math::Distance(s, e, b) ) );
                }
            }
        }

        if (boneDistance.size() == 1)
        {
            _dots[i].p[0].boneName = boneDistance.back().first->GetName();
            _dots[i].p[0].bone = boneDistance.back().first;
            _dots[i].p[0].mass = 1.f;

            _dots[i].p[1].boneName = "";
            _dots[i].p[1].bone = NULL;
            _dots[i].p[1].mass = 0.f;
        }
        else if (boneDistance.size() >= 2)
        {
            std::sort(boneDistance.begin(), boneDistance.end(), CmpBoneDistance);
            float m = boneDistance[0].second + boneDistance[1].second;

            _dots[i].p[0].boneName = boneDistance[0].first->GetName();
            _dots[i].p[0].bone = boneDistance[0].first;
            _dots[i].p[0].mass = boneDistance[1].second / m;

            _dots[i].p[1].boneName = boneDistance[1].first->GetName();
            _dots[i].p[1].bone = boneDistance[1].first;
            _dots[i].p[1].mass = boneDistance[0].second / m;
        }
        else
        {
            skipped.push_back(i);
        }
    }

    if (skipped.size())
    {
        assert(false);// fail
    }
}
void ColoredPolygon::DebugDraw(bool onlyControl)
{
    if (!onlyControl)
    {
        Draw();
    }

    _debugDraw = true;

    Render::SetFiltering(false);
    Render::PushMatrix();

    for (unsigned int i = 0; i < _triangles.GetVB().SizeIndex(); i += 3)
    {
        Render::Line(_triangles.GetVB().VertXY(_triangles.GetVB().Index(i + 0)).x, _triangles.GetVB().VertXY(_triangles.GetVB().Index(i + 0)).y
                     , _triangles.GetVB().VertXY(_triangles.GetVB().Index(i + 1)).x, _triangles.GetVB().VertXY(_triangles.GetVB().Index(i + 1)).y, 0x4FFFFFFF);
        Render::Line(_triangles.GetVB().VertXY(_triangles.GetVB().Index(i + 2)).x, _triangles.GetVB().VertXY(_triangles.GetVB().Index(i + 2)).y
                     , _triangles.GetVB().VertXY(_triangles.GetVB().Index(i + 1)).x, _triangles.GetVB().VertXY(_triangles.GetVB().Index(i + 1)).y, 0x4FFFFFFF);
        Render::Line(_triangles.GetVB().VertXY(_triangles.GetVB().Index(i + 0)).x, _triangles.GetVB().VertXY(_triangles.GetVB().Index(i + 0)).y
                     , _triangles.GetVB().VertXY(_triangles.GetVB().Index(i + 2)).x, _triangles.GetVB().VertXY(_triangles.GetVB().Index(i + 2)).y, 0x4FFFFFFF);
    }

    //parent = Render::GetCurrentMatrix();
    for (unsigned int i = 0; i < _dots.size(); ++i)
    {
        Render::Line(_dots[i].pos.x, _dots[i].pos.y, _dots[(i + 1) % _dots.size()].pos.x, _dots[(i + 1) % _dots.size()].pos.y, 0xFFFFFFFF);
    }

    Render::SetMatrixUnit();

    float alpha = (Render::GetColor() >> 24) / 255.f;
    Render::SetAlpha(Math::round(0xAF * alpha));
    std::set<int> drawBig;
    for (unsigned int i = 0; i < _dotUnderCursor.size(); ++i)
    {
        drawBig.insert(_dotUnderCursor[i]);
    }
    for (unsigned int i = 0; i < _selectedDots.size(); ++i)
    {
        drawBig.insert(_selectedDots[i]);
    }

    for (unsigned int i = 0; i < _screenDots.size(); ++i) {
        if (drawBig.end() != drawBig.find(i)) {
            scale->Render(_screenDots[i].x - scale->Width() / 2.f, _screenDots[i].y - scale->Height() / 2.f);
        } else {
            scaleSide->Render(_screenDots[i].x - scaleSide->Width() / 2.f, _screenDots[i].y - scaleSide->Height() / 2.f);
        }
    }
    Render::SetAlpha(Math::round(0xFF * alpha));

    Render::PopMatrix();
//		Render::SetFiltering(TileEditorInterface::Instance()->FilteringTexture());


    if (!Animation::Instance()->GetDebugBone())
        return;

    BoneList bones;
    bones.push_back(Animation::Instance()->GetDebugBone());
    Animation::Instance()->GetDebugBone()->GetBoneList(bones);

    float signSq = Math::SignedSquare(_dots);

    for (unsigned int j = 0; j < _selectedDots.size(); ++j)
    {
        uint i = _selectedDots[j];

        FPoint &a = _dots[(i + _dots.size() - 1) % _dots.size()].pos;
        FPoint &b = _dots[i].pos;
        FPoint &c = _dots[(i + 1) % _dots.size()].pos;

        std::vector<std::pair<BoneAnimated *, float> > boneDistance;
        for (uint j = 0; j < bones.size(); ++j)
        {
            BoneAnimated *d = bones[j];

            FPoint s(0, 0);
            FPoint e(d->GetLength(), 0);
            d->GetMatrix().Mul(s);
            d->GetMatrix().Mul(e);
            FPoint center = 0.5f * (s + e);

            if (Math::VMul(a - center, b - center) * signSq >= 0.f || Math::VMul(b - center, c - center) * signSq >= 0.f)
            {
                if ((d->GetParent() == NULL && (s - b).Length() < d->GetLength() / 2)
                        || (!d->HasChild() && (e - b).Length() < d->GetLength() / 2))
                {
                    boneDistance.push_back(
                        std::make_pair<BoneAnimated *, float>
                        (d, 0.f)
                    );
                }
                else
                {
                    boneDistance.push_back(std::make_pair<BoneAnimated *, float>(d, Math::Distance(s, e, b) ) );
                }
            }
        }
        std::sort(boneDistance.begin(), boneDistance.end(), CmpBoneDistance);
        for (uint i = 0; i < std::min((size_t)2, boneDistance.size()); ++i)
        {
            boneDistance[i].first->DrawRed();
        }
    }

}
Exemplo n.º 4
0
ValueNode_Bone::BoneList
ValueNode_Bone::get_ordered_bones(etl::handle<const Canvas> canvas)
{
	std::multimap<ValueNode_Bone::Handle, ValueNode_Bone::Handle> uses;
	std::multimap<ValueNode_Bone::Handle, ValueNode_Bone::Handle> is_used_by;
	BoneList current_list;

	{
		BoneMap bone_map(canvas_map[canvas]);
		for(BoneMap::const_iterator iter=bone_map.begin();iter!=bone_map.end();++iter)
		{
			ValueNode_Bone::Handle user(iter->second);
			BoneSet ref(get_bones_referenced_by(user, false));
			if (ref.empty())
			{
				if (getenv("SYNFIG_DEBUG_ORDER_BONES_FOR_SAVE_CANVAS")) printf("%s:%d %s doesn't need anybody\n", __FILE__, __LINE__,
					   user->get_bone_name(0).c_str());
				current_list.push_back(user);
			}
			else
				for(BoneSet::iterator iter=ref.begin();iter!=ref.end();++iter)
				{
					ValueNode_Bone::Handle used(*iter);
					if (getenv("SYNFIG_DEBUG_ORDER_BONES_FOR_SAVE_CANVAS")) printf("%s:%d %s is used by %s\n", __FILE__, __LINE__,
																				   used->get_bone_name(0).c_str(),
																				   user->get_bone_name(0).c_str());
					is_used_by.insert(make_pair(used, user));
					uses.insert(make_pair(user, used));
				}
		}
	}

	BoneList ret;
	BoneSet seen;
	BoneList new_list;

	while (current_list.size())
	{
		if (getenv("SYNFIG_DEBUG_ORDER_BONES_FOR_SAVE_CANVAS")) printf("%s:%d current_list has %zd members; we have %zd in is_used_by and %zd in uses\n",
																	   __FILE__, __LINE__, current_list.size(), is_used_by.size(), uses.size());
		for(BoneList::iterator iter=current_list.begin();iter!=current_list.end();++iter)
		{
			ValueNode_Bone::Handle bone(*iter);
			if (getenv("SYNFIG_DEBUG_ORDER_BONES_FOR_SAVE_CANVAS")) printf("%s:%d bone: %s\n", __FILE__, __LINE__, bone->get_bone_name(0).c_str());
			ret.push_back(bone);

			std::multimap<ValueNode_Bone::Handle, ValueNode_Bone::Handle>::iterator begin(is_used_by.lower_bound(bone));
			std::multimap<ValueNode_Bone::Handle, ValueNode_Bone::Handle>::iterator end(is_used_by.upper_bound(bone));
			for (std::multimap<ValueNode_Bone::Handle, ValueNode_Bone::Handle>::iterator iter = begin; iter != end; iter++)
			{
				ValueNode_Bone::Handle user(iter->second);
				if (getenv("SYNFIG_DEBUG_ORDER_BONES_FOR_SAVE_CANVAS")) printf("\t\t\t%s:%d user: %s\n", __FILE__, __LINE__, user->get_bone_name(0).c_str());

				// erase (user,bone) from uses
				if (getenv("SYNFIG_DEBUG_ORDER_BONES_FOR_SAVE_CANVAS")) printf("%s:%d trying to erase - searching %zd\n", __FILE__, __LINE__, uses.count(user));
				std::multimap<ValueNode_Bone::Handle, ValueNode_Bone::Handle>::iterator begin2(uses.lower_bound(user));
				std::multimap<ValueNode_Bone::Handle, ValueNode_Bone::Handle>::iterator end2(uses.upper_bound(user));
				std::multimap<ValueNode_Bone::Handle, ValueNode_Bone::Handle>::iterator iter2;
				for (iter2 = begin2; iter2 != end2; iter2++)
				{
					if (iter2->second == bone)
					{
						uses.erase(iter2);
						if (getenv("SYNFIG_DEBUG_ORDER_BONES_FOR_SAVE_CANVAS")) printf("%s:%d found it\n", __FILE__, __LINE__);
						break;
					}
					else
					{
						if (getenv("SYNFIG_DEBUG_ORDER_BONES_FOR_SAVE_CANVAS")) printf("no\n");
					}
				}
				if (iter2 == end2)
				{
					if (getenv("SYNFIG_DEBUG_ORDER_BONES_FOR_SAVE_CANVAS")) printf("%s:%d didn't find it?!?\n", __FILE__, __LINE__);
					assert(0);
				}

				if (getenv("SYNFIG_DEBUG_ORDER_BONES_FOR_SAVE_CANVAS")) printf("%s:%d now there are %zd\n", __FILE__, __LINE__, uses.count(user));
				if (uses.count(user) == 0)
				{
					if (getenv("SYNFIG_DEBUG_ORDER_BONES_FOR_SAVE_CANVAS")) printf("\t\t\t%s:%d adding %s\n", __FILE__, __LINE__, user->get_bone_name(0).c_str());
					new_list.push_back(user);
				}
			}
		}
		current_list = new_list;
		new_list.clear();
	}

	assert(uses.empty());

	return ret;
}
NiSkinPartition::NiSkinPartition(Ref<NiTriBasedGeom> shape, int maxBonesPerPartition, int maxBonesPerVertex ) {
   NiSkinInstanceRef skinInst = shape->GetSkinInstance();
   if ( skinInst == NULL ) {
      throw runtime_error( "You must bind a skin before setting generating skin partitions.  No NiSkinInstance found." );
   }
   NiSkinDataRef skinData = skinInst->GetSkinData();
   if ( skinData == NULL ) {
      throw runtime_error( "You must bind a skin before setting generating skin partitions.  No NiSkinData found." );
   }
   NiTriBasedGeomDataRef geomData = DynamicCast<NiTriBasedGeomData>(shape->GetData() );
   if ( geomData == NULL ) {
      throw runtime_error( "Attempted to generate a skin partition on a mesh with no geometry data." );
   }

      // read in the weights from NiSkinData
   vector<Vector3> verts = geomData->GetVertices();
   vector< BoneWeightList > weights;
   if (verts.empty()){
      throw runtime_error( "Attempted to generate a skin partition on a mesh with no vertices." );
   }

   Triangles triangles = geomData->GetTriangles();
   if (triangles.empty()) {
      throw runtime_error( "Attempted to generate a skin partition on a mesh with no triangles." );
   }

   weights.resize( verts.size() );
   int numBones = skinData->GetBoneCount();
   for ( int bone = 0; bone < numBones; bone++ )
   {
      vector<SkinWeight> vertexWeights = skinData->GetBoneWeights(bone);
      for (int r = 0; r < int(vertexWeights.size()); ++r ){
         int vertex = vertexWeights[r].index;
         float weight = vertexWeights[r].weight;
         if ( vertex >= int(weights.size()) )
            throw runtime_error( "bad NiSkinData - vertex count does not match" );
         weights[vertex].insert( weights[vertex].end(), BoneWeight(bone, weight) );
      }
   }

   // count min and max bones per vertex
   int minBones, maxBones;
   minBones = maxBones = weights[0].size();
   for(vector< BoneWeightList >::iterator itr = weights.begin(); itr != weights.end(); ++itr ){
      int n = (*itr).size();
      minBones = min(n, minBones);
      maxBones = max(n, maxBones);
   }

   if ( minBones <= 0 )
      throw runtime_error( "bad NiSkinData - some vertices have no weights at all" );

   // reduce vertex influences if necessary
   if ( maxBones > maxBonesPerVertex )
   {
      int c = 0;
      for ( vector< BoneWeightList >::iterator it = weights.begin(); it != weights.end(); ++it )
      {
         BoneWeightList & lst = *it;
         if ( int(lst.size()) > maxBonesPerVertex )
            c++;

         while ( int(lst.size()) > maxBonesPerVertex ) {
            int j = 0;
            float weight = lst.front().second;
            for ( int i = 0; i < int(lst.size()); i++ )
            {
               if ( lst[i].second < weight )
                  j = i;
            }
            BoneWeightList::iterator jit = lst.begin() + j;
            lst.erase( jit );
         }

         float totalWeight = 0;
         for (BoneWeightList::iterator bw = lst.begin(); bw != lst.end(); ++bw) {
            totalWeight += (*bw).second;
         }
         for (BoneWeightList::iterator bw = lst.begin(); bw != lst.end(); ++bw) {
            (*bw).second /= totalWeight;
         }
      }
      //qWarning() << "reduced" << c << "vertices to" << maxBonesPerVertex << "bone influences (maximum number of bones per vertex was" << maxBones << ")";
   }

   maxBones = maxBonesPerVertex;

   // reduces bone weights so that the triangles fit into the partitions

   typedef multimap<int,int> matchmap;
   typedef pair<matchmap::iterator, matchmap::iterator> matchrange;
   matchmap match;
   bool doMatch = true;

   BoneList tribones;
   int cnt = 0;
   for (Triangles::iterator itr = triangles.begin(); itr != triangles.end(); ++itr) {
      Triangle& tri = (*itr);
      do
      {
         tribones.clear();
         for ( int c = 0; c < 3; c++ ) {
            BoneWeightList& bwl = weights[tri[c]];
            for (BoneWeightList::iterator bw = bwl.begin(); bw != bwl.end(); ++bw) {
               if ( tribones.end() == find(tribones.begin(), tribones.end(), (*bw).first ) )
                  tribones.insert(tribones.end(), (*bw).first );
            }
         }

         if ( int(tribones.size()) > maxBonesPerPartition )
         {
            // sum up the weights for each bone
            // bones with weight == 1 can't be removed

            map<int, float> sum;
            vector<int> nono;

            for ( int t = 0; t < 3; t++ ) {
               BoneWeightList& bwl = weights[tri[t]];
               if ( bwl.size() == 1 )
                  nono.insert(nono.end(), bwl.front().first );

               for (BoneWeightList::iterator bw = bwl.begin(); bw != bwl.end(); ++bw) {
                  sum[ (*bw).first ] += (*bw).second;
               }                 
            }

            // select the bone to remove

            float minWeight = 5.0;
            int minBone = -1;

            for (map<int, float>::iterator sitr = sum.begin(); sitr != sum.end(); ++sitr) {
               int b = (*sitr).first;
               if ( (find(nono.begin(), nono.end(), b) == nono.end()) && sum[b] < minWeight) {
                  minWeight = sum[b];
                  minBone = b;
               }
            }

            if ( minBone < 0 )	// this shouldn't never happen
               throw runtime_error( "internal error 0x01" );

            // do a vertex match detect
            if ( doMatch ) {
               for ( size_t a = 0; a < verts.size(); a++ ) {
                  match.insert(matchmap::value_type(a, a));
                  for ( size_t b = a + 1; b < verts.size(); b++ ) {
                     if ( verts[a] == verts[b] && weights[a] == weights[b] ) {
                        match.insert(matchmap::value_type(a, b));
                        match.insert(matchmap::value_type(b, a));
                     }
                  }
               }
            }

            // now remove that bone from all vertices of this triangle and from all matching vertices too
            for ( int t = 0; t < 3; t++ ) {
               bool rem = false;

               matchrange range = match.equal_range(tri[t]);
               for (matchmap::iterator itr = range.first; itr != range.second; ++itr) {
                  int v = (*itr).second;

                  BoneWeightList & bws = weights[ v ];
                  BoneWeightList::iterator it = bws.begin();
                  while ( it != bws.end() ) {
                     BoneWeight & bw = *it;
                     if ( bw.first == minBone ) {
                        it = bws.erase(it);
                        rem = true;
                     } else {
                        ++it;
                     }
                  }

                  float totalWeight = 0;

                  for (BoneWeightList::iterator bw = bws.begin(); bw != bws.end(); ++bw) {
                     totalWeight += (*bw).second;
                  }

                  if ( totalWeight == 0 )
                     throw runtime_error( "internal error 0x02" );

                  // normalize
                  for (BoneWeightList::iterator bw = bws.begin(); bw != bws.end(); ++bw) {
                     (*bw).second /= totalWeight;
                  }
               }
               if ( rem )
                  cnt++;
            }
         }
      } while ( int(tribones.size()) > maxBonesPerPartition );
   }
   //if ( cnt > 0 )
   //   qWarning() << "removed" << cnt << "bone influences";

   PartitionList& parts = skinPartitionBlocks;
   // split the triangles into partitions
   while ( ! triangles.empty() ) {

      SkinPartition part;
      Triangles::iterator it = triangles.begin();
      while ( it != triangles.end() ) {
         Triangle & tri = *it;

         BoneList tribones;
         for ( int c = 0; c < 3; c++ ) {
            BoneWeightList& bws = weights[tri[c]];
            for (BoneWeightList::iterator bw = bws.begin(); bw != bws.end(); ++bw) {
               if ( tribones.end() == find(tribones.begin(), tribones.end(), (*bw).first ) )
                  tribones.push_back( (*bw).first );
            }
         }

         if ( part.bones.empty() || containsBones( part.bones, tribones ) ) {
            mergeBones( part.bones, tribones );
            part.triangles.push_back( tri );
            it = triangles.erase(it);
         } else {
            ++it;
         }
      }

      parts.push_back( part );
   }

   //qWarning() << parts.size() << "small partitions";

   // merge partitions

   bool merged;
   do
   {
      merged = false;
      // Working backwards through this list minimizes numbers of swaps
      //for ( int p2 = int(parts.size()-1); p2 >= 0  && ! merged; --p2 )
      //{
      //   Partition& part2 = parts[p2];
      //   for ( int p1 = int(p2-1); p1 >= 0 && ! merged; --p1 )
      //   {
      //      Partition& part1 = parts[p1];
      for ( int p1 = 0; p1 < int(parts.size()) && ! merged; p1++ )
      {
         Partition& part1 = parts[p1];
         for ( int p2 = p1+1; p2 < int(parts.size()) && ! merged; p2++ )
         {
            Partition& part2 = parts[p2];
            BoneList mergedBones = part1.bones;
            mergeBones( mergedBones, part2.bones );
            if ( int(mergedBones.size()) <= maxBonesPerPartition )
            {
               PartitionList::iterator p2i = parts.begin() + p2;
               part1.bones = mergedBones;
               part1.triangles.insert(part1.triangles.end(), (*p2i).triangles.begin(), (*p2i).triangles.end());
               parts.erase(p2i);
               merged = true;
            }
         }
      }
   }
   while ( merged );

   //qWarning() << parts.size() << "partitions";

   // start writing NiSkinPartition

   for ( int p = 0; p < int(parts.size()); p++ )
   {
      Partition& part = parts[p];
      BoneList& bones = part.bones;
      sort( bones.begin(), bones.end() );

      Triangles& triangles = part.triangles;

      vector<unsigned short>& vertices = part.vertexMap;
      for( Triangles::iterator tri = triangles.begin(); tri !=  triangles.end(); ++tri) {
         for ( int t = 0; t < 3; t++ ) {
            if ( vertices.end() == find(vertices.begin(), vertices.end(), (*tri)[t] ) )
               vertices.push_back( (*tri)[t] );
         }
      }
      sort( vertices.begin(), vertices.end() );
      part.numVertices = vertices.size();
      part.hasVertexMap = true;

      // map the vertices

      for ( int tri = 0; tri < int(triangles.size()); tri++ ) {
         for ( int t = 0; t < 3; t++ ) {
            triangles[tri][t] = indexOf(vertices.begin(), vertices.end(), triangles[tri][t]);
         }
      }

      SetWeightsPerVertex(p, maxBones);
      EnableVertexWeights(p, true);
      EnableVertexBoneIndices(p, true);

      // strippify the triangles
      NiTriStripsDataRef data = new NiTriStripsData(triangles, true);
      int nstrips = data->GetStripCount();
      SetStripCount( p, nstrips );
      for ( int i=0; i<nstrips; ++i ) {
         SetStrip(p, i, data->GetStrip(i));
      }

      //// Special case for pre-stripped data
      //unsigned short *data = new unsigned short[triangles.size() * 3 * 2];
      //for (size_t i=0; i< triangles.size(); i++) {
      //   data[i * 3 + 0] = triangles[i][0];
      //   data[i * 3 + 1] = triangles[i][1];
      //   data[i * 3 + 2] = triangles[i][2];
      //}
      //PrimitiveGroup * groups = 0;
      //unsigned short numGroups = 0;

      //// GF 3+
      //SetCacheSize(CACHESIZE_GEFORCE3);
      //// don't generate hundreds of strips
      //SetStitchStrips(true);
      //GenerateStrips(data, triangles.size()*3, &groups, &numGroups);
      //delete [] data;
      //if (groups) {
      //   SetStripCount(p, numGroups);
      //   for (int g=0; g<numGroups; g++) {
      //      if (groups[g].type == PT_STRIP) {
      //         vector<Niflib::unsigned short> strip(groups[g].numIndices);
      //         for (size_t s=0; s<groups[g].numIndices; s++)
      //            strip[s] = groups[g].indices[s];
      //         SetStrip(p, g, strip);
      //      }
      //   }
      //   delete [] groups;
      //}

      // fill in vertex weights and bones
      for (size_t v = 0; v < vertices.size(); ++v) {
         BoneWeightList& bwl = weights[vertices[v]];
         for ( int b = 0; b < maxBones; b++ ) {
            part.boneIndices[v][b] = (int(bwl.size()) > b) ? indexOf(bones.begin(), bones.end(), bwl[b].first) : 0 ;
            part.vertexWeights[v][b] = (int(bwl.size()) > b ? bwl[b].second : 0.0f);
         }
      }
   }
}