Exemplo n.º 1
0
Material* FBXScene::GetMaterialLinkedWithPolygon(FbxMesh* pFBXMesh, int nLayerIndex, int nPolygonIndex, int nPolygonVertexIndex, int nVertexIndex)
{
	if( nLayerIndex < 0 || nLayerIndex > pFBXMesh->GetLayerCount() )
		return NULL;

	FbxNode* pNode = pFBXMesh->GetNode();

	if( !pNode )
		return NULL;

	FbxLayerElementMaterial* pFBXMaterial = pFBXMesh->GetLayer(nLayerIndex)->GetMaterials();

	if( pFBXMaterial )
	{
		int nMappingIndex = GetMappingIndex( pFBXMaterial->GetMappingMode(), nPolygonIndex, 0, nVertexIndex );

		if( nMappingIndex < 0 )
			return NULL;

		FbxLayerElement::EReferenceMode referenceMode = pFBXMaterial->GetReferenceMode();

		
		if( referenceMode == FbxLayerElement::EReferenceMode::eDirect )
		{
			if( nMappingIndex < pNode->GetMaterialCount() )
			{
				return GetMaterial(pNode->GetMaterial(nMappingIndex));
			}
		}
		else if( referenceMode == FbxLayerElement::EReferenceMode::eIndexToDirect )
		{
			const FbxLayerElementArrayTemplate<int>& pMaterialIndexArray = pFBXMaterial->GetIndexArray();

			if( nMappingIndex < pMaterialIndexArray.GetCount() )
			{
				int nIndex = pMaterialIndexArray.GetAt(nMappingIndex);
				if( nIndex < pNode->GetMaterialCount() )
				{
					return GetMaterial(pNode->GetMaterial(nIndex));
				}
			}
		}
	}
	
	return NULL;
}
Exemplo n.º 2
0
void Model::processMeshTextCoords(FbxMesh *mesh, Vertex *verts, int numVerts)
{
	int matCount = mesh->GetElementMaterialCount();
	FbxLayerElementMaterial *matElem = NULL;
	if (matCount) //only supporting 1 material layer max
		matElem = mesh->GetElementMaterial(0);
	int polCount = mesh->GetPolygonCount();
	for (int polInd = 0; polInd < polCount; polInd++)
	{
		int textureId = 0;
		if (matElem)
		{
			textureId = matElem->GetIndexArray().GetAt(polInd);
		}
		for (unsigned polVert = 0; polVert < 3; polVert++)
		{
			int cornerIndex = mesh->GetPolygonVertex(polInd, polVert);
			FbxVector2 UV = FbxVector2(0, 0);
			FbxLayer *layer = mesh->GetLayer(0);
			FbxLayerElementUV *layerUV = layer->GetUVs();
			FbxLayerElementTexture *layerTexture = layer->GetTextures(FbxLayerElement::eTextureDiffuse);
			if (layerUV)
			{
				int UVindex = 0;
				switch (layerUV->GetMappingMode())
				{
				case FbxLayerElement::eByControlPoint:
					UVindex = cornerIndex;
					break;
				case FbxLayerElement::eByPolygonVertex:
					UVindex = mesh->GetTextureUVIndex(polInd, polVert, FbxLayerElement::eTextureDiffuse);
					break;
				case FbxLayerElement::eByPolygon:
					UVindex = polInd;
					break;
				}

				UV = layerUV->GetDirectArray().GetAt(UVindex);
				verts[cornerIndex].color.x = textureId;
				verts[cornerIndex].texture.x = UV[0];
				verts[cornerIndex].texture.y = 1.f - UV[1];
			}
		}
	}
}
/**
 * Adds an Fbx Mesh to the FBX scene based on the data in the given FStaticLODModel
 */
FbxNode* FFbxExporter::CreateMesh(const USkeletalMesh* SkelMesh, const TCHAR* MeshName)
{
	const FSkeletalMeshResource* SkelMeshResource = SkelMesh->GetImportedResource();
	const FStaticLODModel& SourceModel = SkelMeshResource->LODModels[0];
	const int32 VertexCount = SourceModel.NumVertices;

	// Verify the integrity of the mesh.
	if (VertexCount == 0) return NULL;

	// Copy all the vertex data from the various chunks to a single buffer.
	// Makes the rest of the code in this function cleaner and easier to maintain.  
	TArray<FSoftSkinVertex> Vertices;
	SourceModel.GetVertices(Vertices);
	if (Vertices.Num() != VertexCount) return NULL;

	FbxMesh* Mesh = FbxMesh::Create(Scene, TCHAR_TO_UTF8(MeshName));

	// Create and fill in the vertex position data source.
	Mesh->InitControlPoints(VertexCount);
	FbxVector4* ControlPoints = Mesh->GetControlPoints();
	for (int32 VertIndex = 0; VertIndex < VertexCount; ++VertIndex)
	{
		FVector Position			= Vertices[VertIndex].Position;
		ControlPoints[VertIndex]	= Converter.ConvertToFbxPos(Position);
	}

	// Create Layer 0 to hold the normals
	FbxLayer* LayerZero = Mesh->GetLayer(0);
	if (LayerZero == NULL)
	{
		Mesh->CreateLayer();
		LayerZero = Mesh->GetLayer(0);
	}

	// Create and fill in the per-face-vertex normal data source.
	// We extract the Z-tangent and drop the X/Y-tangents which are also stored in the render mesh.
	FbxLayerElementNormal* LayerElementNormal= FbxLayerElementNormal::Create(Mesh, "");

	LayerElementNormal->SetMappingMode(FbxLayerElement::eByControlPoint);
	// Set the normal values for every control point.
	LayerElementNormal->SetReferenceMode(FbxLayerElement::eDirect);

	for (int32 VertIndex = 0; VertIndex < VertexCount; ++VertIndex)
	{
		FVector Normal			= Vertices[VertIndex].TangentZ;
		FbxVector4 FbxNormal	= Converter.ConvertToFbxPos(Normal);

		LayerElementNormal->GetDirectArray().Add(FbxNormal);
	}

	LayerZero->SetNormals(LayerElementNormal);


	// Create and fill in the per-face-vertex texture coordinate data source(s).
	// Create UV for Diffuse channel.
	const int32 TexCoordSourceCount = SourceModel.NumTexCoords;
	TCHAR UVChannelName[32];
	for (int32 TexCoordSourceIndex = 0; TexCoordSourceIndex < TexCoordSourceCount; ++TexCoordSourceIndex)
	{
		FbxLayer* Layer = Mesh->GetLayer(TexCoordSourceIndex);
		if (Layer == NULL)
		{
			Mesh->CreateLayer();
			Layer = Mesh->GetLayer(TexCoordSourceIndex);
		}

		if (TexCoordSourceIndex == 1)
		{
			FCString::Sprintf(UVChannelName, TEXT("LightMapUV"));
		}
		else
		{
			FCString::Sprintf(UVChannelName, TEXT("DiffuseUV"));
		}

		FbxLayerElementUV* UVDiffuseLayer = FbxLayerElementUV::Create(Mesh, TCHAR_TO_UTF8(UVChannelName));
		UVDiffuseLayer->SetMappingMode(FbxLayerElement::eByControlPoint);
		UVDiffuseLayer->SetReferenceMode(FbxLayerElement::eDirect);

		// Create the texture coordinate data source.
		for (int32 TexCoordIndex = 0; TexCoordIndex < VertexCount; ++TexCoordIndex)
		{
			const FVector2D& TexCoord = Vertices[TexCoordIndex].UVs[TexCoordSourceIndex];
			UVDiffuseLayer->GetDirectArray().Add(FbxVector2(TexCoord.X, -TexCoord.Y + 1.0));
		}

		Layer->SetUVs(UVDiffuseLayer, FbxLayerElement::eTextureDiffuse);
	}

	FbxLayerElementMaterial* MatLayer = FbxLayerElementMaterial::Create(Mesh, "");
	MatLayer->SetMappingMode(FbxLayerElement::eByPolygon);
	MatLayer->SetReferenceMode(FbxLayerElement::eIndexToDirect);
	LayerZero->SetMaterials(MatLayer);


	// Create the per-material polygons sets.
	TArray<uint32> Indices;
	SourceModel.MultiSizeIndexContainer.GetIndexBuffer(Indices);

	int32 SectionCount = SourceModel.Sections.Num();
	for (int32 SectionIndex = 0; SectionIndex < SectionCount; ++SectionIndex)
	{
		const FSkelMeshSection& Section = SourceModel.Sections[SectionIndex];

		int32 MatIndex = Section.MaterialIndex;

		// Static meshes contain one triangle list per element.
		int32 TriangleCount = Section.NumTriangles;

		// Copy over the index buffer into the FBX polygons set.
		for (int32 TriangleIndex = 0; TriangleIndex < TriangleCount; ++TriangleIndex)
		{
			Mesh->BeginPolygon(MatIndex);
			for (int32 PointIndex = 0; PointIndex < 3; PointIndex++)
			{
				Mesh->AddPolygon(Indices[Section.BaseIndex + ((TriangleIndex * 3) + PointIndex)]);
			}
			Mesh->EndPolygon();
		}
	}

	// Create and fill in the vertex color data source.
	FbxLayerElementVertexColor* VertexColor = FbxLayerElementVertexColor::Create(Mesh, "");
	VertexColor->SetMappingMode(FbxLayerElement::eByControlPoint);
	VertexColor->SetReferenceMode(FbxLayerElement::eDirect);
	FbxLayerElementArrayTemplate<FbxColor>& VertexColorArray = VertexColor->GetDirectArray();
	LayerZero->SetVertexColors(VertexColor);

	for (int32 VertIndex = 0; VertIndex < VertexCount; ++VertIndex)
	{
		FLinearColor VertColor = Vertices[VertIndex].Color.ReinterpretAsLinear();
		VertexColorArray.Add( FbxColor(VertColor.R, VertColor.G, VertColor.B, VertColor.A ));
	}

	FbxNode* MeshNode = FbxNode::Create(Scene, TCHAR_TO_UTF8(MeshName));
	MeshNode->SetNodeAttribute(Mesh);



	// Add the materials for the mesh
	int32 MaterialCount = SkelMesh->Materials.Num();

	for(int32 MaterialIndex = 0; MaterialIndex < MaterialCount; ++MaterialIndex)
	{
		UMaterialInterface* MatInterface = SkelMesh->Materials[MaterialIndex].MaterialInterface;

		FbxSurfaceMaterial* FbxMaterial = NULL;
		if(MatInterface && !FbxMaterials.Find(MatInterface))
		{
			FbxMaterial = ExportMaterial(MatInterface);
		}
		else
		{
			// Note: The vertex data relies on there being a set number of Materials.  
			// If you try to add the same material again it will not be added, so create a 
			// default material with a unique name to ensure the proper number of materials

			TCHAR NewMaterialName[MAX_SPRINTF]=TEXT("");
			FCString::Sprintf( NewMaterialName, TEXT("Fbx Default Material %i"), MaterialIndex );

			FbxMaterial = FbxSurfaceLambert::Create(Scene, TCHAR_TO_UTF8(NewMaterialName));
			((FbxSurfaceLambert*)FbxMaterial)->Diffuse.Set(FbxDouble3(0.72, 0.72, 0.72));
		}

		MeshNode->AddMaterial(FbxMaterial);
	}

	int32 SavedMaterialCount = MeshNode->GetMaterialCount();
	check(SavedMaterialCount == MaterialCount);

	return MeshNode;
}
Exemplo n.º 4
0
void ParseMesh( FbxNode* pNode, FbxMesh* pFbxMesh, ExportFrame* pParentFrame, bool bSubDProcess, const CHAR* strSuffix )
{
    if( !g_pScene->Settings().bExportMeshes )
        return;

    if( !pNode || !pFbxMesh )
        return;

    const CHAR* strName = pFbxMesh->GetName();
    if( !strName || strName[0] == '\0' )
        strName = pParentFrame->GetName().SafeString();

    if( !strSuffix )
    {
        strSuffix = "";
    }
    CHAR strDecoratedName[512];
    sprintf_s( strDecoratedName, "%s_%s%s", g_pScene->Settings().strMeshNameDecoration, strName, strSuffix );
    ExportMesh* pMesh = new ExportMesh( strDecoratedName );
    pMesh->SetDCCObject( pFbxMesh );

    bool bSmoothMesh = false;

    auto Smoothness = pFbxMesh->GetMeshSmoothness();
    if( Smoothness != FbxMesh::eHull && g_pScene->Settings().bConvertMeshesToSubD )
    {
        bSubDProcess = true;
        bSmoothMesh = true;
    }

    ExportLog::LogMsg( 2, "Parsing %s mesh \"%s\", renamed to \"%s\"", bSmoothMesh ? "smooth" : "poly", strName, strDecoratedName );

    SkinData skindata;
    bool bSkinnedMesh = ParseMeshSkinning( pFbxMesh, &skindata );
    if( bSkinnedMesh )
    {
        DWORD dwBoneCount = skindata.GetBoneCount();
        for( DWORD i = 0; i < dwBoneCount; ++i )
        {
            pMesh->AddInfluence( skindata.InfluenceNodes[i]->GetName() );
        }
    }

    bool bExportColors = g_pScene->Settings().bExportColors;
    pMesh->SetVertexColorCount( 0 );

    // Vertex normals and tangent spaces
    if( !g_pScene->Settings().bExportNormals )
    {
        pMesh->SetVertexNormalCount( 0 );
    }
    else if( g_pScene->Settings().bComputeVertexTangentSpace )
    {
        if( g_pScene->Settings().bExportBinormal )
            pMesh->SetVertexNormalCount( 3 );
        else
            pMesh->SetVertexNormalCount( 2 );
    }
    else
    {
        pMesh->SetVertexNormalCount( 1 );
    }

    DWORD dwLayerCount = pFbxMesh->GetLayerCount();
    ExportLog::LogMsg( 4, "%u layers in FBX mesh", dwLayerCount );

    if (!dwLayerCount || !pFbxMesh->GetLayer(0)->GetNormals())
    {
        ExportLog::LogMsg( 4, "Generating normals..." );
        pFbxMesh->InitNormals();
#if (FBXSDK_VERSION_MAJOR >= 2015)
        pFbxMesh->GenerateNormals();
#else
        pFbxMesh->ComputeVertexNormals();
#endif
    }

    DWORD dwVertexColorCount = 0;
    FbxLayerElementVertexColor* pVertexColorSet = nullptr;
    DWORD dwUVSetCount = 0;
    FbxLayerElementMaterial* pMaterialSet = nullptr;
    std::vector<FbxLayerElementUV*> VertexUVSets;
    for( DWORD dwLayerIndex = 0; dwLayerIndex < dwLayerCount; ++dwLayerIndex )
    {
        if( pFbxMesh->GetLayer(dwLayerIndex)->GetVertexColors() && bExportColors )
        {
            if( dwVertexColorCount == 0 )
            {
                dwVertexColorCount++;
                pVertexColorSet = pFbxMesh->GetLayer(dwLayerIndex)->GetVertexColors();
            }
            else
            {
                ExportLog::LogWarning( "Only one vertex color set is allowed; ignoring additional vertex color sets." );
            }
        }
        if( pFbxMesh->GetLayer(dwLayerIndex)->GetUVs() )
        {
            dwUVSetCount++;
            VertexUVSets.push_back( pFbxMesh->GetLayer(dwLayerIndex)->GetUVs() );
        }
        if( pFbxMesh->GetLayer(dwLayerIndex)->GetMaterials() )
        {
            if( pMaterialSet )
            {
                ExportLog::LogWarning( "Multiple material layers detected on mesh %s.  Some will be ignored.", pMesh->GetName().SafeString() );
            }
            pMaterialSet = pFbxMesh->GetLayer(dwLayerIndex)->GetMaterials();
        }
    }

    std::vector<ExportMaterial*> MaterialList;
    for( int dwMaterial = 0; dwMaterial < pNode->GetMaterialCount(); ++dwMaterial )
    {
        auto pMat = pNode->GetMaterial( dwMaterial );
        if ( !pMat )
            continue;

        auto pMaterial = ParseMaterial( pMat );
        MaterialList.push_back( pMaterial );
    }

    ExportLog::LogMsg( 4, "Found %u UV sets", dwUVSetCount );
    dwUVSetCount = std::min<DWORD>( dwUVSetCount, g_pScene->Settings().iMaxUVSetCount );
    ExportLog::LogMsg( 4, "Using %u UV sets", dwUVSetCount );

    pMesh->SetVertexColorCount( dwVertexColorCount );
    pMesh->SetVertexUVCount( dwUVSetCount );
    // TODO: Does FBX only support 2D texture coordinates?
    pMesh->SetVertexUVDimension( 2 );

    DWORD dwMeshOptimizationFlags = 0;
    if( g_pScene->Settings().bCompressVertexData )
        dwMeshOptimizationFlags |= ExportMesh::COMPRESS_VERTEX_DATA;

    DWORD dwPolyCount = pFbxMesh->GetPolygonCount();
    // Assume that polys are usually quads.
    g_MeshTriangleAllocator.SetSizeHint( dwPolyCount * 2 );

    DWORD dwVertexCount = pFbxMesh->GetControlPointsCount();
    auto pVertexPositions = pFbxMesh->GetControlPoints();

    if( bSkinnedMesh )
    {
        assert( skindata.dwVertexCount == dwVertexCount );
    }
    
    ExportLog::LogMsg( 4, "%u vertices, %u polygons", dwVertexCount, dwPolyCount );

    DWORD dwNonConformingSubDPolys = 0;

    // Compute total transformation
    FbxAMatrix vertMatrix;
    FbxAMatrix normMatrix;
    {
        auto trans = pNode->GetGeometricTranslation( FbxNode::eSourcePivot );
        auto rot = pNode->GetGeometricRotation( FbxNode::eSourcePivot );
        auto scale = pNode->GetGeometricScaling( FbxNode::eSourcePivot );

        FbxAMatrix geom;
        geom.SetT( trans );
        geom.SetR( rot );
        geom.SetS( scale );

        if ( g_pScene->Settings().bExportAnimations || !g_pScene->Settings().bApplyGlobalTrans )
        {
            vertMatrix = geom;
        }
        else
        {
            auto global = pNode->EvaluateGlobalTransform();
            vertMatrix = global * geom;
        }

        // Calculate the normal transform matrix (inverse-transpose)
        normMatrix = vertMatrix;
        normMatrix = normMatrix.Inverse();
        normMatrix = normMatrix.Transpose();
    }

    const bool bInvertTexVCoord = g_pScene->Settings().bInvertTexVCoord;
    
    // Loop over polygons.
    DWORD basePolyIndex = 0;
    for( DWORD dwPolyIndex = 0; dwPolyIndex < dwPolyCount; ++dwPolyIndex )
    {
        // Triangulate each polygon into one or more triangles.
        DWORD dwPolySize = pFbxMesh->GetPolygonSize( dwPolyIndex );
        assert( dwPolySize >= 3 );
        DWORD dwTriangleCount = dwPolySize - 2;
        assert( dwTriangleCount > 0 );

        if( dwPolySize > 4 )
        {
            ++dwNonConformingSubDPolys;
        }

        DWORD dwMaterialIndex = 0;
        if( pMaterialSet )
        {
            switch( pMaterialSet->GetMappingMode() )
            {
            case FbxLayerElement::eByPolygon:
                switch( pMaterialSet->GetReferenceMode() )
                {
                case FbxLayerElement::eDirect:
                    dwMaterialIndex = dwPolyIndex;
                    break;
                case FbxLayerElement::eIndex:
                case FbxLayerElement::eIndexToDirect:
                    dwMaterialIndex = pMaterialSet->GetIndexArray().GetAt( dwPolyIndex );
                    break;
                }
            }
        }

        DWORD dwCornerIndices[3];
        // Loop over triangles in the polygon.
        for( DWORD dwTriangleIndex = 0; dwTriangleIndex < dwTriangleCount; ++dwTriangleIndex )
        {
            dwCornerIndices[0] = pFbxMesh->GetPolygonVertex( dwPolyIndex, 0 );
            dwCornerIndices[1] = pFbxMesh->GetPolygonVertex( dwPolyIndex, dwTriangleIndex + 1 );
            dwCornerIndices[2] = pFbxMesh->GetPolygonVertex( dwPolyIndex, dwTriangleIndex + 2 );

            //ExportLog::LogMsg( 4, "Poly %d Triangle %d: %d %d %d", dwPolyIndex, dwTriangleIndex, dwCornerIndices[0], dwCornerIndices[1], dwCornerIndices[2] );

            FbxVector4 vNormals[3];
            ZeroMemory( vNormals, 3 * sizeof(FbxVector4) );
            INT iPolyIndex = static_cast<INT>( dwPolyIndex );
            INT iVertIndex[3] = { 0, static_cast<INT>( dwTriangleIndex + 1 ), static_cast<INT>( dwTriangleIndex + 2 ) };
            pFbxMesh->GetPolygonVertexNormal( iPolyIndex, iVertIndex[0], vNormals[0] );
            pFbxMesh->GetPolygonVertexNormal( iPolyIndex, iVertIndex[1], vNormals[1] );
            pFbxMesh->GetPolygonVertexNormal( iPolyIndex, iVertIndex[2], vNormals[2] );

            // Build the raw triangle.
            auto pTriangle = g_MeshTriangleAllocator.GetNewTriangle();

            // Store polygon index
            pTriangle->PolygonIndex = static_cast<INT>( dwPolyIndex );

            // Store material subset index
            pTriangle->SubsetIndex = dwMaterialIndex;

            for( DWORD dwCornerIndex = 0; dwCornerIndex < 3; ++dwCornerIndex )
            {
                const DWORD& dwDCCIndex = dwCornerIndices[dwCornerIndex];
                // Store DCC vertex index (this helps the mesh reduction/VB generation code)
                pTriangle->Vertex[dwCornerIndex].DCCVertexIndex = dwDCCIndex;

                // Store vertex position
                auto finalPos = vertMatrix.MultT( pVertexPositions[dwDCCIndex] );

                pTriangle->Vertex[dwCornerIndex].Position.x = (float)finalPos.mData[0];
                pTriangle->Vertex[dwCornerIndex].Position.y = (float)finalPos.mData[1];
                pTriangle->Vertex[dwCornerIndex].Position.z = (float)finalPos.mData[2];

                // Store vertex normal
                auto finalNorm = vNormals[dwCornerIndex];
                finalNorm.mData[3] = 0.0;
                finalNorm = normMatrix.MultT( finalNorm );
                finalNorm.Normalize();

                pTriangle->Vertex[dwCornerIndex].Normal.x = (float)finalNorm.mData[0];
                pTriangle->Vertex[dwCornerIndex].Normal.y = (float)finalNorm.mData[1];
                pTriangle->Vertex[dwCornerIndex].Normal.z = (float)finalNorm.mData[2];

                // Store UV sets
                for( DWORD dwUVIndex = 0; dwUVIndex < dwUVSetCount; ++dwUVIndex )
                {
                    // Crack apart the FBX dereferencing system for UV coordinates
                    FbxLayerElementUV* pUVSet = VertexUVSets[dwUVIndex];
                    FbxVector2 Value( 0, 0 );
                    switch( pUVSet->GetMappingMode() )
                    {
                    case FbxLayerElement::eByControlPoint:
                        switch (pUVSet->GetReferenceMode())
                        {
                        case FbxLayerElement::eDirect:
                            Value = pUVSet->GetDirectArray().GetAt(dwDCCIndex);
                            break;

                        case FbxLayerElement::eIndex:
                        case FbxLayerElement::eIndexToDirect:
                            {
                                int iUVIndex = pUVSet->GetIndexArray().GetAt(dwDCCIndex);
                                Value = pUVSet->GetDirectArray().GetAt(iUVIndex);
                            }
                            break;
                        }
                        break;

                    case FbxLayerElement::eByPolygonVertex:
                        switch (pUVSet->GetReferenceMode())
                        {
                        case FbxLayerElement::eDirect:
                            Value = pUVSet->GetDirectArray().GetAt( basePolyIndex + iVertIndex[dwCornerIndex] );
                            break;

                        case FbxLayerElement::eIndex:
                        case FbxLayerElement::eIndexToDirect:
                            {
                                int iUVIndex = pUVSet->GetIndexArray().GetAt( basePolyIndex + iVertIndex[dwCornerIndex] );
#ifdef _DEBUG
                                if (!dwUVIndex)
                                {
                                    // Warning: pFbxMesh->GetTextureUVIndex only works for the first layer of the mesh
                                    int iUVIndex2 = pFbxMesh->GetTextureUVIndex(iPolyIndex, iVertIndex[dwCornerIndex]);
                                    assert(iUVIndex == iUVIndex2);
                                }
#endif
                                Value = pUVSet->GetDirectArray().GetAt( iUVIndex );
                            }
                            break;
                        }
                        break;
                    }

                    // Store a single UV set
                    pTriangle->Vertex[dwCornerIndex].TexCoords[dwUVIndex].x = (float)Value.mData[0];
                    if( bInvertTexVCoord )
                    {
                        pTriangle->Vertex[dwCornerIndex].TexCoords[dwUVIndex].y = 1.0f - (float) Value.mData[1];
                    }
                    else
                    {
                        pTriangle->Vertex[dwCornerIndex].TexCoords[dwUVIndex].y = (float)Value.mData[1];
                    }
                }

                // Store vertex color set
                if( dwVertexColorCount > 0 && pVertexColorSet )
                {
                    // Crack apart the FBX dereferencing system for Color coordinates
                    FbxColor Value( 1, 1, 1, 1 );
                    switch( pVertexColorSet->GetMappingMode() )
                    {
                    case FbxLayerElement::eByControlPoint:
                        switch( pVertexColorSet->GetReferenceMode() )
                        {
                        case FbxLayerElement::eDirect:
                            Value = pVertexColorSet->GetDirectArray().GetAt( dwDCCIndex );
                            break;
                        case FbxLayerElement::eIndex:
                        case FbxLayerElement::eIndexToDirect:
                            {
                                int iColorIndex = pVertexColorSet->GetIndexArray().GetAt(dwDCCIndex);
                                Value = pVertexColorSet->GetDirectArray().GetAt(iColorIndex);
                            }
                            break;
                        }
                        break;

                    case FbxLayerElement::eByPolygonVertex:
                        switch( pVertexColorSet->GetReferenceMode() )
                        {
                        case FbxLayerElement::eDirect:
                            Value = pVertexColorSet->GetDirectArray().GetAt( basePolyIndex + iVertIndex[dwCornerIndex] );
                            break;
                        case FbxLayerElement::eIndex:
                        case FbxLayerElement::eIndexToDirect:
                            {
                                int iColorIndex = pVertexColorSet->GetIndexArray().GetAt( basePolyIndex + iVertIndex[dwCornerIndex] );
                                Value = pVertexColorSet->GetDirectArray().GetAt(iColorIndex);
                            }
                            break;
                        }
                        break;
                    }

                    // Store a single vertex color set
                    pTriangle->Vertex[dwCornerIndex].Color.x = (float)Value.mRed;
                    pTriangle->Vertex[dwCornerIndex].Color.y = (float)Value.mGreen;
                    pTriangle->Vertex[dwCornerIndex].Color.z = (float)Value.mBlue;
                    pTriangle->Vertex[dwCornerIndex].Color.w = (float)Value.mAlpha;
                }

                // Store skin weights
                if( bSkinnedMesh )
                {
                    memcpy( &pTriangle->Vertex[dwCornerIndex].BoneIndices, skindata.GetIndices( dwDCCIndex ), sizeof(PackedVector::XMUBYTE4) );
                    memcpy( &pTriangle->Vertex[dwCornerIndex].BoneWeights, skindata.GetWeights( dwDCCIndex ), sizeof(XMFLOAT4) );
                }
            }

            // Add raw triangle to the mesh.
            pMesh->AddRawTriangle( pTriangle );
        }

        basePolyIndex += dwPolySize;
    }

    if( bSubDProcess )
    {
        dwMeshOptimizationFlags |= ExportMesh::FORCE_SUBD_CONVERSION;
    }

    if ( g_pScene->Settings().bCleanMeshes )
    {
        dwMeshOptimizationFlags |= ExportMesh::CLEAN_MESHES;
    }

    if ( g_pScene->Settings().bOptimizeVCache )
    {
        dwMeshOptimizationFlags |= ExportMesh::CLEAN_MESHES | ExportMesh::VCACHE_OPT;
    }

    pMesh->Optimize( dwMeshOptimizationFlags );

    ExportModel* pModel = new ExportModel( pMesh );
    size_t dwMaterialCount = MaterialList.size();
    if( !pMesh->GetSubDMesh() )
    {
        for( size_t dwSubset = 0; dwSubset < dwMaterialCount; ++dwSubset )
        {
            auto pMaterial = MaterialList[dwSubset];
            auto pSubset = pMesh->GetSubset( dwSubset );
            CHAR strUniqueSubsetName[100];
            sprintf_s( strUniqueSubsetName, "subset%Iu_%s", dwSubset, pMaterial->GetName().SafeString() );
            pSubset->SetName( strUniqueSubsetName );
            pModel->SetSubsetBinding( pSubset->GetName(), pMaterial );
        }
    }
    else
    {
        auto pSubDMesh = pMesh->GetSubDMesh();
        size_t dwSubsetCount = pSubDMesh->GetSubsetCount();
        for( size_t dwSubset = 0; dwSubset < dwSubsetCount; ++dwSubset )
        {
            auto pSubset = pSubDMesh->GetSubset( dwSubset );
            assert( pSubset != nullptr );
            assert( pSubset->iOriginalMeshSubset < static_cast<INT>( dwMaterialCount ) );
            auto pMaterial = MaterialList[pSubset->iOriginalMeshSubset];
            CHAR strUniqueSubsetName[100];
            sprintf_s( strUniqueSubsetName, "subset%Iu_%s", dwSubset, pMaterial->GetName().SafeString() );
            pSubset->Name = strUniqueSubsetName;
            pModel->SetSubsetBinding( pSubset->Name, pMaterial, true );
        }
    }

    if( bSubDProcess && ( dwNonConformingSubDPolys > 0 ) )
    {
        ExportLog::LogWarning( "Encountered %u polygons with 5 or more sides in mesh \"%s\", which were subdivided into quad and triangle patches.  Mesh appearance may have been affected.", dwNonConformingSubDPolys, pMesh->GetName().SafeString() );
    }

    // update statistics
    if( pMesh->GetSubDMesh() )
    {
        g_pScene->Statistics().SubDMeshesProcessed++;
        g_pScene->Statistics().SubDQuadsProcessed += pMesh->GetSubDMesh()->GetQuadPatchCount();
        g_pScene->Statistics().SubDTrisProcessed += pMesh->GetSubDMesh()->GetTrianglePatchCount();
    }
    else
    {
        g_pScene->Statistics().TrisExported += pMesh->GetIB()->GetIndexCount() / 3;
        g_pScene->Statistics().VertsExported += pMesh->GetVB()->GetVertexCount();
        g_pScene->Statistics().MeshesExported++;
    }

    pParentFrame->AddModel( pModel );
    g_pScene->AddMesh( pMesh );
}