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; }