Ejemplo n.º 1
0
bool CollisionImport::ImportTriStripsShape(INode *rbody, bhkRigidBodyRef body, bhkNiTriStripsShapeRef shape, INode *parent, Matrix3& tm)
{
	if (shape->GetNumStripsData() != 1)
		return NULL;

	if ( ImpNode *node = ni.i->CreateNode() )
	{
		TriObject *triObject = CreateNewTriObject();
		node->Reference(triObject);

		INode *inode = node->GetINode();

		// Texture
		Mesh& mesh = triObject->GetMesh();
		NiTriStripsDataRef triShapeData = shape->GetStripsData(0);
		if (triShapeData == NULL)
			return false;

		// Temporary shape
		NiTriStripsRef triShape = new NiTriStrips();
		vector<Triangle> tris = triShapeData->GetTriangles();
		ni.ImportMesh(node, triObject, triShape, triShapeData, tris);
		CreatebhkCollisionModifier(inode, bv_type_shapes, shape->GetMaterial(), OL_UNIDENTIFIED, 0);
		ImportBase(body, shape, parent, inode, tm);
		AddShape(rbody, inode);
		return true;
	}
	return false;
}
Ejemplo n.º 2
0
NiTriStripsDataRef Exporter::makeTriStripsData(const TriStrips &strips)
{
	NiTriStripsDataRef stripData = new NiTriStripsData();

	if (strips.size() > 0)
	{
		stripData->SetStripCount(strips.size());

		int i = 0;
		TriStrips::const_iterator it;
		for (it=strips.begin(); it!=strips.end(); ++it)
			stripData->SetStrip(i++, *it);
	}

	return stripData;
}
bool NifImporter::ImportMesh(NiTriStripsRef triStrips)
{
   bool ok = true;

   ImpNode *node = i->CreateNode();
   if(!node)
	   return false;
   INode *inode = node->GetINode();
   TriObject *triObject = CreateNewTriObject();
   node->Reference(triObject);
   wstring name = wide(triStrips->GetName());
   node->SetName(name.c_str());

   // Texture
   Mesh& mesh = triObject->GetMesh();
   NiTriStripsDataRef triStripsData = DynamicCast<NiTriStripsData>(triStrips->GetData());
   if (triStripsData == NULL)
      return false;

   vector<Triangle> tris = triStripsData->GetTriangles();
   ok |= ImportMesh(node, triObject, triStrips, triStripsData, tris);
   return ok;
}
Ejemplo n.º 4
0
/*---------------------------------------------------------------------------*/
NiTriShapeRef NifConvertUtility::convertNiTriStrips(NiTriStripsRef pSrcNode, NiTriShapeRef pTmplNode, NiAlphaPropertyRef pTmplAlphaProp)
{
	NiTriShapeRef			pDstNode   (new NiTriShape());
	NiTriShapeDataRef		pDstGeo    (new NiTriShapeData());
	NiTriStripsDataRef		pSrcGeo    (DynamicCast<NiTriStripsData>(pSrcNode->GetData()));
	vector<NiPropertyRef>	srcPropList(pSrcNode->GetProperties());

	//  copy NiTriStrips to NiTriShape
	pDstNode->SetCollisionObject(NULL);  //  no collision object here
	pDstNode->SetFlags          (14);    //  ???
	pDstNode->SetName           (pSrcNode->GetName());
	pDstNode->SetLocalTransform (pSrcNode->GetLocalTransform());
	pDstNode->SetData           (pDstGeo);

	//  move properties
	for (auto pIter=srcPropList.begin(), pEnd=srcPropList.end(); pIter != pEnd; ++pIter)
	{
		if (DynamicCast<NiVertexColorProperty>(*pIter) != NULL)
		{
			int	iii=0;
		}
		pDstNode->AddProperty(*pIter);
	}
	pSrcNode->ClearProperties();

	//  data node
	if (pSrcGeo != NULL)
	{
		pDstGeo->SetVertices    (pSrcGeo->GetVertices());
		pDstGeo->SetNormals     (pSrcGeo->GetNormals());
		pDstGeo->SetTriangles   (pSrcGeo->GetTriangles());
		pDstGeo->SetVertexColors(pSrcGeo->GetColors());
		pDstGeo->SetUVSetCount  (pSrcGeo->GetUVSetCount());
		for (short idx(0), max(pSrcGeo->GetUVSetCount()); idx < max; ++idx)
		{
			pDstGeo->SetUVSet(idx, pSrcGeo->GetUVSet(idx));
		}
	}  //  if (pSrcGeo != NULL)

	//  return converted NiTriShape
	return convertNiTri(pDstNode, pTmplNode, pTmplAlphaProp);
}
Ejemplo n.º 5
0
MeshData *CMesh::AddTriStrips(NiTriStripsRef tristripsnode)
{
	unsigned long index;
	MeshData *newmeshpart;
	MeshVertex *davert;
	unsigned short *daind;

	if(tristripsnode==NULL) return NULL;

	NiGeometryDataRef ergeom = tristripsnode->GetData();
	NiTriStripsDataRef geom = DynamicCast<NiTriStripsData>(ergeom);
	if(geom==NULL)
	{
		output_text(L"Couldn't cast to NiTriStripsData!\r\n");
		return NULL;
	}


	if(geom->GetUVSetCount()==0||geom->GetStripCount()==0)
	{
		if(geom->GetUVSetCount()==0) output_text_hex(L"No UV Set:",formID);
		if(geom->GetStripCount()==0) output_text_hex(L"No Strip:",formID);
		return NULL;
	}

	
	//Get the vertices
	vector<Vector3> davertices=geom->GetVertices();
	//Get the uvs
	vector<TexCoord> dauvs=geom->GetUVSet(0);
	//Get the normals
	vector<Vector3> danormals=geom->GetNormals();
	//Get the colors
	vector<Color4> dacolors=geom->GetColors();
	//Get the indices
	vector<unsigned short> daindices=geom->GetStrip(0);

	newmeshpart=new MeshData;

	//Get the number of vertex
	newmeshpart->numvert=geom->GetVertexCount();
	//Get the number of indices
	newmeshpart->numind=daindices.size();
	//newmeshpart->numind=geom->GetVertexIndexCount();

	//output_text_value(L"Number of vertex:",newmeshpart->numvert);
	//output_text_value(L"Number of indices:",newmeshpart->numind);

	if(newmeshpart->numvert==0||newmeshpart->numind==0)
	{
		if(newmeshpart->numvert==0) output_text_hex(L"No vertex?:",formID);
		if(newmeshpart->numind==0) output_text_hex(L"No indices?:",formID);
		delete newmeshpart;
		return NULL;
	}

	davert=new MeshVertex[newmeshpart->numvert];
	daind=new unsigned short[newmeshpart->numind];

	for(index=0;index<newmeshpart->numind;index++)
	{
		//memcpy later
		//daind[index]=daindices[(newmeshpart->numind-1)-index];
		daind[index]=daindices[index];
	}

	for(index=0;index<newmeshpart->numvert;index++)
	{
		//change to use memcpy later
		davert[index].Pos.x=davertices[index].x;
		davert[index].Pos.y=davertices[index].y;
		davert[index].Pos.z=davertices[index].z;
		davert[index].TextUV.x=dauvs[index].u;
		davert[index].TextUV.y=dauvs[index].v;
		davert[index].Norm.x=danormals[index].x;
		davert[index].Norm.y=danormals[index].y;
		davert[index].Norm.z=danormals[index].z;
	}

	if(dacolors.size()!=0)
	{
		for(index=0;index<newmeshpart->numvert;index++)
		{
			davert[index].Color.x=dacolors[index].r;
			davert[index].Color.y=dacolors[index].g;
			davert[index].Color.z=dacolors[index].b;
			davert[index].Color.w=dacolors[index].a;
		}
	}
	else
	{
		for(index=0;index<newmeshpart->numvert;index++)
		{
			davert[index].Color.x=1.0f;
			davert[index].Color.y=1.0f;
			davert[index].Color.z=1.0f;
			davert[index].Color.w=1.0f;
		}
	}

	//Load the texture
	vector<NiPropertyRef> daprops=tristripsnode->GetProperties();
	bool foundtext=false;
	for(index=0;index<daprops.size();index++)
	{
		if(daprops[index]->IsSameType(NiTexturingProperty::TYPE))
		{
			NiTexturingPropertyRef datextprop=DynamicCast<NiTexturingProperty>(daprops[index]);
			if(datextprop==NULL)
			{
				output_text(L"Failed NiTexturingProperty Cast!\r\n");
				delete newmeshpart;
				delete [] davert;
				delete [] daind;
				return NULL;
			}
			foundtext=true;
			TexDesc datextdesc=datextprop->GetTexture(0);
			newmeshpart->datext=pTextureManager->Load_Direct((char *)datextdesc.source->GetTextureFileName().c_str());
			break;
		}
	}

	if(foundtext==false) output_text_hex(L"Couldn't find texture:",formID);

	newmeshpart->pIndiceBuf=pDXManager->CreateIndiceBuffer((unsigned char *)daind,newmeshpart->numind,sizeof(unsigned short));
	newmeshpart->pVertexBuf=pDXManager->CreateVertexBuffer((unsigned char *)davert,newmeshpart->numvert,sizeof(MeshVertex));

	delete [] daind;
	delete [] davert;
	
	//Ok now let's add it
	listdata.push_back(newmeshpart);

	return newmeshpart;
}
Ejemplo n.º 6
0
static void BuildCollision(NiNodeRef top, vector<bhkRigidBodyRef>& bodies)
{
	for (vector<bhkRigidBodyRef>::iterator itr = bodies.begin(); itr != bodies.end(); ++itr)
	{
		bhkRigidBodyRef body = (*itr);
		bhkShapeRef shape = body->GetShape();
		hkpShapeCollection * list = NULL;
		
		// Mopp already in place
		bhkMoppBvTreeShapeRef mopp;
		if (shape->IsDerivedType(bhkMoppBvTreeShape::TYPE))
		{
			if ( mopp = DynamicCast<bhkMoppBvTreeShape>(shape) )
			{
				if ( bhkPackedNiTriStripsShapeRef mesh = DynamicCast<bhkPackedNiTriStripsShape>( mopp->GetShape() ) )
				{
					list = ConstructHKMesh(mesh);
				}
			}
		}
		else if ( shape->IsDerivedType(bhkPackedNiTriStripsShape::TYPE) )
		{
			bhkPackedNiTriStripsShapeRef mesh = DynamicCast<bhkPackedNiTriStripsShape>( shape );
			mopp = new bhkMoppBvTreeShape();
			mopp->SetMaterial( HAV_MAT_WOOD );
			body->SetShape( mopp );
			mopp->SetShape( mesh );
			list = ConstructHKMesh(mesh);
		}
		else if ( shape->IsDerivedType(bhkNiTriStripsShape::TYPE) )
		{
			bhkNiTriStripsShapeRef mesh = DynamicCast<bhkNiTriStripsShape>( shape );
			
			bhkPackedNiTriStripsShapeRef packedMesh = new bhkPackedNiTriStripsShape();
			hkPackedNiTriStripsDataRef packedData = new hkPackedNiTriStripsData();

			vector<OblivionSubShape> shapes;
			vector<Vector3> verts, norms;
			vector<Triangle> tris;
			for (int i=0;i<mesh->GetNumStripsData(); ++i){
				int toff = tris.size();
				NiTriStripsDataRef data = mesh->GetStripsData(i);

				vector<Vector3> v = data->GetVertices();
				verts.reserve( verts.size() + v.size() );
				for (vector<Vector3>::iterator vi=v.begin(); vi != v.end(); ++vi )
					verts.push_back( *vi / 7.0 );

				vector<Vector3> n = data->GetNormals();
				norms.reserve( norms.size() + n.size() );
				for (vector<Vector3>::iterator ni=n.begin(); ni != n.end(); ++ni )
					norms.push_back(*ni);

				vector<Triangle> t = data->GetTriangles();
				tris.reserve( tris.size() + t.size() );
				for (vector<Triangle>::iterator ti=t.begin(); ti != t.end(); ++ti )
					tris.push_back( Triangle(ti->v1 + toff, ti->v2 + toff, ti->v3 + toff) );

				OblivionSubShape subshape;
				subshape.material = mesh->GetMaterial();
				if ( i < (int)mesh->GetNumDataLayers() )
					subshape.layer = mesh->GetOblivionLayer( i );
				else
					subshape.layer = OL_STATIC;

				shapes.push_back( subshape );
			}
			packedData->SetNumFaces( tris.size() );
			packedData->SetVertices( verts );
			packedData->SetTriangles( tris );
			//packedData->SetNormals( norms );
			packedMesh->SetData( packedData );
			packedMesh->SetSubShapes( shapes );

			list = ConstructHKMesh(verts, tris);

			mopp = new bhkMoppBvTreeShape();
			mopp->SetMaterial( mesh->GetMaterial() );
			body->SetShape( mopp );
			mopp->SetShape( packedMesh );
		}

		if (!mopp || NULL == list)
			continue;

		//////////////////////////////////////////////////////////////////////////

		hkpMoppCompilerInput mfr;
		mfr.setAbsoluteFitToleranceOfAxisAlignedTriangles( hkVector4( 0.1f, 0.1f, 0.1f ) );
		//mfr.setAbsoluteFitToleranceOfTriangles(0.1f);
		//mfr.setAbsoluteFitToleranceOfInternalNodes(0.0001f);

		hkpMoppCode* code = hkpMoppUtility::buildCode(list, mfr);

		vector<Niflib::byte> moppcode;
		moppcode.resize( code->m_data.getSize() );
		for (int i=0; i<code->m_data.getSize(); ++i )
			moppcode[i] = code->m_data[i];
		mopp->SetMoppCode( moppcode );
		mopp->SetMoppOrigin( Vector3(code->m_info.m_offset(0), code->m_info.m_offset(1), code->m_info.m_offset(2) ));
		mopp->SetMoppScale( code->m_info.getScale() );
	
		code->removeReference();
		list->removeReference();
	}
}
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);
         }
      }
   }
}