SkinData *XMLDataParser::parseSkinData(const dragonBones::XMLElement *skinXML, SkeletonData *data) { SkinData *skinData = new SkinData(); skinData->name = skinXML->Attribute(ConstValues::A_NAME.c_str()); for(const dragonBones::XMLElement* slotXML = skinXML->FirstChildElement(ConstValues::SLOT.c_str()) ; slotXML ; slotXML = slotXML->NextSiblingElement(ConstValues::SLOT.c_str())) { skinData->addSlotData(parseSlotData(slotXML, data)); } return skinData; }
SkinData * ObjectDataParser::parseSkinData(Json::Value & skinObject, SkeletonData * data) { SkinData* skinData = new SkinData(); int nameVal = skinObject[ConstValues::A_NAME].asInt(); char temp[64]; sprintf(temp, "%d", nameVal); skinData->name = temp; Json::Value & slots = skinObject[ConstValues::SLOT]; for (uint i = 0; i < slots.size(); i++) { Json::Value & slotObject = slots[i]; skinData->addSlotData(parseSlotData(slotObject, data)); } return skinData; }
static void initBones(const aiScene * scene, const aiMesh * nodeMesh, Mesh * mesh, SubMesh * subMesh) { Armature * armature = mesh->getArmature(); BoneData bdata; map<unsigned int, AssimpSkinData> skinDatas; // bones for(unsigned int i=0; i<nodeMesh->mNumBones; i++) { aiBone * nodeBone = nodeMesh->mBones[i]; unsigned int boneId; if(! armature->getBoneId(nodeBone->mName.data, &boneId)) continue; OBone * bone = armature->getBone(boneId); aiMatrix4x4 offsetMat = nodeBone->mOffsetMatrix; aiTransposeMatrix4(&offsetMat); Matrix4x4 matrix = (*bone->getMatrix()) * Matrix4x4((float*)&offsetMat); // pose skinning Vector3 * vertices = subMesh->getVertices(); Vector3 * normals = subMesh->getNormals(); Vector3 * tangents = subMesh->getTangents(); unsigned int w; for(w=0; w<nodeBone->mNumWeights; w++) { unsigned int vid = nodeBone->mWeights[w].mVertexId; float weight = nodeBone->mWeights[w].mWeight; bdata.id = boneId; bdata.weight = weight; skinDatas[vid].bones.push_back(bdata); if(skinDatas[vid].bones.size() == 1) { if(vertices) vertices[vid] = Vector3(0, 0, 0); if(normals) normals[vid] = Vector3(0, 0, 0); if(tangents) tangents[vid] = Vector3(0, 0, 0); } if(vertices) vertices[vid] += matrix * Vector3(nodeMesh->mVertices[vid].x, nodeMesh->mVertices[vid].y, nodeMesh->mVertices[vid].z) * weight; if(normals) normals[vid] += matrix.getRotatedVector3(Vector3(nodeMesh->mNormals[vid].x, nodeMesh->mNormals[vid].y, nodeMesh->mNormals[vid].z)) * weight; if(tangents) tangents[vid] += matrix.getRotatedVector3(Vector3(nodeMesh->mTangents[vid].x, nodeMesh->mTangents[vid].y, nodeMesh->mTangents[vid].z)) * weight; } } // alloc skin unsigned int skinSize = skinDatas.size(); if(skinSize > 0) { SkinData * skin = subMesh->createSkinData(); SkinPoint * skinPoints = skin->allocPoints(skinSize); map<unsigned int, AssimpSkinData>::iterator mit (skinDatas.begin()), mend(skinDatas.end()); unsigned int p = 0; for(; mit!=mend; ++mit) { unsigned int vertexId = mit->first; AssimpSkinData * sdata = &mit->second; unsigned int b, bSize = sdata->bones.size(); if(skinPoints[p].allocateBonesLinks(bSize)) { skinPoints[p].setVertexId(vertexId); unsigned short * ids = skinPoints[p].getBonesIds(); float * weights = skinPoints[p].getBonesWeights(); for(b=0; b<bSize; b++) { ids[b] = sdata->bones[b].id; weights[b] = sdata->bones[b].weight; } } p++; } } }
/** * Build and returns a new Armature instance. * @example * <listing> * var armature:Armature = factory.buildArmature('dragon'); * </listing> * @param armatureName The name of this Armature instance. * @param The name of this animation * @param The name of this SkeletonData. * @param The name of this textureAtlas. * @param The name of this skin. * @return A Armature instance. */ Armature* BaseFactory::buildArmature(const String &armatureName, const String &animationName, const String &skeletonName, const String &textureAtlasName, const String &skinName) { ArmatureData* armatureData = 0; SkeletonData *data = 0; if(!skeletonName.empty()) { std::map<String , SkeletonData*>::iterator iter = _dataDic.find(skeletonName); if(iter != _dataDic.end()) { data = iter->second; armatureData = data->getArmatureData(armatureName); } } //else //{ // for(skeletonName in _dataDic) // { // data = _dataDic[skeletonName]; // armatureData = data->getArmatureData(armatureName); // if(armatureData) // { // break; // } // } //} if(!armatureData) { return nullptr; } _currentDataName = skeletonName; _currentTextureAtlasName = textureAtlasName.empty() ? skeletonName : textureAtlasName; Armature* armature = generateArmature(); armature->name = armatureName; Bone* bone; for(size_t i = 0 ; i < armatureData->boneDataList.size() ; i ++) { BoneData* boneData = armatureData->boneDataList[i]; bone = new Bone(); bone->name = boneData->name; bone->fixedRotation = boneData->fixedRotation; bone->scaleMode = boneData->scaleMode; bone->origin = boneData->transform; if(armatureData->getBoneData(boneData->parent)) { armature->addBone(bone, boneData->parent); } else { armature->addBone(bone); } } ArmatureData* animationArmatureData = 0; SkinData *skinDataCopy = 0; if(!animationName.empty() && animationName != armatureName) { //ArmatureData* animationArmatureData = data->getArmatureData(animationName); // Get the default animation //if(!animationArmatureData) //{ // for (skeletonName in _dataDic) // { // data = _dataDic[skeletonName]; // animationArmatureData = data->getArmatureData(animationName); // if(animationArmatureData) // { // break; // } // } //} ArmatureData* armatureDataCopy = data->getArmatureData(animationName); if(armatureDataCopy) { skinDataCopy = armatureDataCopy->getSkinData(""); } } if(animationArmatureData) { armature->getAnimation()->setAnimationDataList(animationArmatureData->animationDataList); } else { armature->getAnimation()->setAnimationDataList(armatureData->animationDataList); } SkinData* skinData = armatureData->getSkinData(skinName); if(!skinData) { return nullptr; //throw new ArgumentError(); } Slot* slot; DisplayData* displayData; Armature* childArmature; size_t i; //var helpArray:Array = []; for(size_t j = 0 ; j < skinData->slotDataList.size() ; j ++) { SlotData* slotData = skinData->slotDataList[j]; bone = armature->getBone(slotData->parent); if(!bone) { continue; } slot = generateSlot(); slot->name = slotData->name; slot->setBlendMode(slotData->blendMode); slot->_originZOrder = slotData->zOrder; slot->_dislayDataList = slotData->displayDataList; std::vector<Object*> helpArray; i = slotData->displayDataList.size(); helpArray.resize(i); while(i --) { displayData = slotData->displayDataList[i]; if(displayData->type == DisplayData::ARMATURE) { DisplayData* displayDataCopy = 0; if(skinDataCopy) { SlotData* slotDataCopy = skinDataCopy->getSlotData(slotData->name); if(slotDataCopy) { displayDataCopy = slotDataCopy->displayDataList[i]; } } else { displayDataCopy = 0; } childArmature = buildArmature(displayData->name, displayDataCopy?displayDataCopy->name:"", _currentDataName, _currentTextureAtlasName); if(childArmature) { helpArray[i] = childArmature; } //fix by Wayne Dimart: // break; we don't use break here, or will crach the program due to incomplete helpArray. continue; } else { helpArray[i] = generateDisplay(getTextureAtlas(_currentTextureAtlasName), displayData->name, displayData->pivot.x, displayData->pivot.y); } } slot->setDisplayList(helpArray); slot->changeDisplay(0); bone->addChild(slot); } // i = armature->_boneList.size(); while(i --) { armature->_boneList[i]->update(); } i = armature->_slotList.size(); while(i --) { slot = armature->_slotList[i]; slot->update(); } armature->updateSlotsZOrder(); return armature; }
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 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 ); }