void FbxLoader::LoadAnimationClipData() { // load animation data const int animCount = scene->GetSrcObjectCount<FbxAnimStack>(); for(int ac = 0; ac < animCount; ++ac) { FbxAnimStack* currAnimStack = scene->GetSrcObject<FbxAnimStack>(ac); if(!currAnimStack) return; FbxString animStackName = currAnimStack->GetName(); FbxTakeInfo* takeInfo = scene->GetTakeInfo(animStackName); if(!takeInfo) return; FbxTime start = takeInfo->mLocalTimeSpan.GetStart(); FbxTime stop = takeInfo->mLocalTimeSpan.GetStop(); FbxTime::EMode mode = FbxTime::eDefaultMode; int startFrame = (int)start.GetFrameCount(mode); int stopFrame = (int)stop.GetFrameCount(mode); int animLength = stopFrame - startFrame + 1; shared_ptr<AnimClip> clip = make_shared<AnimClip>(); clip->name = animStackName; animationClips[clip->name] = clip; } }
std::string OsgFbxReader::readFbxAnimation(FbxNode* pNode, const char* targetName) { std::string result; for (int i = 0; i < fbxScene.GetSrcObjectCount<FbxAnimStack>(); ++i) { FbxAnimStack* pAnimStack = FbxCast<FbxAnimStack>(fbxScene.GetSrcObject<FbxAnimStack>(i)); int nbAnimLayers = pAnimStack->GetMemberCount<FbxAnimLayer>(); const char* pTakeName = pAnimStack->GetName(); if (!pTakeName || !*pTakeName) continue; for (int j = 0; j < nbAnimLayers; j++) { FbxAnimLayer* pAnimLayer = pAnimStack->GetMember<FbxAnimLayer>(j); osgAnimation::Animation* pAnimation = ::readFbxAnimation(pNode, pAnimLayer, pTakeName, targetName, pAnimationManager); if (pAnimation) { result = targetName; } } } return result; }
bool Animation::save(QString file) { // ex: // create a SdkManager FbxManager* lSdkManager = FbxManager::Create(); // create an IOSettings object FbxIOSettings* ios = FbxIOSettings::Create(lSdkManager, IOSROOT); // set some IOSettings options ios->SetBoolProp(EXP_FBX_MATERIAL, true); ios->SetBoolProp(EXP_FBX_TEXTURE, true); ios->SetBoolProp(EXP_FBX_SHAPE, true); ios->SetBoolProp(EXP_FBX_GOBO, true); ios->SetBoolProp(EXP_FBX_ANIMATION, true); ios->SetBoolProp(EXP_FBX_GLOBAL_SETTINGS, true); // create an empty scene FbxScene* pScene = FbxScene::Create(lSdkManager, m_name.toStdString().c_str()); QMap<int,FbxNode*> nodes; FbxNode* lRootNode = pScene->GetRootNode(); // Create the Animation Stack FbxAnimStack* lAnimStack = FbxAnimStack::Create(pScene, "Show all faces"); // The animation nodes can only exist on AnimLayers therefore it is mandatory to // add at least one AnimLayer to the AnimStack. And for the purpose of this example, // one layer is all we need. FbxAnimLayer* lAnimLayer = FbxAnimLayer::Create(pScene, "Base animation Layer"); lAnimStack->AddMember(lAnimLayer); int time = 0; foreach (const Frame &frame, m_frames) { time += frame.elapsedTime(); foreach (const Marker &marker, frame.markers()) { int id = marker.id(); if(! nodes.contains(id)) { FbxNode *newMarker = WccToFbxExporter::createSphere(pScene, (QString("marker_")+QString::number(id)).toStdString().c_str() , 1.0f); nodes[id] = newMarker; lRootNode->AddChild(newMarker); } WccToFbxExporter::addPositionKeyToNode(nodes[id], lAnimLayer, time, marker.position()*m_roomDimensions); } }
void Tools::DisplayAnimation::DisplayAnimation(FbxScene* i_scene) { DisplayCommon::DisplayString( "\n\n--------------------\nAnimation\n--------------------" ); int i; for ( i = 0; i < i_scene->GetSrcObjectCount<FbxAnimStack>(); i++ ) { FbxAnimStack *animStack = i_scene->GetSrcObject<FbxAnimStack>( i ); FbxString string = "Animation Stack Name: "; string += animStack->GetName(); string += "\n"; FBXSDK_printf( string ); DisplayCommon::DisplayString( string ); DisplayAnimation( animStack, i_scene->GetRootNode(), true ); DisplayAnimation( animStack, i_scene->GetRootNode() ); } }
void fbxLoader2::readAnimationTakeData(FbxNode* node) { FbxAnimStack* pAnimStack = FbxCast<FbxAnimStack>(scene->GetSrcObject(FBX_TYPE(FbxAnimStack))); FbxAnimLayer* pAnimLayer = pAnimStack->GetMember(FBX_TYPE(FbxAnimLayer)); FbxAnimCurve* animCv = node->LclTranslation.GetCurve(pAnimLayer, FBXSDK_CURVENODE_COMPONENT_X); FbxTimeSpan length = FbxTimeSpan(); int p = animCv->KeyGetCount(); const char* nameAnim = animCv->GetName(); const size_t len = strlen(nameAnim); char * new_name = new char[len + 1]; strncpy(new_name, nameAnim, len); animCv->GetTimeInterval(length); FbxTime duration = length.GetDuration(); FbxTime::EMode mode = duration.GetGlobalTimeMode(); double frameRate = duration.GetFrameRate(mode); double startt = length.GetStart().GetMilliSeconds(); double endt = length.GetStop().GetMilliSeconds(); int frames = animCv->KeyGetCount(); animationStructure = new AnimationData(new_name, startt, endt, (int)frameRate, frames); for (int i = 0; i< frames; i++) { SkeletalData *sk = new SkeletalData(); for(int j = 0; j<skeleton->GetBonesCount(); j++) { BoneData *bonecopy = new BoneData(); bonecopy->SetID(skeleton->GetBone(j)->GetID()); bonecopy->SetName(skeleton->GetBone(j)->GetName()); bonecopy->SetParent(skeleton->GetBone(j)->GetParent()); sk->SetBones(bonecopy); } animationStructure->SetSkeleton(sk, i); } }
int main(int argc, char** argv) { if (argc < 2) { printf("Usage: emdfbx model.emd skeleton.esk output.fbx\n Can include multiple emd and esk files into one fbx."); getchar(); return 1; } LibXenoverse::initializeDebuggingLog(); vector<string> model_filenames; vector<string> skeleton_filenames; vector<string> animation_filenames; string export_filename = ""; for (int i = 1; i < argc; i++) { string parameter = ToString(argv[i]); string extension = LibXenoverse::extensionFromFilename(parameter); if (extension == "emd") { model_filenames.push_back(parameter); } if (extension == "esk") { skeleton_filenames.push_back(parameter); } if (extension == "ean") { animation_filenames.push_back(parameter); } if (extension == "fbx") { export_filename = parameter; } } if (!export_filename.size()) { if (model_filenames.size()) { export_filename = model_filenames[0] + ".fbx"; } else if (skeleton_filenames.size()) { export_filename = skeleton_filenames[0] + ".fbx"; } else { export_filename = "Out.fbx"; } } // Create FBX Manager FbxManager *sdk_manager = FbxManager::Create(); FbxIOSettings *ios = FbxIOSettings::Create(sdk_manager, IOSROOT); ios->SetBoolProp(EXP_FBX_EMBEDDED, true); sdk_manager->SetIOSettings(ios); // Create Scene vector<FbxNode *> global_fbx_bones; global_fbx_bones.reserve(300); vector<size_t> global_fbx_bones_index; //TODO find a better way to link skeleton and animations global_fbx_bones_index.reserve(300); vector<FbxAnimCurveNode *> global_fbx_animation; global_fbx_animation.reserve(300); FbxScene *scene = FbxScene::Create(sdk_manager, "EMDFBXScene"); // Load Shaders and convert it to fx file (will be use by fbx). vector<string> shader_names; shader_names.push_back("adam_shader/shader_age_vs.emb"); //must specified vs folloxed by ps shaders shader_names.push_back("adam_shader/shader_age_ps.emb"); shader_names.push_back("adam_shader/shader_default_vs.emb"); shader_names.push_back("adam_shader/shader_default_ps.emb"); bool needs_install_shaders = false; for (size_t i = 0; i < shader_names.size(); i++) { if (!LibXenoverse::fileCheck(shader_names[i])) { needs_install_shaders = true; break; } } if (needs_install_shaders) { printf("Shaders not found. Please use Xenoviewer to prepare shaders in bin folder."); return -1; } for (size_t i = 0; i+1 < shader_names.size(); i+=2) { LibXenoverse::EMB *shader_pack_vs = new LibXenoverse::EMB(); LibXenoverse::EMB *shader_pack_ps = new LibXenoverse::EMB(); if (!shader_pack_vs->load(shader_names[i])) { delete shader_pack_vs; printf("Couldn't load Shader Pack %s. File is either missing, open by another application, or corrupt.", shader_names[i].c_str()); continue; } if (!shader_pack_ps->load(shader_names[i+1])) { delete shader_pack_vs; delete shader_pack_ps; printf("Couldn't load Shader Pack %s. File is either missing, open by another application, or corrupt.", shader_names[i].c_str()); continue; } shader_pack_vs->exportShadersToFx(shader_pack_vs, shader_pack_ps); //convert all shaders in fx file with defaults program parameters (like Ogre's version). } //build FBX skeleton for (size_t i = 0; i < skeleton_filenames.size(); i++) { LibXenoverse::ESK *esk_skeleton = new LibXenoverse::ESK(); esk_skeleton->load(skeleton_filenames[i]); vector<FbxNode *> fbx_bones = esk_skeleton->createFBXSkeleton(scene); for (size_t j = 0; j < fbx_bones.size(); j++) { global_fbx_bones.push_back(fbx_bones[j]); global_fbx_bones_index.push_back(j); //kepp index of bone to link with animations. } } //build FBX animations for (size_t i = 0; i < animation_filenames.size(); i++) { string filename = animation_filenames.at(i); string ean_name = LibXenoverse::nameFromFilenameNoExtension(filename, true); //vector<FbxAnimCurveNode *> global_fbx_animation; LibXenoverse::EAN *animation = new LibXenoverse::EAN(); if (animation->load(filename)) { std::vector<FbxAnimStack *> list_AnimStack; size_t nbAnims = animation->getAnimations().size(); for (size_t j = 0; j < nbAnims; j++) { //we create only one stack and one layer by animation. each will animate all bones of all skeleton. LibXenoverse::EANAnimation *anim_tmp = &(animation->getAnimations().at(j)); FbxAnimStack* lAnimStack = FbxAnimStack::Create(scene, anim_tmp->getName().c_str()); FbxAnimLayer* lAnimLayer = FbxAnimLayer::Create(scene, (anim_tmp->getName() + "_Layer0").c_str()); lAnimStack->AddMember(lAnimLayer); list_AnimStack.push_back(lAnimStack); } size_t k = 0; for (vector<FbxNode *>::iterator it = global_fbx_bones.begin(); it != global_fbx_bones.end(); it++) { vector<FbxAnimCurveNode *> fbx_anim = animation->exportFBXAnimations(scene, list_AnimStack, *it, global_fbx_bones_index.at(k)); for (size_t j = 0; j < fbx_anim.size(); j++) { global_fbx_animation.push_back(fbx_anim[j]); } k++; } } else { delete animation; animation = NULL; } } for (size_t i = 0; i < model_filenames.size(); i++) { string node_name = LibXenoverse::nameFromFilenameNoExtension(model_filenames[i]); string path = model_filenames[i].substr(0, model_filenames[i].size() - LibXenoverse::nameFromFilename(model_filenames[i]).size()); LibXenoverse::EMD *emd_model = new LibXenoverse::EMD(); emd_model->load(model_filenames[i]); // Fill Scene FbxNode *lMeshNode = NULL; // Create Node lMeshNode = FbxNode::Create(scene, node_name.c_str()); lMeshNode->LclTranslation.Set(FbxVector4(0, 0, 0)); lMeshNode->LclScaling.Set(FbxVector4(1, 1, 1)); lMeshNode->LclRotation.Set(FbxVector4(0, 0, 0)); // Create and attach Mesh FbxMesh *lMesh = emd_model->exportFBX(scene, lMeshNode, path); lMeshNode->SetNodeAttribute(lMesh); if (global_fbx_bones.size()) { emd_model->exportFBXSkin(scene, lMesh, global_fbx_bones, lMeshNode->EvaluateGlobalTransform()); } // Add node to scene FbxNode *lRootNode = scene->GetRootNode(); lRootNode->AddChild(lMeshNode); } // Export FBX int lFileFormat = sdk_manager->GetIOPluginRegistry()->GetNativeWriterFormat(); FbxExporter* lExporter = FbxExporter::Create(sdk_manager, ""); bool lExportStatus = lExporter->Initialize(export_filename.c_str(), lFileFormat, sdk_manager->GetIOSettings()); if (!lExportStatus) { printf("Call to FbxExporter::Initialize() failed.\n"); printf("Error returned: %s\n\n", lExporter->GetStatus().GetErrorString()); return 1; } scene->GetGlobalSettings().SetAxisSystem(FbxAxisSystem::eMax); scene->GetGlobalSettings().SetSystemUnit(FbxSystemUnit::m); // Export scene lExporter->Export(scene); lExporter->Destroy(); return 0; }
int32 UnFbx::FFbxImporter::GetMaxSampleRate(TArray<FbxNode*>& SortedLinks, TArray<FbxNode*>& NodeArray) { int32 MaxStackResampleRate = 0; int32 AnimStackCount = Scene->GetSrcObjectCount<FbxAnimStack>(); for( int32 AnimStackIndex = 0; AnimStackIndex < AnimStackCount; AnimStackIndex++) { FbxAnimStack* CurAnimStack = Scene->GetSrcObject<FbxAnimStack>(AnimStackIndex); FbxTimeSpan AnimStackTimeSpan = GetAnimationTimeSpan(SortedLinks[0], CurAnimStack); double AnimStackStart = AnimStackTimeSpan.GetStart().GetSecondDouble(); double AnimStackStop = AnimStackTimeSpan.GetStop().GetSecondDouble(); FbxAnimLayer* AnimLayer = (FbxAnimLayer*)CurAnimStack->GetMember(0); for(int32 LinkIndex = 0; LinkIndex < SortedLinks.Num(); ++LinkIndex) { FbxNode* CurrentLink = SortedLinks[LinkIndex]; FbxAnimCurve* Curves[6]; Curves[0] = CurrentLink->LclTranslation.GetCurve(AnimLayer, FBXSDK_CURVENODE_COMPONENT_X, false); Curves[1] = CurrentLink->LclTranslation.GetCurve(AnimLayer, FBXSDK_CURVENODE_COMPONENT_Y, false); Curves[2] = CurrentLink->LclTranslation.GetCurve(AnimLayer, FBXSDK_CURVENODE_COMPONENT_Z, false); Curves[3] = CurrentLink->LclRotation.GetCurve(AnimLayer, FBXSDK_CURVENODE_COMPONENT_X, false); Curves[4] = CurrentLink->LclRotation.GetCurve(AnimLayer, FBXSDK_CURVENODE_COMPONENT_Y, false); Curves[5] = CurrentLink->LclRotation.GetCurve(AnimLayer, FBXSDK_CURVENODE_COMPONENT_Z, false); for(int32 CurveIndex = 0; CurveIndex < 6; ++CurveIndex) { FbxAnimCurve* CurrentCurve = Curves[CurveIndex]; if(CurrentCurve) { int32 KeyCount = CurrentCurve->KeyGetCount(); FbxTimeSpan TimeInterval(FBXSDK_TIME_INFINITE,FBXSDK_TIME_MINUS_INFINITE); bool bValidTimeInterval = CurrentCurve->GetTimeInterval(TimeInterval); if(KeyCount > 1 && bValidTimeInterval) { double KeyAnimLength = TimeInterval.GetDuration().GetSecondDouble(); double KeyAnimStart = TimeInterval.GetStart().GetSecondDouble(); double KeyAnimStop = TimeInterval.GetStop().GetSecondDouble(); if(KeyAnimLength != 0.0) { // 30 fps animation has 31 keys because it includes index 0 key for 0.0 second int32 NewRate = FPlatformMath::RoundToInt((KeyCount-1) / KeyAnimLength); MaxStackResampleRate = FMath::Max(NewRate, MaxStackResampleRate); } } } } } } // Make sure we're not hitting 0 for samplerate if ( MaxStackResampleRate != 0 ) { return MaxStackResampleRate; } return DEFAULT_SAMPLERATE; }
/** * Add to the animation set, the animations contained within the FBX document, for the given skeleton */ UAnimSequence * UnFbx::FFbxImporter::ImportAnimations(USkeleton* Skeleton, UObject* Outer, TArray<FbxNode*>& SortedLinks, const FString& Name, UFbxAnimSequenceImportData* TemplateImportData, TArray<FbxNode*>& NodeArray) { // we need skeleton to create animsequence if (Skeleton == NULL) { return NULL; } int32 ValidTakeCount = 0; if (IsValidAnimationData(SortedLinks, NodeArray, ValidTakeCount) == false) { AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Warning, LOCTEXT("FBXImport_InvalidAnimationData", "This does not contain any valid animation takes.")), FFbxErrors::Animation_InvalidData); return NULL; } UAnimSequence* LastCreatedAnim = NULL; int32 ResampleRate = DEFAULT_SAMPLERATE; if ( ImportOptions->bResample ) { // For FBX data, "Frame Rate" is just the speed at which the animation is played back. It can change // arbitrarily, and the underlying data can stay the same. What we really want here is the Sampling Rate, // ie: the number of animation keys per second. These are the individual animation curve keys // on the FBX nodes of the skeleton. So we loop through the nodes of the skeleton and find the maximum number // of keys that any node has, then divide this by the total length (in seconds) of the animation to find the // sampling rate of this set of data // we want the maximum resample rate, so that we don't lose any precision of fast anims, // and don't mind creating lerped frames for slow anims int32 MaxStackResampleRate = GetMaxSampleRate(SortedLinks, NodeArray); if(MaxStackResampleRate != 0) { ResampleRate = MaxStackResampleRate; } } int32 AnimStackCount = Scene->GetSrcObjectCount<FbxAnimStack>(); for( int32 AnimStackIndex = 0; AnimStackIndex < AnimStackCount; AnimStackIndex++ ) { FbxAnimStack* CurAnimStack = Scene->GetSrcObject<FbxAnimStack>(AnimStackIndex); FbxTimeSpan AnimTimeSpan = GetAnimationTimeSpan(SortedLinks[0], CurAnimStack); bool bValidAnimStack = ValidateAnimStack(SortedLinks, NodeArray, CurAnimStack, ResampleRate, ImportOptions->bImportMorph, AnimTimeSpan); // no animation if (!bValidAnimStack) { continue; } FString SequenceName = Name; if (ValidTakeCount > 1) { SequenceName += "_"; SequenceName += UTF8_TO_TCHAR(CurAnimStack->GetName()); } // See if this sequence already exists. SequenceName = ObjectTools::SanitizeObjectName(SequenceName); FString ParentPath = FString::Printf(TEXT("%s/%s"), *FPackageName::GetLongPackagePath(*Outer->GetName()), *SequenceName); UObject* ParentPackage = CreatePackage(NULL, *ParentPath); UObject* Object = LoadObject<UObject>(ParentPackage, *SequenceName, NULL, LOAD_None, NULL); UAnimSequence * DestSeq = Cast<UAnimSequence>(Object); // if object with same name exists, warn user if (Object && !DestSeq) { AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Error, LOCTEXT("Error_AssetExist", "Asset with same name exists. Can't overwrite another asset")), FFbxErrors::Generic_SameNameAssetExists); continue; // Move on to next sequence... } // If not, create new one now. if(!DestSeq) { DestSeq = NewObject<UAnimSequence>(ParentPackage, *SequenceName, RF_Public | RF_Standalone); // Notify the asset registry FAssetRegistryModule::AssetCreated(DestSeq); } else { DestSeq->RecycleAnimSequence(); } DestSeq->SetSkeleton(Skeleton); // since to know full path, reimport will need to do same UFbxAnimSequenceImportData* ImportData = UFbxAnimSequenceImportData::GetImportDataForAnimSequence(DestSeq, TemplateImportData); ImportData->Update(UFactory::CurrentFilename); ImportAnimation(Skeleton, DestSeq, Name, SortedLinks, NodeArray, CurAnimStack, ResampleRate, AnimTimeSpan); LastCreatedAnim = DestSeq; } return LastCreatedAnim; }
bool UnFbx::FFbxImporter::IsValidAnimationData(TArray<FbxNode*>& SortedLinks, TArray<FbxNode*>& NodeArray, int32& ValidTakeCount) { // If there are no valid links, then we cannot import the anim set if(SortedLinks.Num() == 0) { return false; } ValidTakeCount = 0; int32 AnimStackCount = Scene->GetSrcObjectCount<FbxAnimStack>(); int32 AnimStackIndex; for (AnimStackIndex = 0; AnimStackIndex < AnimStackCount; AnimStackIndex++ ) { FbxAnimStack* CurAnimStack = Scene->GetSrcObject<FbxAnimStack>(AnimStackIndex); // set current anim stack Scene->SetCurrentAnimationStack(CurAnimStack); // debug purpose for (int32 BoneIndex = 0; BoneIndex < SortedLinks.Num(); BoneIndex++) { FString BoneName = UTF8_TO_TCHAR(MakeName(SortedLinks[BoneIndex]->GetName())); UE_LOG(LogFbx, Log, TEXT("SortedLinks :(%d) %s"), BoneIndex, *BoneName ); } FbxTimeSpan AnimTimeSpan = GetAnimationTimeSpan(SortedLinks[0], CurAnimStack); if (AnimTimeSpan.GetDuration() <= 0) { AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Warning, FText::Format(LOCTEXT("FBXImport_ZeroLength", "Animation Stack {0} does not contain any valid key. Try different time options when import."), FText::FromString(UTF8_TO_TCHAR(CurAnimStack->GetName())))), FFbxErrors::Animation_ZeroLength); continue; } ValidTakeCount++; { bool bBlendCurveFound = false; for ( int32 NodeIndex = 0; !bBlendCurveFound && NodeIndex < NodeArray.Num(); NodeIndex++ ) { // consider blendshape animation curve FbxGeometry* Geometry = (FbxGeometry*)NodeArray[NodeIndex]->GetNodeAttribute(); if (Geometry) { int32 BlendShapeDeformerCount = Geometry->GetDeformerCount(FbxDeformer::eBlendShape); for(int32 BlendShapeIndex = 0; BlendShapeIndex<BlendShapeDeformerCount; ++BlendShapeIndex) { FbxBlendShape* BlendShape = (FbxBlendShape*)Geometry->GetDeformer(BlendShapeIndex, FbxDeformer::eBlendShape); int32 BlendShapeChannelCount = BlendShape->GetBlendShapeChannelCount(); for(int32 ChannelIndex = 0; ChannelIndex<BlendShapeChannelCount; ++ChannelIndex) { FbxBlendShapeChannel* Channel = BlendShape->GetBlendShapeChannel(ChannelIndex); if(Channel) { // Get the percentage of influence of the shape. FbxAnimCurve* Curve = Geometry->GetShapeChannel(BlendShapeIndex, ChannelIndex, (FbxAnimLayer*)CurAnimStack->GetMember(0)); if (Curve && Curve->KeyGetCount() > 0) { bBlendCurveFound = true; break; } } } } } } } } return ( ValidTakeCount != 0 ); }
void fbxLoader2::processMesh(FbxNode* node) { FbxMesh* mesh = node->GetMesh(); this->readAnimationWeigths(mesh); if(mesh!=NULL && mesh->IsTriangleMesh()) { for (int i = 0; i<mesh->GetControlPointsCount(); i++) { readVertex(mesh, i, &vertex[i]); vertexArray[i].position = D3DXVECTOR3(vertex[i]); } int a = mesh->GetPolygonVertexCount(); for (int i = 0; i<mesh->GetPolygonVertexCount(); i++) { readUV(mesh, i, 0, &uv[i]); readNormal(mesh, i, &normal[i]); indices[i].indice = mesh->GetPolygonVertices()[i]; indices[i].normal1 = normal[i]; indices[i].uv1 = uv[i]; indicesMeshCount++; } } //vertexLists.push_back(vertexArray); indiceLists.push_back(indices); FbxAnimStack* pAnimStack = FbxCast<FbxAnimStack>(scene->GetSrcObject(FBX_TYPE(FbxAnimStack))); int numAnimLayers = pAnimStack->GetMemberCount(FBX_TYPE(FbxAnimLayer)); this->setBindPoseCluster(node); for (int i = 0; i < numAnimLayers; i++) { FbxAnimLayer* pAnimLayer = pAnimStack->GetMember(FBX_TYPE(FbxAnimLayer), i); FbxAnimCurve* animCv = this->findSkeletonRootBone(scene->GetRootNode())->GetChild(0)->LclTranslation.GetCurve(pAnimLayer, FBXSDK_CURVENODE_COMPONENT_X); if (animCv) { FbxTimeSpan animationLength; int p = animCv->KeyGetCount(); animCv->GetTimeInterval(animationLength); for(int j = 0; j<animationStructure->GetFramesNumber();j++) { FbxTime timeKey = animCv->KeyGet(j).mTime; //FbxTime interval = (duration/p)*j + animationLength.GetStart(); //int intervalVal = (duration.GetSecondCount()/p)*j + animationLength.GetStart().GetSecondCount(); const FbxTime pTime = animCv->KeyGet(j).mTime; FbxAMatrix pGlobalPos = GetGlobalPosition(node, pTime, scene->GetPose(j)); ComputeSkinDeformation(pGlobalPos, mesh, timeKey, scene->GetPose(j), j); } } } for(int i = 0; i<node->GetChildCount(); i++) { processMesh(node->GetChild(i)); } }
void FbxToHkxConverter::extractKeyFramesAndAnnotations(hkxScene *scene, FbxNode* fbxChildNode, hkxNode* newChildNode, int animStackIndex) { FbxAMatrix bindPoseMatrix; FbxAnimStack* lAnimStack = NULL; int numAnimLayers = 0; FbxTimeSpan animTimeSpan; if (animStackIndex >= 0) { lAnimStack = m_curFbxScene->GetSrcObject<FbxAnimStack>(animStackIndex); numAnimLayers = lAnimStack->GetMemberCount<FbxAnimLayer>(); animTimeSpan = lAnimStack->GetLocalTimeSpan(); } // Find the time offset (in the "time space" of the FBX file) of the first animation frame FbxTime timePerFrame; timePerFrame.SetTime(0, 0, 0, 1, 0, m_curFbxScene->GetGlobalSettings().GetTimeMode()); const FbxTime startTime = animTimeSpan.GetStart(); const FbxTime endTime = animTimeSpan.GetStop(); const hkReal startTimeSeconds = static_cast<hkReal>(startTime.GetSecondDouble()); const hkReal endTimeSeconds = static_cast<hkReal>(endTime.GetSecondDouble()); int numFrames = 0; bool staticNode = true; if (scene->m_sceneLength == 0) { bindPoseMatrix = fbxChildNode->EvaluateLocalTransform(startTime); } else { hkArray<hkStringOld> annotationStrings; hkArray<hkReal> annotationTimes; HK_ASSERT(0x0, newChildNode->m_keyFrames.getSize() == 0); // Sample each animation frame for (FbxTime time = startTime, priorSampleTime = endTime; time < endTime; priorSampleTime = time, time += timePerFrame, ++numFrames) { FbxAMatrix frameMatrix = fbxChildNode->EvaluateLocalTransform(time); staticNode = staticNode && (frameMatrix == bindPoseMatrix); hkMatrix4 mat; // Extract this frame's transform convertFbxXMatrixToMatrix4(frameMatrix, mat); newChildNode->m_keyFrames.pushBack(mat); // Extract all annotation strings for this frame using the deprecated // pipeline (new annotations are extracted when sampling attributes) if (m_options.m_exportAnnotations && numAnimLayers > 0) { FbxProperty prop = fbxChildNode->GetFirstProperty(); while(prop.IsValid()) { FbxString propName = prop.GetName(); FbxDataType lDataType = prop.GetPropertyDataType(); hkStringOld name(propName.Buffer(), (int) propName.GetLen()); if (name.asUpperCase().beginsWith("HK") && lDataType.GetType() == eFbxEnum) { FbxAnimLayer* lAnimLayer = lAnimStack->GetMember<FbxAnimLayer>(0); FbxAnimCurve* lAnimCurve = prop.GetCurve(lAnimLayer); int currentKeyIndex; const int keyIndex = (int) lAnimCurve->KeyFind(time, ¤tKeyIndex); const int priorKeyIndex = (int) lAnimCurve->KeyFind(priorSampleTime); // Only store annotations on frames where they're explicitly keyframed, or if this is the first keyframe if (priorKeyIndex != keyIndex) { const int currentEnumValueIndex = keyIndex < 0 ? (int) lAnimCurve->Evaluate(priorSampleTime) : (int) lAnimCurve->Evaluate(time); HK_ASSERT(0x0, currentEnumValueIndex < prop.GetEnumCount()); const char* enumValue = prop.GetEnumValue(currentEnumValueIndex); hkxNode::AnnotationData& annotation = newChildNode->m_annotations.expandOne(); annotation.m_time = (hkReal) (time - startTime).GetSecondDouble(); annotation.m_description = (name + hkStringOld(enumValue, hkString::strLen(enumValue))).cString(); } } prop = fbxChildNode->GetNextProperty(prop); } } } } // Replace animation key data for static nodes with just 1 or 2 frames of bind pose data if (staticNode) { // Static nodes in animated scene data are exported with two keys const bool exportTwoFramesForStaticNodes = (numFrames > 1); // replace transform newChildNode->m_keyFrames.setSize(exportTwoFramesForStaticNodes ? 2: 1); newChildNode->m_keyFrames.optimizeCapacity(0, true); // convert the bind pose transform to Havok format convertFbxXMatrixToMatrix4(bindPoseMatrix, newChildNode->m_keyFrames[0]); if (exportTwoFramesForStaticNodes) { newChildNode->m_keyFrames[1] = newChildNode->m_keyFrames[0]; } } // Extract all times of actual keyframes for the current node... this can be used by Vision if ( m_options.m_storeKeyframeSamplePoints && newChildNode->m_keyFrames.getSize() > 2 && numAnimLayers > 0 ) { FbxAnimLayer* lAnimLayer = lAnimStack->GetMember<FbxAnimLayer>(0); extractKeyTimes(fbxChildNode, lAnimLayer, FBXSDK_CURVENODE_TRANSLATION, newChildNode, startTimeSeconds, endTimeSeconds); extractKeyTimes(fbxChildNode, lAnimLayer, FBXSDK_CURVENODE_ROTATION, newChildNode, startTimeSeconds, endTimeSeconds); extractKeyTimes(fbxChildNode, lAnimLayer, FBXSDK_CURVENODE_SCALING, newChildNode, startTimeSeconds, endTimeSeconds); extractKeyTimes(fbxChildNode, lAnimLayer, FBXSDK_CURVENODE_COMPONENT_X, newChildNode, startTimeSeconds, endTimeSeconds); extractKeyTimes(fbxChildNode, lAnimLayer, FBXSDK_CURVENODE_COMPONENT_Y, newChildNode, startTimeSeconds, endTimeSeconds); extractKeyTimes(fbxChildNode, lAnimLayer, FBXSDK_CURVENODE_COMPONENT_Z, newChildNode, startTimeSeconds, endTimeSeconds); if (newChildNode->m_linearKeyFrameHints.getSize() > 1) { hkSort(newChildNode->m_linearKeyFrameHints.begin(), newChildNode->m_linearKeyFrameHints.getSize()); } } }
// This method is templated on the implementation of hctMayaSceneExporter/hctMaxSceneExporter::createScene() bool FbxToHkxConverter::createSceneStack(int animStackIndex) { hkxScene *scene = new hkxScene; scene->m_modeller.set(m_modeller.cString()); scene->m_asset = m_curFbxScene->GetSceneInfo()->Original_FileName.Get(); if (m_rootNode) { // create root node hkxNode* rootNode = new hkxNode; bool rigPass = (animStackIndex == -1); int currentAnimStackIndex = -1; FbxAnimStack* lAnimStack = NULL; if (rigPass && m_numAnimStacks > 0) { currentAnimStackIndex = 0; } else if (animStackIndex >= 0) { currentAnimStackIndex = animStackIndex; } if (currentAnimStackIndex != -1) { lAnimStack = m_curFbxScene->GetSrcObject<FbxAnimStack>(currentAnimStackIndex); m_curFbxScene->GetEvaluator()->SetContext(lAnimStack); } if (rigPass) { rootNode->m_name = "ROOT_NODE"; scene->m_sceneLength = 0.f; scene->m_numFrames = 1; printf("Converting nodes for root...\n"); } else { rootNode->m_name = lAnimStack->GetName(); const FbxTimeSpan animTimeSpan = lAnimStack->GetLocalTimeSpan(); scene->m_sceneLength = static_cast<hkReal>( animTimeSpan.GetDuration().GetSecondDouble() ); scene->m_numFrames = static_cast<hkUint32>( animTimeSpan.GetDuration().GetFrameCount(m_curFbxScene->GetGlobalSettings().GetTimeMode()) ); printf("Converting nodes for [%s]...\n", rootNode->m_name.cString()); } scene->m_rootNode = rootNode; rootNode->removeReference(); // Setup (identity) keyframes(s) for the 'static' root node rootNode->m_keyFrames.setSize( scene->m_numFrames > 1 ? 2 : 1, hkMatrix4::getIdentity() ); addNodesRecursive(scene, m_rootNode, scene->m_rootNode, currentAnimStackIndex); } m_scenes.pushBack(scene); return true; }
//=============================================================================================================================== 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 ExportAnimation(const Value& obj) { double timeLength = obj["Time"].GetDouble(); double frameRate = obj["FrameRate"].GetDouble(); const Value& animCurve = obj["CurveData"]; FbxAnimStack* lAnimStack = FbxAnimStack::Create(pScene, "base stack"); FbxAnimLayer* lAnimLayer = FbxAnimLayer::Create(pScene, "Base Layer"); lAnimStack->AddMember(lAnimLayer); uint32_t i = 0, j = 0; while (true) { if (i >= animCurve.Size()) { break; } const Value& first = animCurve[i]; j = i + 1; while (j < animCurve.Size()) { const Value& last = animCurve[j]; if (strcmp(first["path"].GetString(), last["path"].GetString()) != 0) { break; } j++; } FbxNode* pNode = FindNode(first["path"].GetString()); if (pNode == NULL) { printf("path %s node not found!!!!!!!!!!!!!!!!!!!!\n", first["path"].GetString()); } else { printf("%d path %s node found %p\n", j - i, first["path"].GetString(), pNode); FbxAnimCurveNode* curveNode = pNode->LclRotation.GetCurveNode(lAnimLayer, true); if (curveNode) { unsigned int numChannel = curveNode->GetChannelsCount(); for (uint32_t n = 0; n < numChannel; n++) { FbxString name = curveNode->GetChannelName(n); printf("%d %s\n", n, (const char*)name); } return; } ExportCurve(lAnimLayer, pNode, animCurve, i, j); } i = j; } }
//----------------------------------------------------------------------------------- static void ImportMotions(SceneImport* import, FbxScene* scene, Matrix4x4& matrixStackTop, std::map<int, FbxNode*>& map, float framerate) { int animationCount = scene->GetSrcObjectCount<FbxAnimStack>(); if (animationCount == 0) { return; } if (import->skeletons.size() == 0) { return; } //Timing information for animation in this scene. How fast is the framerate in this file? FbxGlobalSettings& settings = scene->GetGlobalSettings(); FbxTime::EMode timeMode = settings.GetTimeMode(); double sceneFramerate; if (timeMode == FbxTime::eCustom) { sceneFramerate = settings.GetCustomFrameRate(); } else { sceneFramerate = FbxTime::GetFrameRate(timeMode); } //Only supporting one skeleton for now, update when needed. uint32_t skeletonCount = import->skeletons.size(); Skeleton* skeleton= import->skeletons.at(0); ASSERT_OR_DIE(skeletonCount == 1, "Had multiple skeletons, we only support 1!"); //Time between frames FbxTime advance; advance.SetSecondDouble((double)(1.0f / framerate)); for (int animIndex = 0; animIndex < animationCount; ++animIndex) { //Import Motions FbxAnimStack* anim = scene->GetSrcObject<FbxAnimStack>(); if (nullptr == anim) { continue; } FbxTime startTime = anim->LocalStart; FbxTime endTime = anim->LocalStop; FbxTime duration = endTime - startTime; scene->SetCurrentAnimationStack(anim); const char* motionName = anim->GetName(); float timeSpan = duration.GetSecondDouble(); AnimationMotion* motion = new AnimationMotion(motionName, timeSpan, framerate, skeleton); int jointCount = skeleton->GetJointCount(); for (int jointIndex = 0; jointIndex < jointCount; ++jointIndex) { FbxNode* node = map[jointIndex]; //Extracting world position //local, you would need to grab parent as well Matrix4x4* boneKeyframes = motion->GetJointKeyframes(jointIndex); FbxTime evalTime = FbxTime(0); for (uint32_t frameIndex = 0; frameIndex < motion->m_frameCount; ++frameIndex) { Matrix4x4* boneKeyframe = boneKeyframes + frameIndex; Matrix4x4 boneTransform = GetNodeWorldTransformAtTime(node, evalTime, matrixStackTop); double seconds = evalTime.GetSecondDouble(); seconds = seconds; *boneKeyframe = boneTransform; evalTime += advance; } } import->motions.push_back(motion); } }