VOID ParseMesh( KFbxMesh* pFbxMesh, ExportFrame* pParentFrame, BOOL bSubDProcess, const CHAR* strSuffix ) { if( !g_pScene->Settings().bExportMeshes ) return; if( pFbxMesh == NULL ) return; const CHAR* strName = pFbxMesh->GetName(); if( strName == NULL || strName[0] == '\0' ) strName = pParentFrame->GetName().SafeString(); if( strSuffix == NULL ) { 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; KFbxMesh::MeshSmoothness Smoothness = pFbxMesh->GetMeshSmoothness(); if( Smoothness != KFbxMesh::HULL && 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() ); } } pMesh->SetVertexColorCount( 0 ); KFbxLayerElementArrayTemplate<KFbxVector4> *pNormals = NULL; pFbxMesh->GetNormals( &pNormals ); if( pNormals == NULL ) { pFbxMesh->InitNormals(); pFbxMesh->ComputeVertexNormals(); } // 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, "%d layers in FBX mesh", dwLayerCount ); DWORD dwVertexColorCount = 0; KFbxLayerElementVertexColor* pVertexColorSet = NULL; DWORD dwUVSetCount = 0; std::vector<KFbxLayerElementUV*> VertexUVSets; KFbxLayerElementMaterial* pMaterialSet = NULL; std::vector<ExportMaterial*> MaterialList; for( DWORD dwLayerIndex = 0; dwLayerIndex < dwLayerCount; ++dwLayerIndex ) { if( pFbxMesh->GetLayer(dwLayerIndex)->GetVertexColors() != NULL ) { 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() != NULL ) { dwUVSetCount++; VertexUVSets.push_back( pFbxMesh->GetLayer(dwLayerIndex)->GetUVs() ); } if( pFbxMesh->GetLayer(dwLayerIndex)->GetMaterials() != NULL ) { if( pMaterialSet != NULL ) { ExportLog::LogWarning( "Multiple material layers detected on mesh %s. Some will be ignored.", pMesh->GetName().SafeString() ); } pMaterialSet = pFbxMesh->GetLayer(dwLayerIndex)->GetMaterials(); DWORD dwMaterialCount = pMaterialSet->GetDirectArray().GetCount(); for( DWORD i = 0; i < dwMaterialCount; ++i ) { ExportMaterial* pMaterial = ParseMaterialInLayer( pFbxMesh, pFbxMesh->GetLayer( dwLayerIndex ), i ); MaterialList.push_back( pMaterial ); } } } ExportLog::LogMsg( 4, "Found %d UV sets", dwUVSetCount ); dwUVSetCount = min( dwUVSetCount, (DWORD)g_pScene->Settings().iMaxUVSetCount ); ExportLog::LogMsg( 4, "Using %d 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(); KFbxVector4* pVertexPositions = pFbxMesh->GetControlPoints(); if( bSkinnedMesh ) { assert( skindata.dwVertexCount == dwVertexCount ); } ExportLog::LogMsg( 4, "%d vertices, %d polygons", dwVertexCount, dwPolyCount ); DWORD dwNonConformingSubDPolys = 0; // Loop over polygons. 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 != NULL ) { switch( pMaterialSet->GetMappingMode() ) { case KFbxLayerElement::eBY_POLYGON: switch( pMaterialSet->GetReferenceMode() ) { case KFbxLayerElement::eDIRECT: dwMaterialIndex = dwPolyIndex; break; case KFbxLayerElement::eINDEX: case KFbxLayerElement::eINDEX_TO_DIRECT: dwMaterialIndex = pMaterialSet->GetIndexArray().GetAt( dwPolyIndex ); break; } case KFbxLayerElement::eALL_SAME: break; } } const BOOL bInvertTexVCoord = g_pScene->Settings().bInvertTexVCoord; 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] ); KFbxVector4 vNormals[3]; ZeroMemory( vNormals, 3 * sizeof(KFbxVector4) ); INT iPolyIndex = (INT)dwPolyIndex; INT iVertIndex[3] = { 0, (INT)dwTriangleIndex + 1, (INT)dwTriangleIndex + 2 }; //INT iVertIndexUV[3] = { (INT)dwTriangleIndex, (INT)dwTriangleIndex + 1, (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. ExportMeshTriangle* pTriangle = g_MeshTriangleAllocator.GetNewTriangle(); // Store polygon index pTriangle->PolygonIndex = (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 pTriangle->Vertex[dwCornerIndex].Position.x = (FLOAT)pVertexPositions[dwDCCIndex].mData[0]; pTriangle->Vertex[dwCornerIndex].Position.y = (FLOAT)pVertexPositions[dwDCCIndex].mData[1]; pTriangle->Vertex[dwCornerIndex].Position.z = (FLOAT)pVertexPositions[dwDCCIndex].mData[2]; // Store vertex normal pTriangle->Vertex[dwCornerIndex].Normal.x = (FLOAT)vNormals[dwCornerIndex].mData[0]; pTriangle->Vertex[dwCornerIndex].Normal.y = (FLOAT)vNormals[dwCornerIndex].mData[1]; pTriangle->Vertex[dwCornerIndex].Normal.z = (FLOAT)vNormals[dwCornerIndex].mData[2]; // Store UV sets for( DWORD dwUVIndex = 0; dwUVIndex < dwUVSetCount; ++dwUVIndex ) { // Crack apart the FBX dereferencing system for UV coordinates KFbxLayerElementUV* pUVSet = VertexUVSets[dwUVIndex]; KFbxVector2 Value( 0, 0 ); INT iUVIndex = 0; switch( pUVSet->GetMappingMode() ) { case KFbxLayerElement::eBY_CONTROL_POINT: iUVIndex = pFbxMesh->GetPolygonVertex( iPolyIndex, iVertIndex[dwCornerIndex] ); break; case KFbxLayerElement::eBY_POLYGON_VERTEX: iUVIndex = pFbxMesh->GetTextureUVIndex( iPolyIndex, iVertIndex[dwCornerIndex] ); break; } Value = pUVSet->GetDirectArray().GetAt( iUVIndex ); // 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 != NULL ) { // Crack apart the FBX dereferencing system for Color coordinates KFbxColor Value( 0, 0, 0, 1 ); switch( pVertexColorSet->GetMappingMode() ) { case KFbxLayerElement::eBY_CONTROL_POINT: switch( pVertexColorSet->GetReferenceMode() ) { case KFbxLayerElement::eDIRECT: Value = pVertexColorSet->GetDirectArray().GetAt( dwDCCIndex ); break; case KFbxLayerElement::eINDEX_TO_DIRECT: Value = pVertexColorSet->GetDirectArray().GetAt( pVertexColorSet->GetIndexArray().GetAt( dwDCCIndex ) ); break; } break; case KFbxLayerElement::eBY_POLYGON_VERTEX: switch( pVertexColorSet->GetReferenceMode() ) { case KFbxLayerElement::eDIRECT: Value = pVertexColorSet->GetDirectArray().GetAt( iVertIndex[dwCornerIndex] ); break; case KFbxLayerElement::eINDEX_TO_DIRECT: Value = pVertexColorSet->GetDirectArray().GetAt( pVertexColorSet->GetIndexArray().GetAt( iVertIndex[dwCornerIndex] ) ); 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(ByteVector4) ); memcpy( &pTriangle->Vertex[dwCornerIndex].BoneWeights, skindata.GetWeights( dwDCCIndex ), sizeof(D3DXVECTOR4) ); } } // Add raw triangle to the mesh. pMesh->AddRawTriangle( pTriangle ); } } if( bSubDProcess ) { dwMeshOptimizationFlags |= ExportMesh::FORCE_SUBD_CONVERSION; } pMesh->Optimize( dwMeshOptimizationFlags ); ExportModel* pModel = new ExportModel( pMesh ); DWORD dwMaterialCount = (DWORD)MaterialList.size(); if( pMesh->GetSubDMesh() == NULL ) { for( DWORD dwSubset = 0; dwSubset < dwMaterialCount; ++dwSubset ) { ExportMaterial* pMaterial = MaterialList[dwSubset]; ExportIBSubset* pSubset = pMesh->GetSubset( dwSubset ); CHAR strUniqueSubsetName[100]; sprintf_s( strUniqueSubsetName, "subset%d_%s", dwSubset, pMaterial->GetName().SafeString() ); pSubset->SetName( strUniqueSubsetName ); pModel->SetSubsetBinding( pSubset->GetName(), pMaterial ); } } else { ExportSubDProcessMesh* pSubDMesh = pMesh->GetSubDMesh(); DWORD dwSubsetCount = pSubDMesh->GetSubsetCount(); for( DWORD dwSubset = 0; dwSubset < dwSubsetCount; ++dwSubset ) { ExportSubDPatchSubset* pSubset = pSubDMesh->GetSubset( dwSubset ); assert( pSubset != NULL ); assert( pSubset->iOriginalMeshSubset < (INT)dwMaterialCount ); ExportMaterial* pMaterial = MaterialList[pSubset->iOriginalMeshSubset]; CHAR strUniqueSubsetName[100]; sprintf_s( strUniqueSubsetName, "subset%d_%s", dwSubset, pMaterial->GetName().SafeString() ); pSubset->Name = strUniqueSubsetName; pModel->SetSubsetBinding( pSubset->Name, pMaterial, TRUE ); } } if( bSubDProcess && ( dwNonConformingSubDPolys > 0 ) ) { ExportLog::LogWarning( "Encountered %d 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() != NULL ) { 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 ); }
void FBXJSONSerializer::recurse_over_model(fbxsdk_2012_1::KFbxNode *fbx_node, json::Array& meshes_array) { KFbxMesh* old_mesh = fbx_node->GetMesh(); if (old_mesh) { KFbxGeometryConverter converter(fbx_node->GetFbxSdkManager()); KFbxMesh* mesh = converter.TriangulateMesh(old_mesh); mesh->ComputeBBox(); mesh->ComputeVertexNormals(); Object json_mesh; { KFbxLayerElementUV* uvs = mesh->GetLayer(0)->GetUVs(); KFbxLayerElementNormal* normals = mesh->GetLayer(0, KFbxLayerElement::eNORMAL)->GetNormals(); Array json_mesh_normals; Array json_mesh_uvs; Array json_mesh_vertices; int polygon_count = mesh->GetPolygonCount(); for (int poly_index = 0; poly_index < polygon_count; poly_index++) { for (int vertex_index = 0; vertex_index < mesh->GetPolygonSize(poly_index); vertex_index++) { int vertex_position = mesh->GetPolygonVertex(poly_index, vertex_index); KFbxVector4 vertex = mesh->GetControlPoints()[vertex_position]; Number vertex_x = vertex.GetAt(0); json_mesh_vertices.Insert(vertex_x); Number vertex_y = vertex.GetAt(1); json_mesh_vertices.Insert(vertex_y); Number vertex_z = vertex.GetAt(2); json_mesh_vertices.Insert(vertex_z); KFbxVector4 normal = normals->GetDirectArray()[vertex_position]; Number normal_x = normal.GetAt(0); json_mesh_normals.Insert(normal_x); Number normal_y = normal.GetAt(1); json_mesh_normals.Insert(normal_y); Number normal_z = normal.GetAt(2); json_mesh_normals.Insert(normal_z); if (uvs) { int mesh_index = mesh->GetTextureUVIndex(poly_index, vertex_index); KFbxVector2 uv = uvs->GetDirectArray().GetAt(mesh_index); Number uv_x = 1.0f-uv[0]; json_mesh_uvs.Insert(uv_x); // these are flipped Number uv_y = 1.0f-uv[1]; json_mesh_uvs.Insert(uv_y); } } } json_mesh["vertices"] = json_mesh_vertices; json_mesh["normals"] = json_mesh_normals; json_mesh["uvs"] = json_mesh_uvs; } { fbxDouble3 scale = fbx_node->LclScaling.Get(); Object json_mesh_scale; json_mesh_scale["x"] = Number(scale[0]); json_mesh_scale["y"] = Number(scale[1]); json_mesh_scale["z"] = Number(scale[2]); json_mesh["scale"] = json_mesh_scale; } { fbxDouble3 translation = fbx_node->LclTranslation.Get(); Object json_mesh_translation; json_mesh_translation["x"] = Number(translation[0]); json_mesh_translation["y"] = Number(translation[1]); json_mesh_translation["z"] = Number(translation[2]); json_mesh["translation"] = json_mesh_translation; } { fbxDouble3 rotation = fbx_node->LclRotation.Get(); Object json_mesh_rotation; json_mesh_rotation["x"] = Number(rotation[0]); json_mesh_rotation["y"] = Number(rotation[1]); json_mesh_rotation["z"] = Number(rotation[2]); json_mesh["rotation"] = json_mesh_rotation; } { int material_count = fbx_node->GetMaterialCount(); Array json_mesh_materials; for (int material_index = 0; material_index < material_count; material_index++) { KFbxSurfaceMaterial* surface_material = fbx_node->GetMaterial(material_index); Object json_material; Array json_textures; int textureIndex = 0; FOR_EACH_TEXTURE(textureIndex) { KFbxProperty property = surface_material->FindProperty(KFbxLayerElement::TEXTURE_CHANNEL_NAMES[textureIndex]); int layered_texture_count = property.GetSrcObjectCount(KFbxTexture::ClassId); for (int layered_texture_index = 0; layered_texture_index < layered_texture_count; ++layered_texture_index) { KFbxTexture* texture = KFbxCast <KFbxTexture> (property.GetSrcObject(KFbxTexture::ClassId, layered_texture_index)); if(texture) { KFbxFileTexture *file_texture = KFbxCast<KFbxFileTexture>(texture); if (file_texture) { Object json_texture; json_texture["filename"] = String(file_texture->GetFileName()); json_textures.Insert(json_texture); } } } } json_material["textures"] = json_textures; KFbxSurfaceLambert* lambert_material = KFbxCast<KFbxSurfaceLambert>(surface_material); if (lambert_material) { Object diffuse; double diffuse_r = lambert_material->Diffuse.Get()[0]; diffuse["r"] = Number(diffuse_r); double diffuse_g = lambert_material->Diffuse.Get()[1]; diffuse["g"] = Number(diffuse_g); double diffuse_b = lambert_material->Diffuse.Get()[2]; diffuse["b"] = Number(diffuse_b); json_material["diffuse"] = diffuse; Object ambient; double ambient_r = lambert_material->Ambient.Get()[0]; ambient["r"] = Number(ambient_r); double ambient_g = lambert_material->Ambient.Get()[1]; ambient["g"] = Number(ambient_g); double ambient_b = lambert_material->Ambient.Get()[2]; ambient["b"] = Number(ambient_b); json_material["ambient"] = ambient; KFbxProperty specular_property = lambert_material->FindProperty("SpecularColor"); fbxDouble3 specular_data; specular_property.Get(&specular_data, eDOUBLE3); Object specular; float specular_r = specular_data[0]; specular["r"] = Number(specular_r); float specular_g = specular_data[1]; specular["g"] = Number(specular_g); float specular_b = specular_data[2]; specular["b"] = Number(specular_b); json_material["specular"] = specular; } json_mesh_materials.Insert(json_material); } json_mesh["materials"] = json_mesh_materials; } json_mesh["uv_stride"] = Number(2); json_mesh["vertex_stride"] = Number(3); json_mesh["normal_stride"] = Number(3); meshes_array.Insert(json_mesh); } for(int j = 0; j < fbx_node->GetChildCount(); j++) { recurse_over_model(fbx_node->GetChild(j), meshes_array); } }