// Deform the vertex array according to the links contained in the mesh and the skinning type. void fbxLoader2::ComputeSkinDeformation(FbxAMatrix& pGlobalPosition, FbxMesh* pMesh, FbxTime& pTime, FbxPose* pPose, int frame) { FbxSkin * lSkinDeformer = (FbxSkin *)pMesh->GetDeformer(0, FbxDeformer::eSkin); FbxSkin::EType lSkinningType = lSkinDeformer->GetSkinningType(); if(lSkinningType == FbxSkin::eLinear || lSkinningType == FbxSkin::eRigid) { ComputeLinearDeformation(pGlobalPosition, pMesh, pTime, pPose, frame); } else if(lSkinningType == FbxSkin::eDualQuaternion) { int i = 0; ComputeDualQuaternionDeformation(pGlobalPosition, pMesh, pTime, pPose, frame); } else if(lSkinningType == FbxSkin::eBlend) { int lVertexCount = pMesh->GetControlPointsCount(); FbxVector4* lVertexArrayLinear = new FbxVector4[lVertexCount]; memcpy(lVertexArrayLinear, pMesh->GetControlPoints(), lVertexCount * sizeof(FbxVector4)); FbxVector4* lVertexArrayDQ = new FbxVector4[lVertexCount]; memcpy(lVertexArrayDQ, pMesh->GetControlPoints(), lVertexCount * sizeof(FbxVector4)); ComputeLinearDeformation(pGlobalPosition, pMesh, pTime, pPose, frame); ComputeDualQuaternionDeformation(pGlobalPosition, pMesh, pTime, pPose, frame); } }
void FBXSceneEncoder::loadSkin(FbxMesh* fbxMesh, Model* model) { const int deformerCount = fbxMesh->GetDeformerCount(); for (int i = 0; i < deformerCount; ++i) { FbxDeformer* deformer = fbxMesh->GetDeformer(i); if (deformer->GetDeformerType() == FbxDeformer::eSkin) { FbxSkin* fbxSkin = FbxCast<FbxSkin>(deformer); MeshSkin* skin = new MeshSkin(); vector<string> jointNames; vector<Node*> joints; vector<Matrix> bindPoses; const int clusterCount = fbxSkin->GetClusterCount(); for (int j = 0; j < clusterCount; ++j) { FbxCluster* cluster = fbxSkin->GetCluster(j); assert(cluster); FbxNode* linkedNode = cluster->GetLink(); if (linkedNode && linkedNode->GetSkeleton()) { const char* jointName = linkedNode->GetName(); assert(jointName); jointNames.push_back(jointName); Node* joint = loadNode(linkedNode); assert(joint); joints.push_back(joint); FbxAMatrix matrix; cluster->GetTransformLinkMatrix(matrix); Matrix m; copyMatrix(matrix.Inverse(), m); bindPoses.push_back(m); } } skin->setJointNames(jointNames); skin->setJoints(joints); skin->setBindPoses(bindPoses); model->setSkin(skin); break; } } }
void fbxLoader2::setBindPoseCluster(FbxNode *node) { if( node->GetNodeAttribute()) { switch(node->GetNodeAttribute()->GetAttributeType()) { case FbxNodeAttribute::eMesh: FbxMesh *mesh = node->GetMesh(); for (int j = 0; j<mesh->GetDeformerCount(); j++) { FbxSkin *skin = (FbxSkin*) mesh->GetDeformer(j,FbxDeformer::eSkin); int clusters = skin->GetClusterCount(); for(int k = 0; k<clusters; k++) { FbxCluster* cluster = skin->GetCluster(k); FbxNode* boneLink = cluster->GetLink(); if(boneLink) { std::string nameLink = boneLink->GetName(); FbxAMatrix translationM; FbxAMatrix invert; cluster->GetTransformLinkMatrix(translationM); cluster->GetTransformMatrix(invert); translationM = translationM * invert.Inverse(); D3DXMATRIX mat = D3DXMATRIX((float)translationM.mData[0].mData[0], (float)translationM.mData[0].mData[1], (float)translationM.mData[0].mData[2], (float)translationM.mData[3].mData[0], (float)translationM.mData[1].mData[0], (float)translationM.mData[1].mData[1], (float)translationM.mData[1].mData[2], (float)translationM.mData[3].mData[1], (float)translationM.mData[2].mData[0], (float)translationM.mData[2].mData[1], (float)translationM.mData[2].mData[2], (float)translationM.mData[3].mData[2], 0,0,0,1); skeleton->GetBone(skeleton->GetBoneByName(nameLink))->SetTransformation(mat); } } } break; } } for (int i = 0; i<node->GetChildCount(); i++) { FbxNode* child = node->GetChild(i); setBindPoseCluster(child); } }
bool loadBlendWeights(FbxMesh* fbxMesh, vector<vector<Vector2> >& weights) { assert(fbxMesh); const int vertexCount = fbxMesh->GetControlPointsCount(); FbxSkin* fbxSkin = NULL; const int deformerCount = fbxMesh->GetDeformerCount(); for (int i = 0; i < deformerCount; ++i) { FbxDeformer* deformer = fbxMesh->GetDeformer(i); if (deformer->GetDeformerType() == FbxDeformer::eSkin) { fbxSkin = FbxCast<FbxSkin>(deformer); weights.resize(vertexCount); const int clusterCount = fbxSkin->GetClusterCount(); for (int j = 0; j < clusterCount; ++j) { FbxCluster* cluster = fbxSkin->GetCluster(j); assert(cluster); const int vertexIndexCount = cluster->GetControlPointIndicesCount(); for (int k = 0; k < vertexIndexCount; ++k) { int index = cluster->GetControlPointIndices()[k]; if (index >= vertexCount) { continue; } double weight = cluster->GetControlPointWeights()[k]; if (weight == 0.0) { continue; } weights[index].push_back(Vector2((float)j, (float)weight)); } } // Only the first skin deformer will be loaded. // There probably won't be more than one. break; } } return fbxSkin != NULL; }
// Deform the vertex array according to the links contained in the mesh and the skinning type. void compute_skin_deformation(FbxAMatrix& pGlobalPosition, FbxMesh* pMesh, FbxTime& pTime, FbxVector4* pVertexArray, FbxPose* pPose) { FbxSkin * lSkinDeformer = (FbxSkin *)pMesh->GetDeformer(0, FbxDeformer::eSkin); FbxSkin::EType lSkinningType = lSkinDeformer->GetSkinningType(); if(lSkinningType == FbxSkin::eLinear || lSkinningType == FbxSkin::eRigid) { compute_linear_deformation(pGlobalPosition, pMesh, pTime, pVertexArray, pPose); } else if(lSkinningType == FbxSkin::eDualQuaternion) { compute_dual_quaternion_deformation(pGlobalPosition, pMesh, pTime, pVertexArray, pPose); } else if(lSkinningType == FbxSkin::eBlend) { int lVertexCount = pMesh->GetControlPointsCount(); FbxVector4* lVertexArrayLinear = new FbxVector4[lVertexCount]; memcpy(lVertexArrayLinear, pMesh->GetControlPoints(), lVertexCount * sizeof(FbxVector4)); FbxVector4* lVertexArrayDQ = new FbxVector4[lVertexCount]; memcpy(lVertexArrayDQ, pMesh->GetControlPoints(), lVertexCount * sizeof(FbxVector4)); compute_linear_deformation(pGlobalPosition, pMesh, pTime, lVertexArrayLinear, pPose); compute_dual_quaternion_deformation(pGlobalPosition, pMesh, pTime, lVertexArrayDQ, pPose); // To blend the skinning according to the blend weights // Final vertex = DQSVertex * blend weight + LinearVertex * (1- blend weight) // DQSVertex: vertex that is deformed by dual quaternion skinning method; // LinearVertex: vertex that is deformed by classic linear skinning method; int lBlendWeightsCount = lSkinDeformer->GetControlPointIndicesCount(); for(int lBWIndex = 0; lBWIndex<lBlendWeightsCount; ++lBWIndex) { double lBlendWeight = lSkinDeformer->GetControlPointBlendWeights()[lBWIndex]; pVertexArray[lBWIndex] = lVertexArrayDQ[lBWIndex] * lBlendWeight + lVertexArrayLinear[lBWIndex] * (1 - lBlendWeight); } } }
// Deform the vertex array in classic linear way. void compute_linear_deformation(FbxAMatrix& pGlobalPosition, FbxMesh* pMesh, FbxTime& pTime, FbxVector4* pVertexArray, FbxPose* pPose) { // All the links must have the same link mode. FbxCluster::ELinkMode lClusterMode = ((FbxSkin*)pMesh->GetDeformer(0, FbxDeformer::eSkin))->GetCluster(0)->GetLinkMode(); int lVertexCount = pMesh->GetControlPointsCount(); FbxAMatrix* lClusterDeformation = new FbxAMatrix[lVertexCount]; memset(lClusterDeformation, 0, lVertexCount * sizeof(FbxAMatrix)); double* lClusterWeight = new double[lVertexCount]; memset(lClusterWeight, 0, lVertexCount * sizeof(double)); if(lClusterMode == FbxCluster::eAdditive) { for(int i = 0; i < lVertexCount; ++i) { lClusterDeformation[i].SetIdentity(); } } // For all skins and all clusters, accumulate their deformation and weight // on each vertices and store them in lClusterDeformation and lClusterWeight. int lSkinCount = pMesh->GetDeformerCount(FbxDeformer::eSkin); for(int lSkinIndex=0; lSkinIndex<lSkinCount; ++lSkinIndex) { FbxSkin * lSkinDeformer = (FbxSkin *)pMesh->GetDeformer(lSkinIndex, FbxDeformer::eSkin); int lClusterCount = lSkinDeformer->GetClusterCount(); for(int lClusterIndex=0; lClusterIndex<lClusterCount; ++lClusterIndex) { FbxCluster* lCluster = lSkinDeformer->GetCluster(lClusterIndex); if(!lCluster->GetLink()) { continue; } FbxAMatrix lVertexTransformMatrix; compute_cluster_deformation(pGlobalPosition, pMesh, lCluster, lVertexTransformMatrix, pTime, pPose); int lVertexIndexCount = lCluster->GetControlPointIndicesCount(); for(int k = 0; k < lVertexIndexCount; ++k) { int lIndex = lCluster->GetControlPointIndices()[k]; // Sometimes, the mesh can have less points than at the time of the skinning // because a smooth operator was active when skinning but has been deactivated during export. if(lIndex >= lVertexCount) { continue; } double lWeight = lCluster->GetControlPointWeights()[k]; if(lWeight == 0.0) { continue; } // Compute the influence of the link on the vertex. FbxAMatrix lInfluence = lVertexTransformMatrix; matrix_scale(lInfluence, lWeight); if(lClusterMode == FbxCluster::eAdditive) { // Multiply with the product of the deformations on the vertex. matrix_add_to_diagonal(lInfluence, 1.0 - lWeight); lClusterDeformation[lIndex] = lInfluence * lClusterDeformation[lIndex]; // Set the link to 1.0 just to know this vertex is influenced by a link. lClusterWeight[lIndex] = 1.0; } else { // lLinkMode == FbxCluster::eNormalize || lLinkMode == FbxCluster::eTotalOne // Add to the sum of the deformations on the vertex. matrix_add(lClusterDeformation[lIndex], lInfluence); // Add to the sum of weights to either normalize or complete the vertex. lClusterWeight[lIndex] += lWeight; } }//For each vertex }//lClusterCount } //Actually deform each vertices here by information stored in lClusterDeformation and lClusterWeight for(int i = 0; i < lVertexCount; i++) { FbxVector4 lSrcVertex = pVertexArray[i]; FbxVector4& lDstVertex = pVertexArray[i]; double lWeight = lClusterWeight[i]; // Deform the vertex if there was at least a link with an influence on the vertex, if(lWeight != 0.0) { lDstVertex = lClusterDeformation[i].MultT(lSrcVertex); if(lClusterMode == FbxCluster::eNormalize) { // In the normalized link mode, a vertex is always totally influenced by the links. lDstVertex /= lWeight; } else if(lClusterMode == FbxCluster::eTotalOne) { // In the total 1 link mode, a vertex can be partially influenced by the links. lSrcVertex *= (1.0 - lWeight); lDstVertex += lSrcVertex; } } } delete [] lClusterDeformation; delete [] lClusterWeight; }
void MeshImporter::LoadWeight(FbxMesh* fbxMesh, MeshEntry* mesh) { int numSkin = fbxMesh->GetDeformerCount(FbxDeformer::eSkin); if (numSkin == 0) { return; } assert(numSkin == 1); // Assume only one deformer FbxSkin* fbxSkin = static_cast<FbxSkin*>(fbxMesh->GetDeformer(0, FbxDeformer::eSkin)); int numCluster = fbxSkin->GetClusterCount(); int numControlPoints = fbxMesh->GetControlPointsCount(); vector<VertexWeight> tmpWeightList(numControlPoints); for (int i = 0; i < numCluster; i++) { FbxCluster* fbxCluster = fbxSkin->GetCluster(i); if (!fbxCluster->GetLink()) { continue; } unsigned int boneIndex = model->skeleton->bones.size(); assert(fbxCluster->GetLinkMode() == FbxCluster::eNormalize); // Read skeleton bones data (transformation matrix of each bone) string boneName(fbxCluster->GetLink()->GetName()); if (!model->skeleton->FindBoneByName(boneName)) { if (boneIndex >= MAXBONE) { PrintTab("Too many bones to load!!"); } else { // Read weights of each vertex int numIndexInCluster = fbxCluster->GetControlPointIndicesCount(); int* indicesInCluster = fbxCluster->GetControlPointIndices(); double* weights = fbxCluster->GetControlPointWeights(); for (int j = 0; j < numIndexInCluster; j++) { tmpWeightList[indicesInCluster[j]].AddBoneData(boneIndex, weights[j]); } // Normalize weights /*for (int inVert = 0; inVert < tmpWeightList.size(); inVert++) { tmpWeightList[inVert].Normalize(); }*/ // Read animation bone matrix FbxAMatrix fbxGlobalBoneBaseMatrix;// = fbxCluster->GetLink()->EvaluateGlobalTransform().Inverse().Transpose(); FbxAMatrix referenceGlobalInitPosition; FbxAMatrix clusterGlobalInitPosition; fbxCluster->GetTransformMatrix(referenceGlobalInitPosition); fbxCluster->GetTransformLinkMatrix(clusterGlobalInitPosition); fbxGlobalBoneBaseMatrix = clusterGlobalInitPosition.Inverse() * referenceGlobalInitPosition; // To be considered when importing Maya fbx model //FbxAMatrix geoMatrix = GetTransformMatrix(fbxCluster->GetLink()); Bone bone; bone.name = boneName; bone.boneIndex = boneIndex; bone.fbxNode = fbxCluster->GetLink(); bone.globalBindposeInverseMatrix = fbxGlobalBoneBaseMatrix; model->skeleton->bones.push_back(bone); } } } // Deployed in the index for (unsigned int i = 0; i < mesh->numVertices; i++) { mesh->vertices[i].boneIndices.x = tmpWeightList[mesh->indices[i]].boneWeight[0].first; mesh->vertices[i].boneIndices.y = tmpWeightList[mesh->indices[i]].boneWeight[1].first; mesh->vertices[i].boneIndices.z = tmpWeightList[mesh->indices[i]].boneWeight[2].first; mesh->vertices[i].boneIndices.w = tmpWeightList[mesh->indices[i]].boneWeight[3].first; mesh->vertices[i].weights.x = tmpWeightList[mesh->indices[i]].boneWeight[0].second; mesh->vertices[i].weights.y = tmpWeightList[mesh->indices[i]].boneWeight[1].second; mesh->vertices[i].weights.z = tmpWeightList[mesh->indices[i]].boneWeight[2].second; mesh->vertices[i].weights.w = tmpWeightList[mesh->indices[i]].boneWeight[3].second; } }
void FBXConverter::processAnimations( FbxNode* node , std::vector<JointData> &skeleton , std::vector<VertexData> &verts , std::vector<IndexData> &indices ) { FbxMesh* theMesh = node->GetMesh(); FbxAnimStack* animationStack = node->GetScene()->GetSrcObject<FbxAnimStack>(); FbxAnimLayer* animationLayer = animationStack->GetMember<FbxAnimLayer>(); std::vector<FbxTime> frames; for ( int curveNodeIndex = 0; curveNodeIndex < animationLayer->GetMemberCount(); ++curveNodeIndex ) { FbxAnimCurveNode* currentCurve = animationLayer->GetMember<FbxAnimCurveNode>( curveNodeIndex ); for ( unsigned int channelIndex = 0; channelIndex < currentCurve->GetChannelsCount(); ++channelIndex ) { for ( int curve = 0; curve < currentCurve->GetCurveCount( channelIndex ); ++curve ) { FbxAnimCurve* theCurveVictim = currentCurve->GetCurve( channelIndex , curve ); for ( int keyIndex = 0; keyIndex < theCurveVictim->KeyGetCount(); ++keyIndex ) { bool alreadyIn = false; for ( unsigned int frameIndex = 0; frameIndex < frames.size(); ++frameIndex ) { if ( ( unsigned int ) frames[frameIndex].GetFrameCount() == ( unsigned int ) theCurveVictim->KeyGet( keyIndex ).GetTime().GetFrameCount() ) { alreadyIn = true; //std::cout << frames[frameIndex].GetFrameCount() << " " << theCurveVictim->KeyGet( keyIndex ).GetTime().GetFrameCount() << " " << alreadyIn << std::endl; break; } } std::cout << theCurveVictim->KeyGet( keyIndex ).GetTime().GetFrameCount() << " " << alreadyIn << std::endl; if(!alreadyIn) frames.push_back(theCurveVictim->KeyGet( keyIndex ).GetTime()); } } } } std::sort( frames.begin() , frames.end() , frameCompare ); FbxAMatrix geoTransform( node->GetGeometricTranslation( FbxNode::eSourcePivot ) , node->GetGeometricRotation( FbxNode::eSourcePivot ) , node->GetGeometricScaling( FbxNode::eSourcePivot ) ); for ( int i = 0; i < theMesh->GetDeformerCount(); ++i ) { FbxSkin* theSkin = reinterpret_cast< FbxSkin* >( theMesh->GetDeformer( i , FbxDeformer::eSkin ) ); if ( !theSkin ) continue; for ( int j = 0; j < theSkin->GetClusterCount(); ++j ) { FbxCluster* cluster = theSkin->GetCluster( j ); std::string jointName = cluster->GetLink()->GetName(); int currentJointIndex = -1; for ( unsigned int k = 0; k < skeleton.size(); ++k ) { if ( !skeleton[k].name.compare( jointName ) ) { currentJointIndex = k; break; } } if ( currentJointIndex < 0 ) { std::cout << "wrong bone" << std::endl; continue; } FbxAMatrix transformMatrix, transformLinkMatrix, offsetMatrix; cluster->GetTransformMatrix( transformMatrix ); cluster->GetTransformLinkMatrix( transformLinkMatrix ); //offsetMatrix = transformLinkMatrix.Inverse() * transformMatrix * geoTransform; FbxMatrix realMatrix( transformLinkMatrix.Inverse() * transformMatrix ); for ( unsigned int row = 0; row < 4; ++row ) { for ( unsigned int column = 0; column < 4; ++column ) { skeleton[currentJointIndex].offsetMatrix[row][column] = (float)realMatrix.Get( row , column ); } } for ( int k = 0; k < cluster->GetControlPointIndicesCount(); ++k ) { BlendingIndexWeightPair weightPair; weightPair.blendingIndex = currentJointIndex; weightPair.blendingWeight = (float)cluster->GetControlPointWeights()[k]; unsigned int controlPoint = cluster->GetControlPointIndices()[k]; for ( unsigned int z = 0; z < indices.size(); ++z ) { if ( indices[z].oldControlPoint == controlPoint ) { if ( verts[indices[z].index].blendingInfo.size() > 4 ) { std::cout << "Warning: vert has more than 4 bones connected, ignoring additional bone." << std::endl; } //else verts[indices[z].index].blendingInfo.push_back( weightPair ); bool found = false; for ( unsigned int omg = 0; omg < verts[indices[z].index].blendingInfo.size(); ++omg ) { if ( verts[indices[z].index].blendingInfo[omg].blendingIndex == weightPair.blendingIndex ) { found = true; break; } } if ( !found ) verts[indices[z].index].blendingInfo.push_back( weightPair ); } } } for ( unsigned int frameIndex = 0; frameIndex < frames.size(); ++frameIndex ) { FbxVector4 translation = cluster->GetLink()->EvaluateLocalTranslation( frames[frameIndex] ); FbxVector4 rotation = cluster->GetLink()->EvaluateLocalRotation( frames[frameIndex] ); FbxVector4 scale = cluster->GetLink()->EvaluateLocalScaling( frames[frameIndex] ); AnimationData animData; animData.frame = (int)frames[frameIndex].GetFrameCount(); animData.translation = glm::vec3(translation[0],translation[1],translation[2]); animData.rotation = glm::angleAxis( glm::radians( (float)rotation[2] ), glm::vec3(0,0,1)) * glm::angleAxis(glm::radians((float) rotation[1]), glm::vec3(0,1,0)) * glm::angleAxis(glm::radians((float)rotation[0]), glm::vec3(1,0,0)); animData.scale = glm::vec3(scale[0],scale[1],scale[2]); skeleton[currentJointIndex].animation.push_back( animData ); } } } }
/** * Adds Fbx Clusters necessary to skin a skeletal mesh to the bones in the BoneNodes list */ void FFbxExporter::BindMeshToSkeleton(const USkeletalMesh* SkelMesh, FbxNode* MeshRootNode, TArray<FbxNode*>& BoneNodes) { const FSkeletalMeshResource* SkelMeshResource = SkelMesh->GetImportedResource(); const FStaticLODModel& SourceModel = SkelMeshResource->LODModels[0]; const int32 VertexCount = SourceModel.NumVertices; FbxAMatrix MeshMatrix; FbxScene* lScene = MeshRootNode->GetScene(); if( lScene ) { MeshMatrix = MeshRootNode->EvaluateGlobalTransform(); } FbxGeometry* MeshAttribute = (FbxGeometry*) MeshRootNode->GetNodeAttribute(); FbxSkin* Skin = FbxSkin::Create(Scene, ""); const int32 BoneCount = BoneNodes.Num(); for(int32 BoneIndex = 0; BoneIndex < BoneCount; ++BoneIndex) { FbxNode* BoneNode = BoneNodes[BoneIndex]; // Create the deforming cluster FbxCluster *CurrentCluster = FbxCluster::Create(Scene,""); CurrentCluster->SetLink(BoneNode); CurrentCluster->SetLinkMode(FbxCluster::eTotalOne); // Add all the vertices that are weighted to the current skeletal bone to the cluster // NOTE: the bone influence indices contained in the vertex data are based on a per-chunk // list of verts. The convert the chunk bone index to the mesh bone index, the chunk's // boneMap is needed int32 VertIndex = 0; const int32 SectionCount = SourceModel.Sections.Num(); for(int32 SectionIndex = 0; SectionIndex < SectionCount; ++SectionIndex) { const FSkelMeshSection& Section = SourceModel.Sections[SectionIndex]; for(int32 SoftIndex = 0; SoftIndex < Section.SoftVertices.Num(); ++SoftIndex) { const FSoftSkinVertex& Vert = Section.SoftVertices[SoftIndex]; for(int32 InfluenceIndex = 0; InfluenceIndex < MAX_TOTAL_INFLUENCES; ++InfluenceIndex) { int32 InfluenceBone = Section.BoneMap[ Vert.InfluenceBones[InfluenceIndex] ]; float InfluenceWeight = Vert.InfluenceWeights[InfluenceIndex] / 255.f; if(InfluenceBone == BoneIndex && InfluenceWeight > 0.f) { CurrentCluster->AddControlPointIndex(VertIndex, InfluenceWeight); } } ++VertIndex; } } // Now we have the Patch and the skeleton correctly positioned, // set the Transform and TransformLink matrix accordingly. CurrentCluster->SetTransformMatrix(MeshMatrix); FbxAMatrix LinkMatrix; if( lScene ) { LinkMatrix = BoneNode->EvaluateGlobalTransform(); } CurrentCluster->SetTransformLinkMatrix(LinkMatrix); // Add the clusters to the mesh by creating a skin and adding those clusters to that skin. Skin->AddCluster(CurrentCluster); } // Add the skin to the mesh after the clusters have been added MeshAttribute->AddDeformer(Skin); }
int main(int argc, char **argv) { #ifndef _DEBUG if (argc != 2) { printf("invalid arg"); return 0; } const char* filename = argv[1]; #else const char* filename = "*****@*****.**"; #endif output.open("output.txt", ios::out | ios::trunc); output2.open("output2.txt", ios::out | ios::trunc); output3.open("output3.txt", ios::out | ios::trunc); if (!output.is_open()) { exit(1); } FbxManager* fm = FbxManager::Create(); FbxIOSettings *ios = FbxIOSettings::Create(fm, IOSROOT); //ios->SetBoolProp(EXP_FBX_ANIMATION, false); ios->SetIntProp(EXP_FBX_COMPRESS_LEVEL, 9); ios->SetAllObjectFlags(true); fm->SetIOSettings(ios); FbxImporter* importer = FbxImporter::Create(fm, ""); if (!importer->Initialize(filename, -1, fm->GetIOSettings())) { printf("error returned : %s\n", importer->GetStatus().GetErrorString()); exit(-1); } FbxScene* scene = FbxScene::Create(fm, "myscene"); importer->Import(scene); importer->Destroy(); output << "some\n"; output << "charcnt : " << scene->GetCharacterCount() << endl << "node cnt : " << scene->GetNodeCount() << endl; int animstackcnt = scene->GetSrcObjectCount<FbxAnimStack>(); output << "animstackcnt : " << animstackcnt << endl; output << "------------" << endl; vector<FbxNode*> removableNodes; for (int i = 0; i < scene->GetNodeCount(); i++) { FbxNode* node = scene->GetNode(i); output << "scene's node " << i << " : " << node->GetName() << ", childcnt : " << node->GetChildCount(); if (node->GetNodeAttribute()) { output <<", att type : " << node->GetNodeAttribute()->GetAttributeType(); if (node->GetNodeAttribute()->GetAttributeType() == FbxNodeAttribute::EType::eMesh) { FbxMesh* mesh = node->GetMesh(); output << ", mem usage : " << mesh->MemoryUsage() << ", deformer cnt : " << mesh->GetDeformerCount(FbxDeformer::EDeformerType::eSkin) << endl; collapseMesh(mesh); FbxSkin* skin = (FbxSkin*) (mesh->GetDeformer(0, FbxDeformer::EDeformerType::eSkin)); if (skin) { for (int cli = 0; cli < skin->GetClusterCount(); cli++) { FbxCluster* cluster = skin->GetCluster(cli); output << "\tcluster no." << cli << " has " << cluster->GetControlPointIndicesCount() << " connected verts" << endl; if (cluster->GetControlPointIndicesCount() == 0) removableNodes.push_back( cluster->GetLink() ); //cluster-> //skin->RemoveCluster(cluster);효과없음 } } if (mesh->IsTriangleMesh()) { output << "\tit's triangle mesh" << endl; } //mesh->RemoveDeformer(0);효과없음 } else output << endl; } else { output << ", att type : none" << endl; } } for (int rni = 0; rni < removableNodes.size(); rni++) { FbxNode* rnd = removableNodes[rni]; if (rnd && rnd->GetNodeAttribute()->GetAttributeType() == FbxNodeAttribute::EType::eSkeleton) { output3 << rnd->GetName() << " node with no vert attached's curve : " << rnd->GetSrcObjectCount<FbxAnimCurve>() << "," << rnd->GetSrcObjectCount<FbxAnimCurveNode>() << endl; } } output << "-----------animinfo" << endl; int cubic = 0, linear = 0, cons = 0; for (int si = 0; si < animstackcnt; si++) { FbxAnimStack* stack = scene->GetSrcObject<FbxAnimStack>(si); for (int i = 0; i < stack->GetMemberCount<FbxAnimLayer>(); i++) { FbxAnimLayer* layer = stack->GetMember<FbxAnimLayer>(i); int curvenodecnt = layer->GetMemberCount<FbxAnimCurveNode>(); int compositcnt = 0; for (int j = 0; j < curvenodecnt; j++) { FbxAnimCurveNode* cnode = layer->GetMember<FbxAnimCurveNode>(j); compositcnt += (cnode->IsComposite() ? 1 : 0); } output << "\tanimstack's layer " << i << " : " << layer->GetName() << ", curve node cnt : " << curvenodecnt << ", composit node cnt : " << compositcnt << endl; vector<FbxAnimCurveNode*> nodes2del; for (int j = 0; j < curvenodecnt; j++) { FbxAnimCurveNode* cnode = layer->GetMember<FbxAnimCurveNode>(j); output << "\t\tcurvenode " << j << " channel cnt : " << cnode->GetChannelsCount() << ", dst obj cnt " << cnode->GetDstObjectCount() << "("; for (int dsti = 0; dsti < cnode->GetDstObjectCount(); dsti++) { output << "," << cnode->GetDstObject(dsti)->GetName(); if (cnode->GetDstObject(dsti)->GetSrcObjectCount() > 0) output << "<" << cnode->GetDstObject(dsti)->GetSrcObjectCount<FbxSkeleton>() << ">"; } output << ")"; FbxTimeSpan interval; if (cnode->GetAnimationInterval(interval)) { output << ", start : " << interval.GetStart().GetTimeString() << ", end : " << interval.GetStop().GetTimeString() << endl; } else { nodes2del.push_back(cnode); output << ", no interval" << endl; } for (int chi = 0; chi < cnode->GetChannelsCount(); chi++) { int curvecnt = cnode->GetCurveCount(chi); output << "\t\t\tchannel." << chi << " curvecnt : " << curvecnt << endl; for (int ci = 0; ci < curvecnt; ci++) { FbxAnimCurve* curve = cnode->GetCurve(chi, ci); int keycnt = curve->KeyGetCount(); output << "\t\t\t\tcurve no." << ci << " : key count : " << keycnt; output2 << "curve " << ci << endl; vector<int> keys2Remove; for (int cki = 0; cki < keycnt; cki++) { FbxAnimCurveKey prevkey, currkey, nextkey; if (cki == 0 || cki == keycnt - 1) continue; currkey = curve->KeyGet(cki); prevkey = curve->KeyGet(cki-1); nextkey = curve->KeyGet(cki + 1); bool keepit = true; output2 << ci << "-" << cki; // keepit = keepTestHorizon(curve, prevkey, currkey, nextkey); // if (keepit) // keepit = slopkeepTest(curve, prevkey, currkey, nextkey); if (!keepit) { if (!(currkey.GetInterpolation() == FbxAnimCurveDef::EInterpolationType::eInterpolationConstant && nextkey.GetInterpolation() != FbxAnimCurveDef::EInterpolationType::eInterpolationConstant)) keys2Remove.push_back(cki); } } for (int kri = keys2Remove.size() - 1; kri >= 0; kri--) { //curve->KeyRemove(keys2Remove[kri]); } output2 << endl; //output << ", cubic:linear:const : " << cubic << ":" << linear << ":" << cons << endl; if (keys2Remove.size() > 0) output << ", " << keys2Remove.size() << " keys removed"; keycnt = curve->KeyGetCount(); } } } //이부분은 별로 효과없음 /* for (int di = 0; di < nodes2del.size(); di++) { layer->RemoveMember(nodes2del[di]); } */ } } output << "cubic:linear:const " << cubic << ":" << linear << ":" << cons << endl; FbxExporter* exporter = FbxExporter::Create(fm, ""); const char* outFBXName = "after.fbx"; bool exportstatus = exporter->Initialize(outFBXName, -1, fm->GetIOSettings()); if (exportstatus == false) { puts("err export fail"); } exporter->Export(scene); exporter->Destroy(); scene->Destroy(); ios->Destroy(); fm->Destroy(); output.close(); output2.close(); output3.close(); return 0; }
// Deform the vertex array in classic linear way. void fbxLoader2::ComputeLinearDeformation(FbxAMatrix& pGlobalPosition, FbxMesh* pMesh, FbxTime& pTime, FbxPose* pPose, int frame) { // All the links must have the same link mode. FbxCluster::ELinkMode lClusterMode = ((FbxSkin*)pMesh->GetDeformer(0, FbxDeformer::eSkin))->GetCluster(0)->GetLinkMode(); int lVertexCount = pMesh->GetControlPointsCount(); FbxAMatrix* lClusterDeformation = new FbxAMatrix[lVertexCount]; memset(lClusterDeformation, 0, lVertexCount * sizeof(FbxAMatrix)); double* lClusterWeight = new double[lVertexCount]; memset(lClusterWeight, 0, lVertexCount * sizeof(double)); if (lClusterMode == FbxCluster::eAdditive) { for (int i = 0; i < lVertexCount; ++i) { lClusterDeformation[i].SetIdentity(); } } // For all skins and all clusters, accumulate their deformation and weight // on each vertices and store them in lClusterDeformation and lClusterWeight. int lSkinCount = pMesh->GetDeformerCount(FbxDeformer::eSkin); for ( int lSkinIndex=0; lSkinIndex<lSkinCount; ++lSkinIndex) { FbxSkin * lSkinDeformer = (FbxSkin *)pMesh->GetDeformer(lSkinIndex, FbxDeformer::eSkin); int lClusterCount = lSkinDeformer->GetClusterCount(); for ( int lClusterIndex=0; lClusterIndex<lClusterCount; ++lClusterIndex) { FbxCluster* lCluster = lSkinDeformer->GetCluster(lClusterIndex); if (!lCluster->GetLink()) continue; FbxAMatrix lVertexTransformMatrix; ComputeClusterDeformation(pGlobalPosition, pMesh, lCluster, lVertexTransformMatrix, pTime, pPose, frame); //lVertexTransformMatrix.Transpose(); FbxAMatrix identityM; identityM.SetIdentity(); FbxVector4 rotation = lVertexTransformMatrix.GetROnly(); FbxVector4 translation = lVertexTransformMatrix.GetT(); FbxVector4 scaling = lVertexTransformMatrix.GetS(); //rotation = FbxVector4(rotation.mData[0], rotation.mData[1], rotation.mData[2], rotation.mData[3]); //translation = FbxVector4 (translation.mData[0], translation.mData[1], translation.mData[2], translation.mData[3]); //scaling = FbxVector4 (scaling.mData[0], scaling.mData[1], scaling.mData[2], scaling.mData[3]); //lVertexTransformMatrix = FbxAMatrix(translation, rotation, scaling); //lVertexTransformMatrix = FbxAMatrix(translation, rotation, scaling); identityM = lVertexTransformMatrix * identityM; D3DXMATRIX convert = D3DXMATRIX(1,0,0,0, 0,0,1,0, 0,1,0,0, 0,0,0,1); D3DXMATRIX setMatrix = D3DXMATRIX( (float)identityM.mData[0].mData[0], (float)identityM.mData[1].mData[0], (float)identityM.mData[2].mData[0], (float)identityM.mData[3].mData[0], (float)identityM.mData[0].mData[1], (float)identityM.mData[1].mData[1], (float)identityM.mData[2].mData[1], (float)identityM.mData[3].mData[1], (float)identityM.mData[0].mData[2], (float)identityM.mData[1].mData[2], (float)identityM.mData[2].mData[2], (float)identityM.mData[3].mData[2], (float)identityM.mData[0].mData[3], (float)identityM.mData[1].mData[3], (float)identityM.mData[2].mData[3],1); //setMatrix *=0.5f; setMatrix = D3DXMATRIX( (float)identityM.mData[0].mData[0], (float)identityM.mData[1].mData[0], (float)identityM.mData[2].mData[0], (float)identityM.mData[3].mData[0], (float)identityM.mData[0].mData[1], (float)identityM.mData[1].mData[1], (float)identityM.mData[2].mData[1], (float)identityM.mData[3].mData[1], //(float)identityM.mData[0].mData[2], (float)identityM.mData[1].mData[2], (float)identityM.mData[2].mData[2], (float)identityM.mData[3].mData[1], (float)identityM.mData[0].mData[2], (float)identityM.mData[1].mData[2], (float)identityM.mData[2].mData[2], (float)identityM.mData[3].mData[2], (float)identityM.mData[0].mData[3], (float)identityM.mData[1].mData[3], (float)identityM.mData[2].mData[3], 1); //setMatrix = setMatrix*convert; ///////// juz prawie dziala. sprawdz jeszcze te addytywne itp. /// generalnie dodaj to do włosów i dorzuć poprzednią macierz, żeby liczyć przesunięcia. //setMatrix /= 2.54f; //skala jedna jest w cm, druga w inchach, nieważne czy zmieniam system skali ręcznie... bzdurka fbxa std::string nametype = lCluster->GetLink()->GetName(); animationStructure->GetSkeleton(frame)->GetBone(animationStructure->GetSkeleton(frame)->GetBoneByName(lCluster->GetLink()->GetName()))->SetTransformation(setMatrix); }//lClusterCount } delete [] lClusterDeformation; delete [] lClusterWeight; }
// Deform the vertex array in classic linear way. void ComputeLinearDeformation(FbxAMatrix& pGlobalPosition, FbxMesh* pMesh, FbxVector4* someWeights, FbxVectorTemplate4<int>* someBones, AnimationData* aAnimation) { aAnimation; pGlobalPosition; // All the links must have the same link mode. FbxCluster::ELinkMode lClusterMode = ((FbxSkin*)pMesh->GetDeformer(0, FbxDeformer::eSkin))->GetCluster(0)->GetLinkMode(); lClusterMode; int lVertexCount = pMesh->GetControlPointsCount(); for(int i = 0;i < lVertexCount;++i) { someBones[i][0] = -1; someBones[i][1] = -1; someBones[i][2] = -1; someBones[i][3] = -1; someWeights[i][0] = 0.0; someWeights[i][1] = 0.0; someWeights[i][2] = 0.0; someWeights[i][3] = 0.0; } // For all skins and all clusters, accumulate their deformation and weight // on each vertices and store them in lClusterDeformation and lClusterWeight. int lSkinCount = pMesh->GetDeformerCount(FbxDeformer::eSkin); for ( int lSkinIndex=0; lSkinIndex<lSkinCount; ++lSkinIndex) { FbxSkin * lSkinDeformer = (FbxSkin *)pMesh->GetDeformer(lSkinIndex, FbxDeformer::eSkin); int lClusterCount = lSkinDeformer->GetClusterCount(); for ( int lClusterIndex=0; lClusterIndex<lClusterCount; ++lClusterIndex) { FbxCluster* lCluster = lSkinDeformer->GetCluster(lClusterIndex); if (!lCluster->GetLink()) continue; std::string boneName = lCluster->GetLink()->GetName(); int boneId = (int)lCluster->GetLink()->GetUserDataPtr(); FbxAMatrix lVertexTransformMatrix; int lVertexIndexCount = lCluster->GetControlPointIndicesCount(); for (int k = 0; k < lVertexIndexCount; ++k) { int lIndex = lCluster->GetControlPointIndices()[k]; // Sometimes, the mesh can have less points than at the time of the skinning // because a smooth operator was active when skinning but has been deactivated during export. if (lIndex >= lVertexCount) continue; double lWeight = lCluster->GetControlPointWeights()[k]; if (lWeight == 0.0) { continue; } bool foundSpot = false; for(int spot = 0;spot < 4;++spot) { if(someBones[lIndex][spot] == -1) { someBones[lIndex][spot] = boneId; someWeights[lIndex][spot] = lWeight; foundSpot = true; break; } } /*if (lClusterMode == FbxCluster::eAdditive) { // Set the link to 1.0 just to know this vertex is influenced by a link. lClusterWeight[lIndex] = 1.0; } else // lLinkMode == FbxCluster::eNormalize || lLinkMode == FbxCluster::eTotalOne { // Add to the sum of weights to either normalize or complete the vertex. lClusterWeight[lIndex] += lWeight; }*/ }//For each vertex }//lClusterCount } /*for(int i = 0;i < lVertexCount;++i) { if(someBones[i][0] == -1) { someBones[i][0] = 0; } if(someBones[i][1] == -1) { someBones[i][1] = 0; } if(someBones[i][2] == -1) { someBones[i][2] = 0; } if(someBones[i][3] == -1) { someBones[i][3] = 0; } }*/ //Actually deform each vertices here by information stored in lClusterDeformation and lClusterWeight /*for (int i = 0; i < lVertexCount; i++) { FbxVector4 lSrcVertex = pVertexArray[i]; FbxVector4& lDstVertex = pVertexArray[i]; double lWeight = lClusterWeight[i]; // Deform the vertex if there was at least a link with an influence on the vertex, if (lWeight != 0.0) { lDstVertex = lClusterDeformation[i].MultT(lSrcVertex); if (lClusterMode == FbxCluster::eNormalize) { // In the normalized link mode, a vertex is always totally influenced by the links. lDstVertex /= lWeight; } else if (lClusterMode == FbxCluster::eTotalOne) { // In the total 1 link mode, a vertex can be partially influenced by the links. lSrcVertex *= (1.0 - lWeight); lDstVertex += lSrcVertex; } } } delete [] lClusterDeformation; delete [] lClusterWeight;*/ }
void ExportFbxMesh(const Value& obj) { string name = obj["Name"].GetString(); FbxNode* pNode = FbxNode::Create(pManager, name.c_str()); FbxMesh* pMesh = FbxMesh::Create(pManager, name.c_str()); pNode->AddNodeAttribute(pMesh); pScene->GetRootNode()->AddChild(pNode); int numVertex = obj["NumVertex"].GetInt(); { pMesh->InitControlPoints(numVertex); FbxVector4* lControlPoints = pMesh->GetControlPoints(); const Value& pos = obj["Position"]; for (int i = 0; i < numVertex; i++) { double x = pos[i * 3 + 0].GetDouble(); x = -x; double y = pos[i * 3 + 1].GetDouble(); double z = pos[i * 3 + 2].GetDouble(); lControlPoints[i] = FbxVector4(x, y, z); } } { FbxGeometryElementNormal* lGeometryElementNormal = pMesh->CreateElementNormal(); lGeometryElementNormal->SetMappingMode(FbxGeometryElement::eByControlPoint); lGeometryElementNormal->SetReferenceMode(FbxGeometryElement::eDirect); FbxLayerElementArrayTemplate<FbxVector4>& array = lGeometryElementNormal->GetDirectArray(); const Value& normal = obj["Normal"]; for (int i = 0; i < numVertex; i++) { double x = normal[i * 3 + 0].GetDouble(); x = -x; double y = normal[i * 3 + 1].GetDouble(); double z = normal[i * 3 + 2].GetDouble(); array.Add(FbxVector4(x, y, z)); } } { FbxGeometryElementUV* lUVDiffuseElement = pMesh->CreateElementUV("DiffuseUV"); FBX_ASSERT(lUVDiffuseElement != NULL); lUVDiffuseElement->SetMappingMode(FbxGeometryElement::eByControlPoint); lUVDiffuseElement->SetReferenceMode(FbxGeometryElement::eDirect); FbxLayerElementArrayTemplate<FbxVector2>& array = lUVDiffuseElement->GetDirectArray(); const Value& v = obj["UV0"]; for (int i = 0; i < numVertex; i++) { double x = v[i * 2 + 0].GetDouble(); double y = v[i * 2 + 1].GetDouble(); array.Add(FbxVector2(x, y)); } } { const Value& color = obj["Color"]; if (!color.IsNull()) { FbxGeometryElementVertexColor* pColorElement = pMesh->CreateElementVertexColor(); FBX_ASSERT(pColorElement != NULL); pColorElement->SetMappingMode(FbxGeometryElement::eByControlPoint); pColorElement->SetReferenceMode(FbxGeometryElement::eDirect); FbxLayerElementArrayTemplate<FbxColor>& array = pColorElement->GetDirectArray(); for (int i = 0; i < numVertex; i++) { double r = color[i * 4 + 0].GetDouble(); double g = color[i * 4 + 1].GetDouble(); double b = color[i * 4 + 2].GetDouble(); double a = color[i * 4 + 3].GetDouble(); array.Add(FbxColor(r, g, b, a)); } } } { const Value& Indeices = obj["Indeices"]; for (uint32_t subMesh = 0; subMesh < Indeices.Size(); subMesh++) { const Value& index0 = Indeices[subMesh]; int numIndex = index0.Size(); printf("index %d\n", numIndex); for (int i = 0; i < numIndex / 3; i++) { pMesh->BeginPolygon(-1, -1, subMesh); int index[3] = { index0[i * 3 + 0].GetInt(), index0[i * 3 + 1].GetInt(), index0[i * 3 + 2].GetInt(), }; pMesh->AddPolygon(index[0]); pMesh->AddPolygon(index[2]); pMesh->AddPolygon(index[1]); pMesh->EndPolygon(); } } } ////////////////////////////////////////////////////////////////////////// // export skin const Value& boneIndex = obj["BoneIndex"]; if (!boneIndex.IsNull()) { if (fbxBones.empty()) { printf("no bones, can not export skin"); return; } const Value& boneWeight = obj["BoneWeight"]; vector<FbxCluster*> clusters(fbxBones.size(), NULL); for (uint32_t i = 0; i < fbxBones.size(); i++) { FbxCluster* pCluster = FbxCluster::Create(pScene, ""); pCluster->SetLink(fbxBones[i]); pCluster->SetLinkMode(FbxCluster::eTotalOne); clusters[i] = pCluster; } for (int i = 0; i < numVertex; i++) { for (int j = 0; j < 4; j++) { int bone = boneIndex[i * 4 + j].GetInt(); double weight = boneWeight[i * 4 + j].GetDouble(); clusters[bone]->AddControlPointIndex(i, weight); } } FbxSkin* lSkin = FbxSkin::Create(pScene, ""); FbxScene* p = pNode->GetScene(); FbxAMatrix modelMatrix = pNode->EvaluateGlobalTransform(); for (uint32_t i = 0; i < clusters.size(); i++) { clusters[i]->SetTransformMatrix(modelMatrix); FbxAMatrix boneMatrix = fbxBones[i]->EvaluateGlobalTransform(); clusters[i]->SetTransformLinkMatrix(boneMatrix); lSkin->AddCluster(clusters[i]); } pMesh->AddDeformer(lSkin); } }
// Deform the vertex array in Dual Quaternion Skinning way. void compute_dual_quaternion_deformation(FbxAMatrix& pGlobalPosition, FbxMesh* pMesh, FbxTime& pTime, FbxVector4* pVertexArray, FbxPose* pPose) { // All the links must have the same link mode. FbxCluster::ELinkMode lClusterMode = ((FbxSkin*)pMesh->GetDeformer(0, FbxDeformer::eSkin))->GetCluster(0)->GetLinkMode(); int lVertexCount = pMesh->GetControlPointsCount(); int lSkinCount = pMesh->GetDeformerCount(FbxDeformer::eSkin); FbxDualQuaternion* lDQClusterDeformation = new FbxDualQuaternion[lVertexCount]; memset(lDQClusterDeformation, 0, lVertexCount * sizeof(FbxDualQuaternion)); double* lClusterWeight = new double[lVertexCount]; memset(lClusterWeight, 0, lVertexCount * sizeof(double)); // For all skins and all clusters, accumulate their deformation and weight // on each vertices and store them in lClusterDeformation and lClusterWeight. for(int lSkinIndex=0; lSkinIndex<lSkinCount; ++lSkinIndex) { FbxSkin * lSkinDeformer = (FbxSkin *)pMesh->GetDeformer(lSkinIndex, FbxDeformer::eSkin); int lClusterCount = lSkinDeformer->GetClusterCount(); for(int lClusterIndex=0; lClusterIndex<lClusterCount; ++lClusterIndex) { FbxCluster* lCluster = lSkinDeformer->GetCluster(lClusterIndex); if(!lCluster->GetLink()) { continue; } FbxAMatrix lVertexTransformMatrix; compute_cluster_deformation(pGlobalPosition, pMesh, lCluster, lVertexTransformMatrix, pTime, pPose); FbxQuaternion lQ = lVertexTransformMatrix.GetQ(); FbxVector4 lT = lVertexTransformMatrix.GetT(); FbxDualQuaternion lDualQuaternion(lQ, lT); int lVertexIndexCount = lCluster->GetControlPointIndicesCount(); for(int k = 0; k < lVertexIndexCount; ++k) { int lIndex = lCluster->GetControlPointIndices()[k]; // Sometimes, the mesh can have less points than at the time of the skinning // because a smooth operator was active when skinning but has been deactivated during export. if(lIndex >= lVertexCount) { continue; } double lWeight = lCluster->GetControlPointWeights()[k]; if(lWeight == 0.0) { continue; } // Compute the influence of the link on the vertex. FbxDualQuaternion lInfluence = lDualQuaternion * lWeight; if(lClusterMode == FbxCluster::eAdditive) { // Simply influenced by the dual quaternion. lDQClusterDeformation[lIndex] = lInfluence; // Set the link to 1.0 just to know this vertex is influenced by a link. lClusterWeight[lIndex] = 1.0; } else { // lLinkMode == FbxCluster::eNormalize || lLinkMode == FbxCluster::eTotalOne if(lClusterIndex == 0) { lDQClusterDeformation[lIndex] = lInfluence; } else { // Add to the sum of the deformations on the vertex. // Make sure the deformation is accumulated in the same rotation direction. // Use dot product to judge the sign. double lSign = lDQClusterDeformation[lIndex].GetFirstQuaternion().DotProduct(lDualQuaternion.GetFirstQuaternion()); if(lSign >= 0.0) { lDQClusterDeformation[lIndex] += lInfluence; } else { lDQClusterDeformation[lIndex] -= lInfluence; } } // Add to the sum of weights to either normalize or complete the vertex. lClusterWeight[lIndex] += lWeight; } }//For each vertex }//lClusterCount } //Actually deform each vertices here by information stored in lClusterDeformation and lClusterWeight for(int i = 0; i < lVertexCount; i++) { FbxVector4 lSrcVertex = pVertexArray[i]; FbxVector4& lDstVertex = pVertexArray[i]; double lWeightSum = lClusterWeight[i]; // Deform the vertex if there was at least a link with an influence on the vertex, if(lWeightSum != 0.0) { lDQClusterDeformation[i].Normalize(); lDstVertex = lDQClusterDeformation[i].Deform(lDstVertex); if(lClusterMode == FbxCluster::eNormalize) { // In the normalized link mode, a vertex is always totally influenced by the links. lDstVertex /= lWeightSum; } else if(lClusterMode == FbxCluster::eTotalOne) { // In the total 1 link mode, a vertex can be partially influenced by the links. lSrcVertex *= (1.0 - lWeightSum); lDstVertex += lSrcVertex; } } } delete [] lDQClusterDeformation; delete [] lClusterWeight; }
Bone::Bone(FbxScene& scene) { //construct hierarchy *this = Bone { *(scene.GetRootNode()) }; std::map<std::string, std::pair<unsigned int, scm::math::mat4f> > bone_info{} ; unsigned num_bones = 0; for (unsigned int i = 0; i < scene.GetGeometryCount(); i++) { FbxGeometry* geo = scene.GetGeometry(i); if (geo->GetAttributeType() == FbxNodeAttribute::eMesh) { //check for skinning, use first skin deformer FbxSkin* skin; for (unsigned i = 0; i < geo->GetDeformerCount(); ++i) { FbxDeformer* defPtr = { geo->GetDeformer(i) }; if (defPtr->GetDeformerType() == FbxDeformer::eSkin) { skin = static_cast<FbxSkin*>(defPtr); break; } } if (!skin) { Logger::LOG_ERROR << "Mesh does not contain skin deformer" << std::endl; assert(false); } //one cluster corresponds to one bone for (unsigned i = 0; i < skin->GetClusterCount(); ++i) { FbxCluster* cluster = skin->GetCluster(i); FbxNode* node = cluster->GetLink(); if (!node) { Logger::LOG_ERROR << "associated node does not exist!" << std::endl; assert(false); } std::string bone_name(node->GetName()); //helper to check for matrix magnitude auto magnitude = [](scm::math::mat4f const & mat)->float { float mag = 0.0f; for (unsigned i = 0; i < 16; ++i) { mag += mat[i] * mat[i]; } return mag; } ; //reference pose of bone FbxAMatrix bind_transform; cluster->GetTransformLinkMatrix(bind_transform); //check if the clusters have an extra transformation FbxAMatrix cluster_transform; cluster->GetTransformMatrix(cluster_transform); if (magnitude(to_gua::mat4f(cluster_transform) - scm::math::mat4f::identity()) > 0.000000001f) { Logger::LOG_WARNING << "weight cluster of bone '" << bone_name << "' has transformation, animation will be skewed" << std::endl; } //add bone to list if it is not already included if (bone_info.find(bone_name) == bone_info.end()) { bone_info[bone_name] = std::make_pair( num_bones, to_gua::mat4f(bind_transform.Inverse() * cluster_transform)); ++num_bones; } } // traverse hierarchy and set accumulated values in the bone this->set_properties(bone_info); } } }
//=============================================================================================================================== void FBXLoader::LoadJointsAndAnimation(FbxNode* inNode) { // This is how each subset gets its bone/joint for animation FBXSubsets* subset = mSubsets[iCurrentSubset]; FbxMesh* mesh = inNode->GetMesh(); uint32 numOfDeformers = mesh->GetDeformerCount(); FbxAMatrix geomTrans = ZShadeSandboxMesh::FBXHelper::GetGeometryTransformation(inNode); // A deformer contains clusters. // A cluster contains a link, which is a joint. // Normally, There is only one deformer in a mesh but Maya has many types. for (uint32 deformerIndex = 0; deformerIndex < numOfDeformers; ++deformerIndex) { // Lets see if this deformer is a skin FbxSkin* skin = reinterpret_cast<FbxSkin*>(mesh->GetDeformer(deformerIndex, FbxDeformer::eSkin)); if (!skin) continue; uint32 numOfClusters = skin->GetClusterCount(); for (uint32 clusterIndex = 0; clusterIndex < numOfClusters; ++clusterIndex) { FbxCluster* cluster = skin->GetCluster(clusterIndex); string jointName = cluster->GetLink()->GetName(); uint32 jointIndex = FindJointIndexUsingName(jointName); subset->mJoints.push_back(jointIndex); FbxAMatrix transform; FbxAMatrix transformLink; FbxAMatrix globalBindposeInverse; // The transformation of the mesh at binding time cluster->GetTransformMatrix(transform); // The transformation of the cluster (joint) at binding time from joint space to world space cluster->GetTransformLinkMatrix(transformLink); globalBindposeInverse = transformLink.Inverse() * transform * geomTrans; // Update skeletal information mSkeleton.joints[jointIndex].globalBindposeInverse = globalBindposeInverse; mSkeleton.joints[jointIndex].node = cluster->GetLink(); // Associate each joint with the control points it affects uint32 numOfIndices = cluster->GetControlPointIndicesCount(); for (uint32 i = 0; i < numOfIndices; ++i) { ZShadeSandboxMesh::BlendingIndexWeightPair currBlendingIndexWeightPair; currBlendingIndexWeightPair.blendingIndex = jointIndex; currBlendingIndexWeightPair.blendingWeight = cluster->GetControlPointWeights()[i]; mControlPoints[cluster->GetControlPointIndices()[i]]->blendingInfo.push_back(currBlendingIndexWeightPair); } // Animation information FbxAnimStack* animStack = m_pFbxScene->GetSrcObject<FbxAnimStack>(0); FbxString animStackName = animStack->GetName(); mAnimationName = animStackName.Buffer(); FbxTakeInfo* takeInfo = m_pFbxScene->GetTakeInfo(animStackName); FbxTime start = takeInfo->mLocalTimeSpan.GetStart(); FbxTime end = takeInfo->mLocalTimeSpan.GetStop(); mAnimationLength = end.GetFrameCount(FbxTime::eFrames24) - start.GetFrameCount(FbxTime::eFrames24) + 1; FBXKeyframe** anim = &mSkeleton.joints[jointIndex].animation; for (FbxLongLong i = start.GetFrameCount(FbxTime::eFrames24); i <= end.GetFrameCount(FbxTime::eFrames24); ++i) { FbxTime time; time.SetFrame(i, FbxTime::eFrames24); *anim = new FBXKeyframe(); (*anim)->frameNum = i; FbxAMatrix currentTransformOffset = inNode->EvaluateGlobalTransform(time) * geomTrans; (*anim)->globalTransform = currentTransformOffset.Inverse() * cluster->GetLink()->EvaluateGlobalTransform(time); anim = &((*anim)->next); } } } // Some control points have less than 4 joints // For a normal renderer, there are usually 4 joints ZShadeSandboxMesh::BlendingIndexWeightPair currBlendingIndexWeightPair; currBlendingIndexWeightPair.blendingIndex = 0; currBlendingIndexWeightPair.blendingWeight = 0; for (auto itr = mControlPoints.begin(); itr != mControlPoints.end(); ++itr) { for (unsigned int i = itr->second->blendingInfo.size(); i <= 4; ++i) { itr->second->blendingInfo.push_back(currBlendingIndexWeightPair); } } }
void FbxLoader::ProcessBoneAndAnimation(FbxNode* node, Node& meshNode) { auto currMesh = node->GetMesh(); if(!currMesh) return; FbxVector4 lT = node->GetGeometricTranslation(FbxNode::eSourcePivot); FbxVector4 lR = node->GetGeometricRotation(FbxNode::eSourcePivot); FbxVector4 lS = node->GetGeometricScaling(FbxNode::eSourcePivot); FbxAMatrix geometryTransform = FbxAMatrix(lT, lR, lS); FbxSkin* skin = nullptr; const int deformerCnt = currMesh->GetDeformerCount(); for(int deformerIndex = 0; deformerIndex < deformerCnt; ++deformerIndex) { skin = (FbxSkin*)(currMesh->GetDeformer(deformerIndex, FbxDeformer::eSkin)); if(skin) break; } if(!skin) return; meshNode.useSkinnedMesh = true; const size_t nClusters = skin->GetClusterCount(); if(nClusters < 1) return; for(auto& clip : animationClips) clip.second->transformCurves.resize(nClusters); meshNode.bones.resize(nClusters); const int animCount = scene->GetSrcObjectCount<FbxAnimStack>(); float time = 0; for(int ci = 0; ci < nClusters; ++ci) { FbxCluster* cluster = skin->GetCluster(ci); auto elink = cluster->GetLinkMode(); std::string boneName = cluster->GetLink()->GetName(); FbxAMatrix transformMatrix, transformLinkMatrix, globalBindposeInverse; cluster->GetTransformMatrix(transformMatrix); cluster->GetTransformLinkMatrix(transformLinkMatrix); globalBindposeInverse = transformLinkMatrix.Inverse() * geometryTransform * transformMatrix; FbxNode* boneNode = cluster->GetLink(); Bone& bone = meshNode.bones[ci]; bone.name = boneName; bone.index = ci; auto T = globalBindposeInverse.GetT() * factor; if(axismode == eLeftHanded) { auto R = globalBindposeInverse.GetR(); T[0] *= -1; R[1] *= -1; R[2] *= -1; globalBindposeInverse.SetR(R); } globalBindposeInverse.SetT(T); ConvertMatrix(bone.bindPoseInverse, globalBindposeInverse); const int nCtrl = cluster->GetControlPointIndicesCount(); for(int ctrlIndex = 0; ctrlIndex < nCtrl; ++ctrlIndex) { BlendWeightPair pair; pair.boneIndex = ci; pair.weight = (float)cluster->GetControlPointWeights()[ctrlIndex]; meshNode.controlPoints[cluster->GetControlPointIndices()[ctrlIndex]].blendWeigths.push_back(pair); } FbxAMatrix preRot; auto preR = boneNode->GetPreRotation(FbxNode::eSourcePivot); preRot.SetR(preR); for(int ai = 0; ai < animCount; ++ai) { auto animStack = scene->GetSrcObject<FbxAnimStack>(ai); scene->SetCurrentAnimationStack(animStack); std::string animName = animStack->GetName(); auto& clip = animationClips[animName]; auto& transformCurve = clip->transformCurves[ci]; transformCurve.boneName = boneName; transformCurve.begin = 0; auto animLayer = animStack->GetMember<FbxAnimLayer>(0); FbxAnimCurve* fbxTCurves[3]; FbxAnimCurve* fbxRCurves[3]; FbxAnimCurve* fbxSCurves[3]; fbxTCurves[0] = boneNode->LclTranslation.GetCurve(animLayer, "X"); if(!fbxTCurves[0]) continue; fbxTCurves[1] = boneNode->LclTranslation.GetCurve(animLayer, "Y"); fbxTCurves[2] = boneNode->LclTranslation.GetCurve(animLayer, "Z"); fbxRCurves[0] = boneNode->LclRotation.GetCurve(animLayer, "X"); fbxRCurves[1] = boneNode->LclRotation.GetCurve(animLayer, "Y"); fbxRCurves[2] = boneNode->LclRotation.GetCurve(animLayer, "Z"); fbxSCurves[0] = boneNode->LclScaling.GetCurve(animLayer, "X"); fbxSCurves[1] = boneNode->LclScaling.GetCurve(animLayer, "Y"); fbxSCurves[2] = boneNode->LclScaling.GetCurve(animLayer, "Z"); // set & apply filter FbxAnimCurveFilterKeyReducer keyReducer; keyReducer.SetKeySync(false); keyReducer.Apply(fbxTCurves, 3); keyReducer.Apply(fbxSCurves, 3); keyReducer.SetKeySync(true); keyReducer.Apply(fbxRCurves, 3); FbxAnimCurveFilterUnroll unroll; unroll.SetForceAutoTangents(true); unroll.Apply(fbxRCurves, 3); FbxAnimCurveFilterTSS tss; FbxTime tt; tt.SetSecondDouble(-fbxTCurves[0]->KeyGetTime(0).GetSecondDouble()); tss.SetShift(tt); tss.Apply(fbxTCurves, 3); tss.Apply(fbxRCurves, 3); tss.Apply(fbxSCurves, 3); // // process curves if(fbxTCurves[0]->KeyGetCount() > 0) { ProcessAnimCurveT(fbxTCurves, transformCurve); ProcessAnimCurveS(fbxSCurves, transformCurve); ProcessAnimCurveR(fbxRCurves, transformCurve, preRot); //clamping by reduced keyframes clip->startTime = 0; clip->lengthInSeconds = transformCurve.end; clip->lengthInFrames = (int)(1.5f + (transformCurve.end / (1 / 30.0f))); } } // animations loop } // cluster loop }
void fbxLoader2::readAnimationWeigths(FbxMesh* mesh) { if(!animationStructure) { animationStructure = new AnimationData(); } int numSkins = mesh->GetDeformerCount(FbxDeformer::eSkin); weightsNumber = 0; for (int l = 0; l<this->vertexMaxCount; l++) { for(int p = 0; p<4; p++) { this->vertexArray[l].weights[p].boneID = 0; this->vertexArray[l].weights[p].weight = 0.0f; } } int next = 0; int numindicestest = 0; for(int i = 0; i < numSkins; i++) { FbxSkin* skin = (FbxSkin*) mesh->GetDeformer(i, FbxDeformer::eSkin); int clusterCount = skin->GetClusterCount(); if( clusterCount == 0 ) continue; for (int j = 0; j < clusterCount; j++) { FbxCluster* cluster = skin->GetCluster(j);///trzeba jakoś zrobić wszystkie linki... edit, have no idea what i meant xD FbxNode* bone = cluster->GetLink(); if( !bone ) { continue; } int numInfluencedVertices = cluster->GetControlPointIndicesCount(); int* pIndexArray = cluster->GetControlPointIndices(); numindicestest += cluster->GetControlPointIndicesCount(); double *pWeightArray = cluster->GetControlPointWeights(); for(int iControlPoint = 0; iControlPoint < numInfluencedVertices; iControlPoint++) { /*int iControlPointIndex = pIndexArray[iControlPoint]; ////////////muszę poprawić indeksy wierzchołków. teraz odwoluje sie do vertow afaik int boneIndex = skeleton->GetBoneByName(bone->GetName()); int boneI = skeleton->GetBoneIndex(boneIndex); //boneIndices[iControlPointIndex] = iCluster; double weightAdd = pWeightArray[iControlPoint]; weightsStructure* added = new weightsStructure(boneI, iControlPointIndex, weightAdd); this->weights[next] = added; next++;*/ int iControlPointIndex = pIndexArray[iControlPoint]; int boneIndex = skeleton->GetBoneByName(bone->GetName()); int k = 0; if(vertexArray[iControlPointIndex].weights[0].weight == 0.0f) { vertexArray[iControlPointIndex].weights[0].boneID = skeleton->GetBoneIndex(boneIndex); vertexArray[iControlPointIndex].weights[0].weight = pWeightArray[iControlPoint]; vertexArray[iControlPointIndex].weightsNum = 1; } else { while (k < 3 && vertexArray[iControlPointIndex].weights[k].weight != 0.0f) { k++; } vertexArray[iControlPointIndex].weights[k].boneID = skeleton->GetBoneIndex(boneIndex); vertexArray[iControlPointIndex].weights[k].weight = pWeightArray[iControlPoint]; vertexArray[iControlPointIndex].weightsNum++; } } } } }
//----------------------------------------------------------------------------------- static void GetSkinWeights(SceneImport* import, std::vector<SkinWeight>& skinWeights, const FbxMesh* mesh, std::map<int, FbxNode*>& nodeToJointIndex) { int controlPointCount = mesh->GetControlPointsCount(); skinWeights.resize(controlPointCount); for (size_t i = 0; i < skinWeights.size(); ++i) { skinWeights[i].indices = Vector4Int(0, 0, 0, 0); skinWeights[i].weights = Vector4(0.0f, 0.0f, 0.0f, 0.0f); } int deformerCount = mesh->GetDeformerCount((FbxDeformer::eSkin)); ASSERT_OR_DIE(deformerCount == 1, "Deformer count wasn't 1"); for (int deformerIndex = 0; deformerIndex < deformerCount; ++deformerIndex) { FbxSkin* skin = (FbxSkin*)mesh->GetDeformer(deformerIndex, FbxDeformer::eSkin); if (skin == nullptr) { continue; } //Clusters are a link between this skin object, bones, and the verts that bone affects. int clusterCount = skin->GetClusterCount(); for (int clusterIndex = 0; clusterIndex < clusterCount; ++clusterIndex) { FbxCluster* cluster = skin->GetCluster(clusterIndex); const FbxNode* linkNode = cluster->GetLink(); //Not associated with a bone? Ignore it, we don't care about it if (linkNode == nullptr) { continue; } int jointIndex = Skeleton::INVALID_JOINT_INDEX; for (auto iter = nodeToJointIndex.begin(); iter != nodeToJointIndex.end(); ++iter) { if (iter->second == linkNode) { jointIndex = iter->first; break; } } if (jointIndex == Skeleton::INVALID_JOINT_INDEX) { continue; } int* controlPointIndices = cluster->GetControlPointIndices(); int indexCount = cluster->GetControlPointIndicesCount(); double* weights = cluster->GetControlPointWeights(); for (int i = 0; i < indexCount; ++i) { int controlIndex = controlPointIndices[i]; double weight = weights[i]; SkinWeight* skinWeight = &skinWeights[controlIndex]; AddHighestWeightWhileKickingTheLowestWeightOut(skinWeight, jointIndex, (float)weight); } } } for (SkinWeight& sw : skinWeights) { //Renormalize all the skin weights //Be sure to set weights such that all weights add up to 1 //All weights should add up to 1, so things that were all zeroes add up to 1 //If skin-weights were never added, make sure you set it to 1, 0, 0, 0 float totalWeight = sw.weights.x + sw.weights.y + sw.weights.z + sw.weights.w; if(totalWeight > 0.0f) { sw.weights /= totalWeight; } else { sw.weights = { 1.0f, 0.0f, 0.0f, 0.0f }; } } }