Exemple #1
0
void FBXImporter::ProcessMesh(FbxNodeAttribute* nodeAttribute)
{
	FbxMesh* mesh = (FbxMesh*)nodeAttribute;
	
	// 网格是否三角化的?如果不是先将其转为三角化的。
	// 注意:一步其实应该在建模软件导出的时候进行。
	if (!mesh->IsTriangleMesh())
	{
		FbxGeometryConverter converter(mSDKManager);

		// #1
		// For FBX SDK 2015.1
		nodeAttribute = converter.Triangulate(nodeAttribute, true, false);
		// For FBX SDK 2013.3
		//converter.TriangulateInPlace(nodeAttribute->GetNode());

		mesh = (FbxMesh*)nodeAttribute;
	}

	FBXMeshData* fbxMeshData = new FBXMeshData();
	fbxMeshData->mMesh = mesh;
	mFBXMeshDatas.push_back(fbxMeshData);

	Log("TriangleCount:%d\n", mesh->GetPolygonCount());
	Log("VertexCount:%d\n", mesh->GetControlPointsCount());
	Log("IndexCount:%d\n", mesh->GetPolygonVertexCount());
	Log("Layer:%d\n", mesh->GetLayerCount());
	Log("DeformerCount:%d\n", mesh->GetDeformerCount());
	Log("MaterialCount%d\n", mesh->GetNode()->GetMaterialCount());
	Log("\n");
}
void FBXConverter::convert( const char* input , const char* output )
{
	if ( converting ) return;
	converting = true;
	FbxManager* fbxManger = FbxManager::Create();

	FbxIOSettings* ios = FbxIOSettings::Create( fbxManger , IOSROOT );
	fbxManger->SetIOSettings( ios );

	FbxImporter* importer = FbxImporter::Create( fbxManger , "" );

	bool status = importer->Initialize( input , -1 , fbxManger->GetIOSettings() );
	if ( !status )
	{
		std::cout << importer->GetStatus().GetErrorString() << std::endl;
	}

	FbxScene* scene = FbxScene::Create( fbxManger , "theScene" );
	importer->Import( scene );
	importer->Destroy();

	FbxMesh* theMesh = findMesh( scene->GetRootNode() );
	

	if ( theMesh )
	{
		std::vector<VertexData> vertices;
		std::vector<IndexData> indices;

		FbxStringList uvSets;
		theMesh->GetUVSetNames( uvSets );

		processPolygons( theMesh , vertices , indices , uvSets );

		std::vector<JointData> skeleton;
		processSkeletonHierarchy( scene->GetRootNode() , skeleton );
		if ( skeleton.size() ) processAnimations( theMesh->GetNode() , skeleton , vertices , indices );

		std::string modelData;
		for ( unsigned int i = 0; i < vertices.size(); ++i )
		{
			modelData += DATASTRING( vertices[i].position );
			modelData += DATASTRING( vertices[i].uv );
			modelData += DATASTRING( vertices[i].normal );
			modelData += DATASTRING( vertices[i].tangent );
			modelData += DATASTRING( vertices[i].bitangent );
			
			for ( unsigned int j = 0; j < 4 ; ++j )
			{
				if ( j < vertices[i].blendingInfo.size() )
				{
					int blendingIndex = vertices[i].blendingInfo[j].blendingIndex;
					modelData += DATASTRING( blendingIndex );
				}
				else
				{
					int blendingIndex = -1;
					modelData += DATASTRING( blendingIndex );
				}
			}
			for ( unsigned int j = 0; j < 4; ++j )
			{
				if ( j < vertices[i].blendingInfo.size() )
				{
					float blendingIndex = vertices[i].blendingInfo[j].blendingWeight;
					modelData += DATASTRING( blendingIndex );
				}
				else
				{
					float blendingIndex = -1;
					modelData += DATASTRING( blendingIndex );
				}
			}
		}

		for ( unsigned int i = 0; i < indices.size(); ++i)
		{
			modelData += DATASTRING( indices[i].index );
		}

		std::string boneData;
		std::vector<unsigned int> boneChildren;
		std::vector<AnimationData> boneAnimation;
		for ( unsigned int i = 0; i < skeleton.size(); ++i )
		{
			boneData += DATASTRING( skeleton[i].offsetMatrix );
			int childDataStart , childDataEnd , animationDataStart , animationDataEnd;
			if ( skeleton[i].children.size() )
			{
				childDataStart = boneChildren.size();
				for ( unsigned int j = 0; j < skeleton[i].children.size(); ++j )
				{
					boneChildren.push_back( skeleton[i].children[j] );
				}
				childDataEnd = boneChildren.size();
			}
			else
			{
				childDataStart = -1;
				childDataEnd = -1;
			}

			if ( skeleton[i].animation.size() )
			{
				animationDataStart = boneAnimation.size();
				for ( unsigned int j = 0; j < skeleton[i].animation.size(); ++j )
				{
					boneAnimation.push_back( skeleton[i].animation[j] );
				}
				animationDataEnd = boneAnimation.size();
			}
			else
			{
				animationDataStart = -1;
				animationDataEnd = -1;
			}
			boneData += DATASTRING( childDataStart );
			boneData += DATASTRING( childDataEnd );
			boneData += DATASTRING( animationDataStart );
			boneData += DATASTRING( animationDataEnd );
		}
		unsigned int sizeofAnimationRangeInfo;
		AnimationFrameRangeInfo frameRange;
		if ( boneAnimation.size() > 0 )
		{
			sizeofAnimationRangeInfo = 1;
			frameRange.nextAnimationFrameInfo = 0;
			frameRange.firstFrame = 1;
			frameRange.lastFrame = boneAnimation[boneAnimation.size() - 1].frame;
		}
		else
		{
			sizeofAnimationRangeInfo = 0;
		}

		std::fstream stream( output , std::ios_base::binary | std::ios_base::out | std::ios_base::trunc );

		unsigned int sizeofVertices = vertices.size();
		stream.write( reinterpret_cast< char* >( &sizeofVertices ) , sizeof( sizeofVertices ));
		unsigned int sizeofIndices = indices.size();
		stream.write( reinterpret_cast< char* >( &sizeofIndices ) , sizeof( sizeofIndices ) );

		unsigned int sizeofBoneData = skeleton.size();
		stream.write( reinterpret_cast<char*>( &sizeofBoneData ) , sizeof( sizeofBoneData ) );

		unsigned int sizeofBoneChildData = boneChildren.size();
		stream.write( reinterpret_cast< char* >( &sizeofBoneChildData ) , sizeof( sizeofBoneChildData ));

		unsigned int sizeofBoneAnimationData = boneAnimation.size();
		stream.write( reinterpret_cast< char* >( &sizeofBoneAnimationData ) , sizeof( sizeofBoneAnimationData ) );

		stream.write( reinterpret_cast< char* >( &sizeofAnimationRangeInfo ) , sizeof( sizeofAnimationRangeInfo ) );

		stream.write( modelData.c_str() , modelData.size() );

		stream.write( boneData.c_str() , boneData.size() );

		for ( unsigned int i = 0; i < boneChildren.size(); ++i )
		{
			stream.write( reinterpret_cast< char* >( &boneChildren[i] ) , sizeof( boneChildren[i] ) );
		}

		for ( unsigned int i = 0; i < boneAnimation.size(); ++i )
		{
			stream.write( reinterpret_cast< char* >( &boneAnimation[i] ) , sizeof( boneAnimation[i] ) );
		}

		
		if(sizeofAnimationRangeInfo) stream.write( reinterpret_cast< char* >( &frameRange ) , sizeof( frameRange ) );
		stream.close();
	}
	converting = false;
}
Exemple #3
0
void FBXImporter::LoadMaterials(FBXMeshData* fbxMeshData)
{
	FbxNode* node = nullptr;
	FbxMesh* mesh = fbxMeshData->mMesh;
	int materialCount = 0;
	int polygonCount = mesh->GetPolygonCount();

	if ((mesh != nullptr) && (mesh->GetNode() != nullptr))
	{
		node = mesh->GetNode();
		materialCount = node->GetMaterialCount();
	}

	bool isAllSame = true;
	for (int i = 0; i < mesh->GetElementMaterialCount(); i++)
	{
		FbxGeometryElementMaterial* materialElement = mesh->GetElementMaterial(i);
		if (materialElement->GetMappingMode() == FbxGeometryElement::eByPolygon)
		{
			isAllSame = false;
			break;
		}
	}

	//For eAllSame mapping type, just out the material and texture mapping info once
	if (isAllSame)
	{
		for (int i = 0; i < mesh->GetElementMaterialCount(); i++)
		{
			FbxGeometryElementMaterial* materialElement = mesh->GetElementMaterial(i);
			if (materialElement->GetMappingMode() == FbxGeometryElement::eAllSame)
			{
				FbxSurfaceMaterial* material = mesh->GetNode()->GetMaterial(materialElement->GetIndexArray().GetAt(0));
				fbxMeshData->mSurfaceMaterial = material;

				int materialId = materialElement->GetIndexArray().GetAt(0);
				if (materialId >= 0)
				{
					LoadMaterialTexture(fbxMeshData, FbxSurfaceMaterial::sDiffuse);
					LoadMaterialTexture(fbxMeshData, FbxSurfaceMaterial::sBump);

					vector<string>& textureFiles = mMeshData->textureFiles;
					auto iter = find(textureFiles.begin(), textureFiles.end(), fbxMeshData->getDiffuseTextureFile());

					if (iter == textureFiles.end())
					{
						textureFiles.push_back(fbxMeshData->getDiffuseTextureFile());
					}

					if (fbxMeshData->getNormalMapTextureFile().size() > 0)
					{
						iter = find(textureFiles.begin(), textureFiles.end(), fbxMeshData->getNormalMapTextureFile());
						if (iter == textureFiles.end())
						{
							textureFiles.push_back(fbxMeshData->getNormalMapTextureFile());
						}
					}
				}
			}
		}
	}
	//For eByPolygon mapping type, just out the material and texture mapping info once
	else
	{
		int materialId = 0;
		int polygonId = 0;
		polygonCount = 0;
		vector<string>& textureFiles = mMeshData->textureFiles;
		vector<MaterialIdOffset>& materialIdOffsets = mMeshData->materialIdOffsets;

		for (int i = 0; i < materialIdOffsets.size(); i++)
		{
			FbxGeometryElementMaterial* materialElement = mesh->GetElementMaterial(0);
			FbxSurfaceMaterial* material = NULL;
			materialId = mMeshData->materialIdOffsets[i].material->materialId;

			material = mesh->GetNode()->GetMaterial(materialElement->GetIndexArray().GetAt(polygonId));
			polygonCount = materialIdOffsets[i].polygonCount;

			fbxMeshData->mSurfaceMaterial = material;

			fbxMeshData->mMaterial = new Material();

			LoadMaterialTexture(fbxMeshData, FbxSurfaceMaterial::sDiffuse);
			LoadMaterialTexture(fbxMeshData, FbxSurfaceMaterial::sBump);

			materialIdOffsets[i].material = fbxMeshData->mMaterial;

			auto iter = find(textureFiles.begin(), textureFiles.end(), fbxMeshData->getDiffuseTextureFile());

			if (iter == textureFiles.end())
			{
				textureFiles.push_back(fbxMeshData->getDiffuseTextureFile());
			}

			if (fbxMeshData->getNormalMapTextureFile().size() > 0)
			{
				iter = find(textureFiles.begin(), textureFiles.end(), fbxMeshData->getNormalMapTextureFile());
				if (iter == textureFiles.end())
				{
					textureFiles.push_back(fbxMeshData->getNormalMapTextureFile());
				}
			}

			polygonId += polygonCount;
		}
	}
}
Exemple #4
0
MeshData* FBXImporter::GetMeshInfo()
{
	mMeshData = new MeshData();

	int indicesIndexOffset = 0;		// 记录当前mesh在整个ib中的索引位移。
	int verticesIndexOffset = 0;	// 记录当前mesh在整个vb中的顶点位移。

	for (int meshIndex = 0; meshIndex < mFBXMeshDatas.size(); meshIndex++)
	{
		FbxMesh* mesh = mFBXMeshDatas[meshIndex]->mMesh;
		FBXMeshData* fbxMeshData = mFBXMeshDatas[meshIndex];
		fbxMeshData->mVerticesCount = mesh->GetControlPointsCount();
		fbxMeshData->mIndicesCount = mesh->GetPolygonVertexCount();
		fbxMeshData->mTrianglesCount = mesh->GetPolygonCount();

		// 获取3dsmax中的全局变换矩阵,稍后可以在DX中还原。
		FbxMatrix gloableTransform = mesh->GetNode()->EvaluateGlobalTransform();

		FbxAMatrix matrixGeo;
		matrixGeo.SetIdentity();

		const FbxVector4 lT = mesh->GetNode()->GetGeometricTranslation(FbxNode::eSourcePivot);
		const FbxVector4 lR = mesh->GetNode()->GetGeometricRotation(FbxNode::eSourcePivot);
		const FbxVector4 lS = mesh->GetNode()->GetGeometricScaling(FbxNode::eSourcePivot);

		matrixGeo.SetT(lT);
		matrixGeo.SetR(lR);
		matrixGeo.SetS(lS);

		FbxAMatrix matrixL2W;
		matrixL2W.SetIdentity();

		matrixL2W = mesh->GetNode()->EvaluateGlobalTransform();

		matrixL2W *= matrixGeo;

		XMMATRIX globalTransform = XMLoadFloat4x4(&fbxMeshData->globalTransform);
		FbxMatrixToXMMATRIX(globalTransform, matrixL2W);
		XMStoreFloat4x4(&fbxMeshData->globalTransform, globalTransform);

		// 读取顶点。
		ReadVertices(fbxMeshData);
		// 读取索引。
		ReadIndices(fbxMeshData);

		// 先读取网格对应的材质索引信息,以便优化稍后纹理读取。
		// 一个网格可能只对应一个materialId,也可能对应多个materialId(3dsmax里的Multi/Sub-Object材质)。
		// 如果只对应一个材质,简单的读取就行,不过普遍情况可能是为了优化渲染合并mesh从而拥有多材质。
		// 这个函数调用完毕我们会得到materialId和拥有这个materialId的三角形列表(三角形编号列表),保存在vector<MaterialIdOffset>的容器中。
		//struct Material
		//{
		//	Material() {}
		//	Material(int id, string diffuse, string normalMap)
		//		: materialId(id),
		//		diffuseTextureFile(diffuse),
		//		normalMapTextureFile(normalMap)
		//	{}
		//
		//	int materialId;
		//	string diffuseTextureFile;
		//	string normalMapTextureFile;
		//};
		// struct MaterialIdOffset
		//{
		//	MaterialIdOffset()
		//		: polygonCount(0)
		//	{}
		//	int polygonCount;
		//	Material material;
		//};
		ConnectMaterialsToMesh(mesh, fbxMeshData->mTrianglesCount);

		// 根据ConnectMaterialsToMesh得到的信息读取材质纹理信息,同样存入vector<MaterialIdOffset>容器。
		LoadMaterials(fbxMeshData);

		int triangleCount = mesh->GetPolygonCount();
		int controlPointIndex = 0;
		int normalIndex = 0;
		fbxMeshData->mUVs.resize(fbxMeshData->mIndicesCount, XMFLOAT2(-1.0f, -1.0f));

		// Extract normals and uvs from FbxMesh.
		for (int i = 0; i < triangleCount; i++)
		{
			int polygonSize = mesh->GetPolygonSize(i);

			for (int j = 0; j < polygonSize; j++)
			{ 
				controlPointIndex = mesh->GetPolygonVertex(i, j);

				ReadNormals(fbxMeshData, controlPointIndex, normalIndex);

				// 有纹理我们才读取uv,tangent以及binormal。
				if (fbxMeshData->hasDiffuseTexture())
				{
					ReadUVs(fbxMeshData, controlPointIndex, normalIndex, mesh->GetTextureUVIndex(i, j), 0);
					ReadTangents(fbxMeshData, controlPointIndex, normalIndex);
					ReadBinormals(fbxMeshData, controlPointIndex, normalIndex);
				}

				normalIndex++;
			}
		}

		SplitVertexByNormal(fbxMeshData);

		if (fbxMeshData->hasDiffuseTexture())
		{
			SplitVertexByUV(fbxMeshData);
		}
		else
		{
			fbxMeshData->mUVs.resize(fbxMeshData->mVerticesCount);
		}

		if (fbxMeshData->hasNormalMapTexture())
		{
			SplitVertexByTangent(fbxMeshData);
			SplitVertexByBinormal(fbxMeshData);
		}
		else
		{
			fbxMeshData->mTangents.resize(fbxMeshData->mVerticesCount);
			fbxMeshData->mBinormals.resize(fbxMeshData->mVerticesCount);
		}

		// 如果.fbx包含一个以上的mesh,需要计算当前FBXMeshData的索引在全局索引中的位置。
		for (int i = 0; i < fbxMeshData->mIndicesCount; i++)
		{
			fbxMeshData->mIndices[i] = fbxMeshData->mIndices[i] + verticesIndexOffset;
		}

		mMeshData->verticesCount += fbxMeshData->mVerticesCount;
		mMeshData->indicesCount += fbxMeshData->mIndicesCount;
		mMeshData->meshesCount++;

		// 多材质的情况。
		// 根据之前填充的materialIdOffsets容器保存的materialId和三角形的对应关系,
		// 计算每个RenderPackage渲染所需的索引数量和索引起始位置(偏移)。
		if (isByPolygon && fbxMeshData->hasDiffuseTexture())
		{
			vector<MaterialIdOffset> materialIdOffsets = mMeshData->materialIdOffsets;

			for (int i = 0; i < materialIdOffsets.size(); i++)
			{
				RenderPackage renderPacakge;
				renderPacakge.globalTransform = fbxMeshData->globalTransform;
				renderPacakge.indicesCount = materialIdOffsets[i].polygonCount * 3;
	
				if (i == 0)
				{
					renderPacakge.indicesOffset = indicesIndexOffset;
				}
				else
				{
					renderPacakge.indicesOffset += indicesIndexOffset;
				}

				renderPacakge.material = materialIdOffsets[i].material;

				mMeshData->renderPackages.push_back(renderPacakge);

				indicesIndexOffset += renderPacakge.indicesCount;
			}
		}
		else
		// 单一材质的情况。
		{
			RenderPackage renderPackage;
			renderPackage.indicesCount = fbxMeshData->mIndicesCount;
			renderPackage.indicesOffset = indicesIndexOffset;
			renderPackage.material = fbxMeshData->mMaterial;
			renderPackage.globalTransform = fbxMeshData->globalTransform;

			mMeshData->renderPackages.push_back(renderPackage);

			indicesIndexOffset += fbxMeshData->mIndices.size();
		}

		verticesIndexOffset += fbxMeshData->mVertices.size();

		// 将当前mesh的数据追加到全局数据容器。
		Merge(mMeshData->vertices, fbxMeshData->mVertices);
		Merge(mMeshData->indices, fbxMeshData->mIndices);
		Merge(mMeshData->normals, fbxMeshData->mNormals);
		Merge(mMeshData->uvs, fbxMeshData->mUVs);
		Merge(mMeshData->tangents, fbxMeshData->mTangents);
		Merge(mMeshData->binormals, fbxMeshData->mBinormals);

		mMeshData->materialIdOffsets.clear();
	}

	clear();

	return mMeshData;
}
Exemple #5
0
//--------------------------------------------------------------------------
void SaveMesh(FbxNode* pNode, const VeDirectoryPtr& spDest) noexcept
{
	Mesh kMesh;
	FbxMesh* pMesh = (FbxMesh*)pNode->GetNodeAttribute();
	
	kMesh.m_kName = pNode->GetName();
	kMesh.m_stFaces = pMesh->GetPolygonCount();
	kMesh.m_stVerts = kMesh.m_stFaces * 3;

	kMesh.m_kIndices.resize(kMesh.m_stVerts);
	kMesh.m_kPosition.resize(kMesh.m_stVerts);

	kMesh.m_kNormals.resize(pMesh->GetElementNormalCount());
	for (auto& v : kMesh.m_kNormals)
	{
		v.resize(kMesh.m_stVerts);
	}
	kMesh.m_kTexcoords.resize(pMesh->GetElementUVCount());
	for (auto& v : kMesh.m_kTexcoords)
	{
		v.resize(kMesh.m_stVerts);
	}
	kMesh.m_kColors.resize(pMesh->GetElementVertexColorCount());
	for (auto& v : kMesh.m_kColors)
	{
		v.resize(kMesh.m_stVerts);
	}	

	int element_mat = -1;
	for (int i(0); i < pMesh->GetElementMaterialCount(); ++i)
	{
		FbxGeometryElementMaterial* lMaterialElement = pMesh->GetElementMaterial(i);
		if (lMaterialElement->GetMappingMode() == FbxGeometryElement::eByPolygon)
		{
			element_mat = i;
			break;
		}
	}
	if (element_mat >= 0)
	{
		kMesh.m_kAttributes.resize(kMesh.m_stFaces);
	}

	FbxVector4* lControlPoints = pMesh->GetControlPoints();
	for (int i(0); i < (int)(kMesh.m_stFaces); ++i)
	{
		int lPolygonSize = pMesh->GetPolygonSize(i);
		VE_ASSERT_ALWAYS(lPolygonSize == 3);
		for (int j(0); j < lPolygonSize; ++j)
		{
			uint32_t u32Index = i * 3 + j;
			kMesh.m_kIndices[u32Index] = u32Index;
			int lControlPointIndex = pMesh->GetPolygonVertex(i, j);
			auto& pos = kMesh.m_kPosition[u32Index];
			pos.x = (float)lControlPoints[lControlPointIndex][0];
			pos.y = (float)lControlPoints[lControlPointIndex][1];
			pos.z = (float)lControlPoints[lControlPointIndex][2];
			
			for (int k(0); k < (int)(kMesh.m_kColors.size()); ++k)
			{
				FbxColor c;
				FbxGeometryElementVertexColor* leVtxc = pMesh->GetElementVertexColor(k);
				switch (leVtxc->GetMappingMode())
				{
				default:
					break;
				case FbxGeometryElement::eByControlPoint:
					switch (leVtxc->GetReferenceMode())
					{
					case FbxGeometryElement::eDirect:
						c = leVtxc->GetDirectArray().GetAt(lControlPointIndex);
						break;
					case FbxGeometryElement::eIndexToDirect:
					{
						int id = leVtxc->GetIndexArray().GetAt(lControlPointIndex);
						c = leVtxc->GetDirectArray().GetAt(id);
					}
					break;
					default:
						break; // other reference modes not shown here!
					}
					break;

				case FbxGeometryElement::eByPolygonVertex:
				{
					switch (leVtxc->GetReferenceMode())
					{
					case FbxGeometryElement::eDirect:
						c = leVtxc->GetDirectArray().GetAt(u32Index);
						break;
					case FbxGeometryElement::eIndexToDirect:
					{
						int id = leVtxc->GetIndexArray().GetAt(u32Index);
						c = leVtxc->GetDirectArray().GetAt(id);
					}
					break;
					default:
						break; // other reference modes not shown here!
					}
				}
				break;
				case FbxGeometryElement::eByPolygon: // doesn't make much sense for UVs
				case FbxGeometryElement::eAllSame:   // doesn't make much sense for UVs
				case FbxGeometryElement::eNone:       // doesn't make much sense for UVs
					break;
				}
				auto& color = kMesh.m_kColors[k][u32Index];
				color.x = (float)c[0];
				color.y = (float)c[1];
				color.z = (float)c[2];
				color.w = (float)c[3];
			}

			for (int k(0); k < (int)(kMesh.m_kTexcoords.size()); ++k)
			{
				FbxVector2 uv;
				FbxGeometryElementUV* leUV = pMesh->GetElementUV(k);
				switch (leUV->GetMappingMode())
				{
				default:
					break;
				case FbxGeometryElement::eByControlPoint:
					switch (leUV->GetReferenceMode())
					{
					case FbxGeometryElement::eDirect:
						uv = leUV->GetDirectArray().GetAt(lControlPointIndex);
						break;
					case FbxGeometryElement::eIndexToDirect:
					{
						int id = leUV->GetIndexArray().GetAt(lControlPointIndex);
						uv = leUV->GetDirectArray().GetAt(id);
					}
					break;
					default:
						break; // other reference modes not shown here!
					}
					break;

				case FbxGeometryElement::eByPolygonVertex:
				{
					int lTextureUVIndex = pMesh->GetTextureUVIndex(i, j);
					switch (leUV->GetReferenceMode())
					{
					case FbxGeometryElement::eDirect:
					case FbxGeometryElement::eIndexToDirect:
					{
						uv = leUV->GetDirectArray().GetAt(lTextureUVIndex);
					}
					break;
					default:
						break; // other reference modes not shown here!
					}
				}
				break;

				case FbxGeometryElement::eByPolygon: // doesn't make much sense for UVs
				case FbxGeometryElement::eAllSame:   // doesn't make much sense for UVs
				case FbxGeometryElement::eNone:       // doesn't make much sense for UVs
					break;
				}

				auto& texcoord = kMesh.m_kTexcoords[k][u32Index];
				texcoord.x = (float)uv[0];
				texcoord.y = (float)uv[1];
			}
			
			for (int k(0); k < (int)(kMesh.m_kNormals.size()); ++k)
			{
				FbxVector4 n;
				FbxGeometryElementNormal* leNormal = pMesh->GetElementNormal(k);
				if (leNormal->GetMappingMode() == FbxGeometryElement::eByPolygonVertex)
				{
					switch (leNormal->GetReferenceMode())
					{
					case FbxGeometryElement::eDirect:
						n = leNormal->GetDirectArray().GetAt(u32Index);
						break;
					case FbxGeometryElement::eIndexToDirect:
					{
						int id = leNormal->GetIndexArray().GetAt(u32Index);
						n = leNormal->GetDirectArray().GetAt(id);
					}
					break;
					default:
						break; // other reference modes not shown here!
					}
				}

				auto& normal = kMesh.m_kNormals[k][u32Index];
				normal.x = (float)n[0];
				normal.y = (float)n[1];
				normal.z = (float)n[2];
			}

			if (element_mat >= 0)
			{
				FbxGeometryElementMaterial* lMaterialElement = pMesh->GetElementMaterial(element_mat);
				FbxSurfaceMaterial* lMaterial = NULL;
				int lMatId = -1;
				lMaterial = pMesh->GetNode()->GetMaterial(lMaterialElement->GetIndexArray().GetAt(i));
				lMatId = lMaterialElement->GetIndexArray().GetAt(i);
				kMesh.m_kAttributes[i] = lMatId;
			}
		}
	}
	kMesh.Process();
	kMesh.Save(spDest);
}