// This method is templated on the implementation of hctMayaSceneExporter/hctMaxSceneExporter::createScene() bool FbxToHkxConverter::createScenes(FbxScene* fbxScene) { clear(); m_curFbxScene = fbxScene; m_rootNode = m_curFbxScene->GetRootNode(); m_modeller = "FBX"; hkStringBuf application = fbxScene->GetSceneInfo()->Original_ApplicationName.Get(); if (application.getLength() > 0) { m_modeller += " ["; m_modeller += application; m_modeller += "]"; } printf("Modeller: %s\n", m_modeller.cString()); if (m_options.m_selectedOnly) { printf("Exporting Selected Only\n"); } if (m_options.m_visibleOnly) { printf("Exporting Visible Only\n"); } hkArray<FbxNode*> boneNodes; findChildren(m_rootNode, boneNodes, FbxNodeAttribute::eSkeleton); m_numBones = boneNodes.getSize(); printf("Bones: %d\n", m_numBones); const int poseCount = m_curFbxScene->GetPoseCount(); if (poseCount > 0) { m_pose = m_curFbxScene->GetPose(0); printf("Pose Elements: %d\n", m_pose->GetCount()); } m_numAnimStacks = m_curFbxScene->GetSrcObjectCount<FbxAnimStack>(); if (m_numAnimStacks > 0) { const FbxAnimStack* lAnimStack = m_curFbxScene->GetSrcObject<FbxAnimStack>(0); const FbxTimeSpan animTimeSpan = lAnimStack->GetLocalTimeSpan(); FbxTime timePerFrame; timePerFrame.SetTime(0, 0, 0, 1, 0, m_curFbxScene->GetGlobalSettings().GetTimeMode()); m_startTime = animTimeSpan.GetStart(); } printf("Animation stacks: %d\n", m_numAnimStacks); createSceneStack(-1); for (int animStackIndex = 0; animStackIndex < m_numAnimStacks && m_numBones > 0; animStackIndex++) { createSceneStack(animStackIndex); } return true; }
void FbxLoader::ComputeNodeMatrix(FbxNode* node, Node& meshNode, bool local) { if(!node) return; FbxAnimEvaluator* evaluator = scene->GetAnimationEvaluator(); FbxAMatrix global; global.SetIdentity(); FbxTime time; time.SetSecondDouble(0.0); if(node != scene->GetRootNode()) { if(local) { global = evaluator->GetNodeLocalTransform(node, time); } else { global = evaluator->GetNodeGlobalTransform(node, time); } } auto T = global.GetT() * factor; if(axismode == eLeftHanded) { auto R = global.GetR(); R[1] *= -1; R[2] *= -1; T[0] *= -1; global.SetR(R); } global.SetT(T); meshNode.matrix = Matrix( (float)global[0][0], (float)global[0][1], (float)global[0][2], (float)global[0][3], (float)global[1][0], (float)global[1][1], (float)global[1][2], (float)global[1][3], (float)global[2][0], (float)global[2][1], (float)global[2][2], (float)global[2][3], (float)global[3][0], (float)global[3][1], (float)global[3][2], (float)global[3][3]); }
void Tools::DisplayAnimation::DisplayListCurveKeys( FbxAnimCurve* i_curve, FbxProperty* i_property ) { FbxTime keyTime; int keyValue; char timeString[256]; FbxString listValue; FbxString string; int count; int keyCount = i_curve->KeyGetCount(); for(count = 0; count < keyCount; count++) { keyValue = static_cast<int>( i_curve->KeyGetValue(count) ); keyTime = i_curve->KeyGetTime( count ); string = " Key Time: "; string += keyTime.GetTimeString( timeString, FbxUShort(256) ); string += ".... Key Value: "; string += keyValue; string += " ("; string += i_property->GetEnumValue( keyValue ); string += ")"; DisplayCommon::DisplayString( string ); string += "\n"; FBXSDK_printf ( string ); } }
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; } }
FbxAMatrix& FbxLoader::GetRootMatrixGlobalAtTime(double time) { FbxTime ftime; ftime.SetSecondDouble(time); ftime = m_animEvaluator->ValidateTime(ftime); return m_animEvaluator->GetNodeGlobalTransform(m_firstNode, ftime); }
void decompose(FbxNode* fbxNode, float time, Vector3* scale, Quaternion* rotation, Vector3* translation) { FbxAMatrix fbxMatrix; Matrix matrix; FbxTime kTime; kTime.SetMilliSeconds((FbxLongLong)time); fbxMatrix = fbxNode->EvaluateLocalTransform(kTime); copyMatrix(fbxMatrix, matrix); matrix.decompose(scale, rotation, translation); }
FbxAMatrix& FbxLoader::GetRootMatrixLocalAtTime(double time) { FbxTime ftime; ftime.SetSecondDouble(time); ftime = m_animEvaluator->ValidateTime(ftime); int total = ftime.GetSecondDouble() * 1000; int real = (int)(time * 1000) % total; ftime.SetSecondDouble(real / 1000.0); return m_animEvaluator->GetNodeLocalTransform(m_firstNode, ftime); }
bool keepTestHorizon( FbxAnimCurve* curve, FbxAnimCurveKey prevkey, FbxAnimCurveKey currkey, FbxAnimCurveKey nextkey) { FbxTime prevt = prevkey.GetTime(); FbxTime currt = currkey.GetTime(); FbxTime nextt = nextkey.GetTime(); bool needit = false; float prevEv; FbxLongLong tmpll = (currt - prevt).GetMilliSeconds() / 3; FbxTime termt; float tmpfs[6] = { -1, }; FbxTime times[6]; for (int termi = 0; termi < 3; termi++) { termt.SetMilliSeconds(tmpll*termi); times[termi] = prevt + termt; } tmpll = (nextt - currt).GetMilliSeconds() / 3; for (int termi = 0; termi < 3; termi++) { termt.SetMilliSeconds(tmpll*termi); times[3 + termi] = currt + termt; } output2 << setw(20 + output2.rdbuf()->in_avail() - 4096) << "timee : "; for (int i = 0; i < 6; i++) { output2 << setw(10) << setiosflags(ios::fixed) << setprecision(3) << (times[i]).GetSecondDouble() << "\t"; tmpfs[i] = curve->Evaluate(times[i]); } output2 << endl << setw(20) << "value : "; for (int i = 0; i < 6; i++) { output2 << setw(10) << setiosflags(ios::fixed) << setprecision(3) << tmpfs[i] << "\t"; } for (int i = 1; i<6;i++) if (abs(tmpfs[i] - tmpfs[i-1]) > comp_level_horizon) { output2 << "keepinHorizon at : " << i << endl; return true; } output2 << endl; return false; }
std::shared_ptr<KeyFrameAnimation> FBXConverter::LoadCurve(FbxAnimCurve* curve, int32_t frameCount) { auto keyFrameAnimation = std::make_shared<KeyFrameAnimation>(); for (int32_t i = 0; i < frameCount; i++) { FbxTime time; time.SetFrame(i, FbxTime::eFrames60); auto value = curve->Evaluate(time); keyFrameAnimation->Values.push_back(value); } return keyFrameAnimation; }
void UnFbx::FFbxImporter::MergeAllLayerAnimation(FbxAnimStack* AnimStack, int32 ResampleRate) { FbxTime lFramePeriod; lFramePeriod.SetSecondDouble(1.0 / ResampleRate); FbxTimeSpan lTimeSpan = AnimStack->GetLocalTimeSpan(); AnimStack->BakeLayers(Scene->GetAnimationEvaluator(), lTimeSpan.GetStart(), lTimeSpan.GetStop(), lFramePeriod); // always apply unroll filter FbxAnimCurveFilterUnroll UnrollFilter; FbxAnimLayer* lLayer = AnimStack->GetMember<FbxAnimLayer>(0); UnrollFilter.Reset(); ApplyUnroll(Scene->GetRootNode(), lLayer, &UnrollFilter); }
void Tools::DisplayAnimation::DisplayCurveKeys( FbxAnimCurve* i_curve ) { static const char *interpolation[] = { "?", "constant", "linear", "cubic"}; static const char *constantMode[] = { "?", "Standard", "Next" }; static const char *cubicMode[] = { "?", "Auto", "Auto break", "Tcb", "User", "Break", "User break" }; static const char *tangentWVMode[] = { "?", "None", "Right", "Next left" }; FbxTime keyTime; float keyValue; char timeString[256]; FbxString string; int count; int keyCount = i_curve->KeyGetCount(); for( count = 0; count < keyCount; count++ ) { keyValue = static_cast<float>( i_curve->KeyGetValue(count) ); keyTime = i_curve->KeyGetTime( count ); string = " Key Time: "; string += keyTime.GetTimeString( timeString, FbxUShort(256) ); string += ".... Key Value: "; string += keyValue; string += " [ "; string += interpolation[InterpolationFlagToIndex(i_curve->KeyGetInterpolation(count))]; if( (i_curve->KeyGetInterpolation(count)&FbxAnimCurveDef::eInterpolationConstant) == FbxAnimCurveDef::eInterpolationConstant ) { string += " | "; string += constantMode[ ConstantmodeFlagToIndex(i_curve->KeyGetConstantMode(count)) ]; } else if( (i_curve->KeyGetInterpolation(count)&FbxAnimCurveDef::eInterpolationCubic) == FbxAnimCurveDef::eInterpolationCubic ) { string += " | "; string += cubicMode[ TangentmodeFlagToIndex(i_curve->KeyGetTangentMode(count)) ]; string += " | "; string += tangentWVMode[ TangentweightFlagToIndex(i_curve->KeyGet(count).GetTangentWeightMode()) ]; string += " | "; string += tangentWVMode[ TangentVelocityFlagToIndex(i_curve->KeyGet(count).GetTangentVelocityMode()) ]; } string += " ]"; DisplayCommon::DisplayString( string ); string += "\n"; FBXSDK_printf( string ); } }
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); } }
void ofxFBXScene::parseRotationCurve(ofxFBXNode & node, FbxAnimLayer * pAnimLayer, FbxNode* fbxNode, FbxPropertyT<FbxDouble3> &rotation){ node.originalRotation = ofQuaternion(rotation.Get().mData[0], ofVec3f(1, 0, 0), rotation.Get().mData[1], ofVec3f(0, 1, 0), rotation.Get().mData[2], ofVec3f(0, 0, 1)); node.getNode().setOrientation(node.originalRotation); ofLogVerbose("ofxFBXScene") << "original rotation " << endl << node.originalRotation << endl; if(!rotation.GetCurve(pAnimLayer)) return; FbxAnimCurve* lAnimCurveX = rotation.GetCurve(pAnimLayer,"X"); FbxAnimCurve* lAnimCurveY = rotation.GetCurve(pAnimLayer,"Y"); FbxAnimCurve* lAnimCurveZ = rotation.GetCurve(pAnimLayer,"Z"); int xKeyCount = lAnimCurveX ? lAnimCurveX->KeyGetCount() : 0; int yKeyCount = lAnimCurveY ? lAnimCurveY->KeyGetCount() : 0; int zKeyCount = lAnimCurveZ ? lAnimCurveZ->KeyGetCount() : 0; FbxTime lKeyTime; int lCount; FbxTime lXKeyTime,lYKeyTime,lZKeyTime; for(lCount = 0; lCount < max(max(xKeyCount,yKeyCount),zKeyCount); lCount++) { if(lCount<xKeyCount){ lXKeyTime = lAnimCurveX->KeyGetTime(lCount); } if(lCount<yKeyCount){ lYKeyTime = lAnimCurveY->KeyGetTime(lCount); } if(lCount<zKeyCount){ lZKeyTime = lAnimCurveZ->KeyGetTime(lCount); } lKeyTime = min(min(lXKeyTime,lYKeyTime),lZKeyTime); lKeyTime = lXKeyTime; FbxAMatrix & matrix = fbxNode->EvaluateLocalTransform(lKeyTime); ofxFBXKey<ofQuaternion> key; ofVec3f t,s; ofQuaternion so; ofMatrix4x4 m = toOf(matrix); m.decompose(t,key.value,s,so); key.timeMillis = lKeyTime.GetMilliSeconds(); node.rotationKeys.push_back(key); } }
void FbxTime_CreateLongLong_HasSeconds() { // given: FbxManager* manager = FbxManager::Create(); FbxTime* time; // when: time = new FbxTime(0); // then: AssertEqual(0.0, time->GetSecondDouble()); AssertEqual(0LL, time->GetFrameCount()); // when: time = new FbxTime(-7697693000LL); // then: AssertEqual(-5/30.0, time->GetSecondDouble()); AssertEqual(-5LL, time->GetFrameCount()); }
void WccToFbxExporter::addPositionKeyToNode(FbxNode *pNode, FbxAnimLayer *pAnimLayer, int timeMs, const QVector3D &position) { FbxAnimCurve* lCurve = nullptr; FbxTime lTime; int lKeyIndex = 0; lCurve = pNode->LclTranslation.GetCurve(pAnimLayer, FBXSDK_CURVENODE_COMPONENT_X, true); if (lCurve) { lCurve->KeyModifyBegin(); lTime.SetMilliSeconds(timeMs); lKeyIndex = lCurve->KeyAdd(lTime); lCurve->KeySet(lKeyIndex, lTime, position.x(), FbxAnimCurveDef::eInterpolationCubic); lCurve->KeyModifyEnd(); } lCurve = pNode->LclTranslation.GetCurve(pAnimLayer, FBXSDK_CURVENODE_COMPONENT_Y, true); if (lCurve) { lCurve->KeyModifyBegin(); lTime.SetMilliSeconds(timeMs); lKeyIndex = lCurve->KeyAdd(lTime); lCurve->KeySet(lKeyIndex, lTime, position.y(), FbxAnimCurveDef::eInterpolationCubic); lCurve->KeyModifyEnd(); } lCurve = pNode->LclTranslation.GetCurve(pAnimLayer, FBXSDK_CURVENODE_COMPONENT_Z, true); if (lCurve) { lCurve->KeyModifyBegin(); lTime.SetMilliSeconds(timeMs); lKeyIndex = lCurve->KeyAdd(lTime); lCurve->KeySet(lKeyIndex, lTime, position.z(), FbxAnimCurveDef::eInterpolationCubic); lCurve->KeyModifyEnd(); } }
void ofxFBXScene::parsePositionCurve(ofxFBXNode & node, FbxAnimLayer * pAnimLayer, FbxPropertyT<FbxDouble3> &position){ node.originalPosition = toOf(position.Get()); node.getNode().setPosition(node.originalPosition); ofLogVerbose("ofxFBXScene") << "original position " << node.originalPosition << endl; if(!position.GetCurve(pAnimLayer)) return; FbxAnimCurve* lAnimCurveX = position.GetCurve(pAnimLayer,"X"); FbxAnimCurve* lAnimCurveY = position.GetCurve(pAnimLayer,"Y"); FbxAnimCurve* lAnimCurveZ = position.GetCurve(pAnimLayer,"Z"); FbxTime lKeyTime; int lCount; int xKeyCount = lAnimCurveX? lAnimCurveX->KeyGetCount() : 0; int yKeyCount = lAnimCurveY? lAnimCurveY->KeyGetCount() : 0; int zKeyCount = lAnimCurveZ? lAnimCurveZ->KeyGetCount() : 0; ofxFBXKey<float> key; for(lCount = 0; lCount < xKeyCount; lCount++) { key.value = lAnimCurveX->KeyGetValue(lCount); lKeyTime = lAnimCurveX->KeyGetTime(lCount); key.timeMillis = lKeyTime.GetMilliSeconds(); node.xKeys.push_back(key); } for(lCount = 0; lCount < yKeyCount; lCount++) { key.value = lAnimCurveY->KeyGetValue(lCount); lKeyTime = lAnimCurveY->KeyGetTime(lCount); key.timeMillis = lKeyTime.GetMilliSeconds(); node.yKeys.push_back(key); } for(lCount = 0; lCount < zKeyCount; lCount++) { key.value = lAnimCurveZ->KeyGetValue(lCount); lKeyTime = lAnimCurveZ->KeyGetTime(lCount); key.timeMillis = lKeyTime.GetMilliSeconds(); node.zKeys.push_back(key); } }
std::shared_ptr<AnimationClip> FBXConverter::LoadAnimation(FbxAnimStack* fbxAnimStack, FbxNode* fbxRootNode) { auto animClip = std::make_shared<AnimationClip>(); const int32_t layerCount = fbxAnimStack->GetMemberCount<FbxAnimLayer>(); if (layerCount == 0) return std::shared_ptr<AnimationClip>(); auto animationName = fbxAnimStack->GetName(); FbxTime startTime = fbxAnimStack->LocalStart; FbxTime endTime = fbxAnimStack->LocalStop; int hour, minute, second, frame, field, residual; startTime.GetTime(hour, minute, second, frame, field, residual, FbxTime::eFrames60); auto startFrame = 60 * (hour * 60 * 60 + minute * 60 + second) + frame; endTime.GetTime(hour, minute, second, frame, field, residual, FbxTime::eFrames60); auto endFrame = 60 * (hour * 60 * 60 + minute * 60 + second) + frame; animClip->Name = animationName; animClip->StartFrame = startFrame; animClip->EndFrame = endFrame; for (auto i = 0; i < layerCount; ++i) { auto layer = fbxAnimStack->GetMember<FbxAnimLayer>(); auto kfas = LoadCurve(layer, fbxRootNode, animClip->EndFrame); for(auto a : kfas) { animClip->Animations.push_back(a); } } return animClip; }
BabylonMesh::BabylonMesh(BabylonNode* node) : BabylonAbstractMesh(node), _isEnabled(true), _isVisible(true), _billboardMode(0), _visibility(1), _skeletonId(-1), _pickable(true), _hasVertexAlpha(false), _checkCollision(false), _receiveShadows(false), _infiniteDistance(false), _autoAnimate(false), _autoAnimateFrom(0), _autoAnimateTo(0), _autoAnimateLoop(false), _showBoundingBox(false), _showSubMeshesBoundingBox(false), _applyFog(false), _alphaIndex(0) { pivotMatrix.SetIdentity(); auto fbxNode = node->fbxNode(); std::string ansiName = fbxNode->GetName(); name(std::wstring(ansiName.begin(), ansiName.end())); id(getNodeId(fbxNode)); auto parent = fbxNode->GetParent(); if (parent) { parentId(getNodeId(parent)); } pivotMatrix = ConvertToBabylonCoordinateSystem( GetGeometryTransformation(fbxNode)); auto animStack = fbxNode->GetScene()->GetSrcObject<FbxAnimStack>(0); FbxString animStackName = animStack->GetName(); FbxTakeInfo* takeInfo = fbxNode->GetScene()->GetTakeInfo(animStackName); auto animTimeMode = GlobalSettings::Current().AnimationsTimeMode; auto animFrameRate = GlobalSettings::Current().AnimationsFrameRate(); auto startFrame = takeInfo->mLocalTimeSpan.GetStart().GetFrameCount(animTimeMode); auto endFrame = takeInfo->mLocalTimeSpan.GetStop().GetFrameCount(animTimeMode); auto animLengthInFrame = endFrame - startFrame + 1; _visibility = static_cast<float>(node->fbxNode()->Visibility.Get()); auto posAnim = std::make_shared<BabylonAnimation<babylon_vector3>>(BabylonAnimationBase::loopBehavior_Cycle, static_cast<int>(animFrameRate), L"position", L"position", true, 0, static_cast<int>(animLengthInFrame), true); auto rotAnim = std::make_shared<BabylonAnimation<babylon_vector4>>(BabylonAnimationBase::loopBehavior_Cycle, static_cast<int>(animFrameRate), L"rotationQuaternion", L"rotationQuaternion", true, 0, static_cast<int>(animLengthInFrame), true); auto scaleAnim = std::make_shared<BabylonAnimation<babylon_vector3>>(BabylonAnimationBase::loopBehavior_Cycle, static_cast<int>(animFrameRate), L"scaling", L"scaling", true, 0, static_cast<int>(animLengthInFrame), true); auto visibilityAnim = std::make_shared<BabylonAnimation<float>>(BabylonAnimationBase::loopBehavior_Cycle, static_cast<int>(animFrameRate), L"visibility", L"visibility", true, 0, static_cast<int>(animLengthInFrame), true); auto mesh = fbxNode->GetMesh(); _isVisible = fbxNode->Show.Get(); auto rotCurveNode = fbxNode->LclRotation.GetCurveNode(); auto translateCurveNode = fbxNode->LclTranslation.GetCurveNode(); auto scalingCurveNode = fbxNode->LclScaling.GetCurveNode(); auto visibilityCurveNode = fbxNode->Visibility.GetCurveNode(); if (rotCurveNode || translateCurveNode || scalingCurveNode) { for (auto ix = 0; ix < animLengthInFrame; ix++) { FbxTime currTime; currTime.SetFrame(startFrame + ix, animTimeMode); babylon_animation_key<babylon_vector3> poskey; babylon_animation_key<babylon_vector4> rotkey; babylon_animation_key<babylon_vector3> scalekey; poskey.frame = ix; rotkey.frame = ix; scalekey.frame = ix; auto currTransform = node->GetLocal(currTime); poskey.values = currTransform.translation(); rotkey.values = currTransform.rotationQuaternion(); scalekey.values = currTransform.scaling(); posAnim->appendKey(poskey); rotAnim->appendKey(rotkey); scaleAnim->appendKey(scalekey); } } if (visibilityCurveNode) { for (auto ix = 0; ix < animLengthInFrame; ix++) { FbxTime currTime; currTime.SetFrame(startFrame + ix, animTimeMode); babylon_animation_key<float> visibilityKey; visibilityKey.frame = ix; visibilityKey.values = static_cast<float>(node->fbxNode()->Visibility.EvaluateValue(currTime)); visibilityAnim->appendKey(visibilityKey); } } if (!posAnim->isConstant()){ animations.push_back(posAnim); } if (!rotAnim->isConstant()){ animations.push_back(rotAnim); } if (!scaleAnim->isConstant()){ animations.push_back(scaleAnim); } if (!visibilityAnim->isConstant()) { animations.push_back(visibilityAnim); } if (!mesh) { return; } if (mesh->GetPolygonCount() == 0){ return; } _receiveShadows = mesh->ReceiveShadow.Get(); FbxGeometryConverter conv(mesh->GetFbxManager()); conv.ComputePolygonSmoothingFromEdgeSmoothing(mesh); if (!mesh->IsTriangleMesh()) { mesh = (FbxMesh*) conv.Triangulate(mesh, true); } mesh->RemoveBadPolygons(); mesh->GenerateNormals(); FbxStringList uvSetNameList; mesh->GetUVSetNames(uvSetNameList); std::vector<std::string> uniqueUVSets; int uvCount = uvSetNameList.GetCount(); for (int i = 0; i < uvCount; ++i) { std::string value = uvSetNameList.GetStringAt(i); if (std::find(uniqueUVSets.begin(), uniqueUVSets.end(), value) == uniqueUVSets.end()) { uniqueUVSets.push_back(value); } } uvsets = uniqueUVSets; bool hasUv = uniqueUVSets.size() > 0; bool hasUv2 = uniqueUVSets.size() > 1; bool hasUv3 = uniqueUVSets.size() > 2; bool hasUv4 = uniqueUVSets.size() > 3; bool hasUv5 = uniqueUVSets.size() > 4; bool hasUv6 = uniqueUVSets.size() > 5; std::string uvSetName; std::string uv2SetName; std::string uv3SetName; std::string uv4SetName; std::string uv5SetName; std::string uv6SetName; if (hasUv) { uvSetName = uniqueUVSets[0]; } if (hasUv2) { uv2SetName = uniqueUVSets[1]; } if (hasUv3) { uv3SetName = uniqueUVSets[2]; } if (hasUv4) { uv4SetName = uniqueUVSets[3]; } if (hasUv5) { uv5SetName = uniqueUVSets[4]; } if (hasUv6) { uv6SetName = uniqueUVSets[5]; } auto colors = mesh->GetElementVertexColor(); FbxLayerElement::EMappingMode colorMappingMode; FbxLayerElement::EReferenceMode colorReferenceMode; if (colors) { colorMappingMode = colors->GetMappingMode(); colorReferenceMode = colors->GetReferenceMode(); } auto normals = mesh->GetElementNormal(); FbxGeometryElementUV* uvs = nullptr; FbxGeometryElementUV* uvs2 = nullptr; FbxGeometryElementUV* uvs3 = nullptr; FbxGeometryElementUV* uvs4 = nullptr; FbxGeometryElementUV* uvs5 = nullptr; FbxGeometryElementUV* uvs6 = nullptr; FbxLayerElement::EMappingMode uvsMappingMode; FbxLayerElement::EReferenceMode uvsReferenceMode; FbxLayerElement::EMappingMode uvs2MappingMode; FbxLayerElement::EReferenceMode uvs2ReferenceMode; FbxLayerElement::EMappingMode uvs3MappingMode; FbxLayerElement::EReferenceMode uvs3ReferenceMode; FbxLayerElement::EMappingMode uvs4MappingMode; FbxLayerElement::EReferenceMode uvs4ReferenceMode; FbxLayerElement::EMappingMode uvs5MappingMode; FbxLayerElement::EReferenceMode uvs5ReferenceMode; FbxLayerElement::EMappingMode uvs6MappingMode; FbxLayerElement::EReferenceMode uvs6ReferenceMode; if (hasUv) { uvs = mesh->GetElementUV(uvSetName.c_str()); uvsMappingMode = uvs->GetMappingMode(); uvsReferenceMode = uvs->GetReferenceMode(); } if (hasUv2) { uvs2 = mesh->GetElementUV(uv2SetName.c_str()); uvs2MappingMode = uvs2->GetMappingMode(); uvs2ReferenceMode = uvs2->GetReferenceMode(); } if (hasUv3) { uvs3 = mesh->GetElementUV(uv3SetName.c_str()); uvs3MappingMode = uvs3->GetMappingMode(); uvs3ReferenceMode = uvs3->GetReferenceMode(); } if (hasUv4) { uvs4 = mesh->GetElementUV(uv4SetName.c_str()); uvs4MappingMode = uvs4->GetMappingMode(); uvs4ReferenceMode = uvs4->GetReferenceMode(); } if (hasUv5) { uvs5 = mesh->GetElementUV(uv5SetName.c_str()); uvs5MappingMode = uvs5->GetMappingMode(); uvs5ReferenceMode = uvs5->GetReferenceMode(); } if (hasUv6) { uvs6 = mesh->GetElementUV(uv6SetName.c_str()); uvs6MappingMode = uvs6->GetMappingMode(); uvs6ReferenceMode = uvs6->GetReferenceMode(); } auto normalMappingMode = normals->GetMappingMode(); auto normalReferenceMode = normals->GetReferenceMode(); std::vector<SubmeshData> submeshes; auto materialCount = node->fbxNode()->GetMaterialCount(); if (materialCount == 0) { materialCount = 1; } submeshes.resize(materialCount); auto baseLayer = mesh->GetLayer(0); auto materials = baseLayer->GetMaterials(); FbxLayerElement::EMappingMode materialMappingMode = materials ? materials->GetMappingMode() : FbxLayerElement::eByPolygon; // extract deformers SkinInfo skinInfo(fbxNode); if (skinInfo.hasSkin()){ associatedSkeleton = std::make_shared<BabylonSkeleton>(); skinInfo.buildBabylonSkeleton(*associatedSkeleton); } auto triangleCount = mesh->GetPolygonCount(); for (int triangleIndex = 0; triangleIndex < triangleCount; ++triangleIndex) { int materialIndex = 0; if (materialCount > 0 && materials) { switch (materialMappingMode) { case FbxLayerElement::eAllSame: materialIndex = materials->GetIndexArray().GetAt(0); break; case FbxLayerElement::eByPolygon: materialIndex = materials->GetIndexArray().GetAt(triangleIndex); } } auto& submesh = submeshes[materialIndex]; triangle t; for (int cornerIndex = 0; cornerIndex < 3; ++cornerIndex) { auto controlPointIndex = mesh->GetPolygonVertex(triangleIndex, cornerIndex); auto vertexIndex = triangleIndex * 3 + cornerIndex; auto position = mesh->GetControlPoints()[controlPointIndex]; position[2] = -position[2]; BabylonVertex v; v.position = position; if (normals) { int normalMapIndex = (normalMappingMode == FbxLayerElement::eByControlPoint) ? controlPointIndex : vertexIndex; int normalValueIndex = (normalReferenceMode == FbxLayerElement::eDirect) ? normalMapIndex : normals->GetIndexArray().GetAt(normalMapIndex); v.normal = normals->GetDirectArray().GetAt(normalValueIndex); v.normal.z = -v.normal.z; } if (colors) { int mappingIndex = (colorMappingMode == FbxLayerElement::eByControlPoint) ? controlPointIndex : vertexIndex; int valueIndex = (colorReferenceMode == FbxLayerElement::eDirect) ? mappingIndex : colors->GetIndexArray().GetAt(mappingIndex); v.color = colors->GetDirectArray().GetAt(valueIndex); } if (uvs) { int mappingIndex = (uvsMappingMode == FbxLayerElement::eByControlPoint) ? controlPointIndex : vertexIndex; int valueIndex = (uvsReferenceMode == FbxLayerElement::eDirect) ? mappingIndex : uvs->GetIndexArray().GetAt(mappingIndex); v.uv = uvs->GetDirectArray().GetAt(valueIndex); //v.uv.y = 1 - v.uv.y; } if (uvs2) { int mappingIndex = (uvs2MappingMode == FbxLayerElement::eByControlPoint) ? controlPointIndex : vertexIndex; int valueIndex = (uvs2ReferenceMode == FbxLayerElement::eDirect) ? mappingIndex : uvs2->GetIndexArray().GetAt(mappingIndex); v.uv2 = uvs2->GetDirectArray().GetAt(valueIndex); } if (uvs3) { int mappingIndex = (uvs3MappingMode == FbxLayerElement::eByControlPoint) ? controlPointIndex : vertexIndex; int valueIndex = (uvs3ReferenceMode == FbxLayerElement::eDirect) ? mappingIndex : uvs3->GetIndexArray().GetAt(mappingIndex); v.uv3 = uvs3->GetDirectArray().GetAt(valueIndex); } if (uvs4) { int mappingIndex = (uvs4MappingMode == FbxLayerElement::eByControlPoint) ? controlPointIndex : vertexIndex; int valueIndex = (uvs4ReferenceMode == FbxLayerElement::eDirect) ? mappingIndex : uvs4->GetIndexArray().GetAt(mappingIndex); v.uv4 = uvs4->GetDirectArray().GetAt(valueIndex); } if (uvs5) { int mappingIndex = (uvs5MappingMode == FbxLayerElement::eByControlPoint) ? controlPointIndex : vertexIndex; int valueIndex = (uvs5ReferenceMode == FbxLayerElement::eDirect) ? mappingIndex : uvs5->GetIndexArray().GetAt(mappingIndex); v.uv5 = uvs5->GetDirectArray().GetAt(valueIndex); } if (uvs6) { int mappingIndex = (uvs6MappingMode == FbxLayerElement::eByControlPoint) ? controlPointIndex : vertexIndex; int valueIndex = (uvs6ReferenceMode == FbxLayerElement::eDirect) ? mappingIndex : uvs6->GetIndexArray().GetAt(mappingIndex); v.uv6 = uvs6->GetDirectArray().GetAt(valueIndex); } if (skinInfo.hasSkin()){ auto& skinData = skinInfo.controlPointBoneIndicesAndWeights(controlPointIndex); for (auto boneix = 0; boneix < skinData.size()&&boneix<4; ++boneix){ v.boneIndices[boneix] = skinData[boneix].index; v.boneWeights[boneix] = static_cast<float>(skinData[boneix].weight); } for (auto boneix = skinData.size(); boneix < 4; ++boneix){ v.boneIndices[boneix] = skinInfo.bonesCount(); v.boneWeights[boneix] = 0; } } auto foundVertex = submesh.knownVertices.find(v); if (foundVertex != submesh.knownVertices.end()) { //submesh.indices.push_back(foundVertex->second); t.indices[cornerIndex] = foundVertex->second; } else { auto index = static_cast<int>(submesh.vertices.size()); submesh.vertices.push_back(v); //submesh.indices.push_back(index); submesh.knownVertices[v] = index; t.indices[cornerIndex] = index; } } if (submesh.knownTriangles.insert(t).second) { submesh.indices.push_back(t.indices[0]); submesh.indices.push_back(t.indices[1]); submesh.indices.push_back(t.indices[2]); } else { std::cout << "duplicate triangle found (and eliminated) in " << fbxNode->GetName() << std::endl; } } std::uint32_t vertexOffset = 0; for (auto matIndex = 0u; matIndex < submeshes.size(); ++matIndex) { auto& submesh = submeshes[matIndex]; BabylonSubmesh babsubmesh; babsubmesh.indexCount = static_cast<int>(submesh.indices.size()); babsubmesh.indexStart = static_cast<int>(_indices.size()); babsubmesh.materialIndex = matIndex; babsubmesh.verticesCount = static_cast<int>(submesh.vertices.size()); babsubmesh.verticesStart = static_cast<int>(_positions.size()); for (auto& v : submesh.vertices) { _positions.push_back(v.position); if (normals) { _normals.push_back(v.normal); } if (colors) { _colors.push_back(v.color); } if (uvs) { _uvs.push_back(v.uv); } if (uvs2) { _uvs2.push_back(v.uv2); } if (uvs3) { _uvs3.push_back(v.uv3); } if (uvs4) { _uvs4.push_back(v.uv4); } if (uvs5) { _uvs5.push_back(v.uv5); } if (uvs6) { _uvs6.push_back(v.uv6); } if (skinInfo.hasSkin()){ float weight0 = v.boneWeights[0]; float weight1 = v.boneWeights[1]; float weight2 = v.boneWeights[2]; int bone0 = v.boneIndices[0]; int bone1 = v.boneIndices[1]; int bone2 = v.boneIndices[2]; int bone3 = v.boneIndices[3]; _boneWeights.push_back(babylon_vector4( weight0, weight1, weight2, 1.0f - weight0 - weight1 - weight2)); _boneIndices.push_back((bone3 << 24) | (bone2 << 16) | (bone1 << 8) | bone0); } } for (auto i : submesh.indices) { _indices.push_back(i + vertexOffset); } vertexOffset = static_cast<int>(_positions.size()); _submeshes.push_back(babsubmesh); } }
bool frameCompare( FbxTime i , FbxTime j ) { return ( i.GetFrameCount() < j.GetFrameCount() ); }
//FbxAMatrix ComputeTotalMatrix(FbxNode* node, FbxTime time = FBXSDK_TIME_INFINITE){ // //} SkinInfo::SkinInfo(FbxNode* meshNode) : _node(meshNode), _mesh(meshNode->GetMesh()), _skin(nullptr) { int deformerCount = _mesh->GetDeformerCount(); for (auto ix = 0; ix < deformerCount; ++ix){ auto skin = reinterpret_cast<FbxSkin*>(_mesh->GetDeformer(ix, FbxDeformer::eSkin)); if (skin){ _skin = skin; break; } } if (!_skin){ return; } std::vector<FbxPose*> bindPoses; auto poseCount = _node->GetScene()->GetPoseCount(); for (auto ix = 0; ix < poseCount; ++ix){ auto pose = _node->GetScene()->GetPose(ix); if (pose->IsBindPose()){ bindPoses.push_back(pose); } } std::vector<FbxNode*> unsortedFlatListOfNodes; std::vector<FbxCluster*> unsortedFlatListOfClusters; auto clusterCount = _skin->GetClusterCount(); for (auto ix = 0; ix < clusterCount; ++ix){ auto cluster = _skin->GetCluster(ix); if (!cluster) { std::cout << "Invalid skin" << std::endl; _skin = nullptr; return; } auto linkNode = cluster->GetLink(); if (!linkNode){ std::cout << "Invalid skin" << std::endl; _skin = nullptr; return; } unsortedFlatListOfClusters.push_back(cluster); unsortedFlatListOfNodes.push_back(linkNode); } ComputeBoneHierarchy(unsortedFlatListOfNodes, unsortedFlatListOfClusters, _bones, _fbxClusterIndexToBoneIndex, _controlPointToBoneIndicesAndWeights); auto deformType = _skin->GetDeformerType(); auto geometryTransform = GetGeometryTransformation(meshNode); // compute all bones global inverse and global matrix for (auto& bone : _bones){ FbxAMatrix transformMatrix; FbxAMatrix transformLinkMatrix; FbxMatrix globalBindposeInverseMatrix; bone.cluster->GetTransformMatrix(transformMatrix); // The transformation of the mesh at binding time bone.cluster->GetTransformLinkMatrix(transformLinkMatrix); // The transformation of the cluster(joint) at binding time from joint space to world space /*for (auto pose : bindPoses){ auto inPoseIndex = pose->Find(bone.linkNode); if (inPoseIndex >= 0){ auto tempMat = pose->GetMatrix(inPoseIndex); transformLinkMatrix = *(FbxAMatrix*) (double*) &tempMat; break; } }*/ globalBindposeInverseMatrix = FbxMatrix(transformLinkMatrix.Inverse()) * FbxMatrix(transformMatrix) * geometryTransform; bone.matrixGlobalBindPose = ConvertToBabylonCoordinateSystem(globalBindposeInverseMatrix.Inverse()); if (bone.parentBoneIndex == -1){ bone.matrixLocalBindPose = bone.matrixGlobalBindPose; } else{ bone.matrixLocalBindPose = _bones[bone.parentBoneIndex].matrixGlobalBindPose.Inverse()* bone.matrixGlobalBindPose; } } // compute anim auto animStack = _node->GetScene()->GetCurrentAnimationStack(); FbxString animStackName = animStack->GetName(); //FbxTakeInfo* takeInfo = node->GetScene()->GetTakeInfo(animStackName); auto animTimeMode = GlobalSettings::Current().AnimationsTimeMode; auto animFrameRate = GlobalSettings::Current().AnimationsFrameRate(); auto startFrame = animStack->GetLocalTimeSpan().GetStart().GetFrameCount(animTimeMode); auto endFrame = animStack->GetLocalTimeSpan().GetStop().GetFrameCount(animTimeMode); auto animLengthInFrame = endFrame - startFrame + 1; for (auto ix = 0; ix < animLengthInFrame; ix++){ FbxTime currTime; currTime.SetFrame(startFrame + ix, animTimeMode); auto currTransformOffset = FbxMatrix(meshNode->EvaluateGlobalTransform(currTime)) * geometryTransform; auto currTransformOffsetInverse = currTransformOffset.Inverse(); // compute global transform and local for (auto& bone : _bones){ BoneAnimKeyFrame kf; kf.frame = ix; kf.matrixGlobal = ConvertToBabylonCoordinateSystem(currTransformOffsetInverse*bone.linkNode->EvaluateGlobalTransform(currTime)); if (bone.parentBoneIndex == -1){ kf.matrixLocal = kf.matrixGlobal; } else{ auto& parentBone = _bones[bone.parentBoneIndex]; kf.matrixLocal = //bone.matrixLocalBindPose; parentBone.keyFrames[parentBone.keyFrames.size() - 1].matrixGlobal.Inverse()* kf.matrixGlobal; } bone.keyFrames.push_back(kf); } } }
void FBXScene::ProcessAnimation(FbxNode* pNode, const char* strTakeName, float fFrameRate, float fStart, float fStop) { FbxNodeAttribute* pNodeAttribute = pNode->GetNodeAttribute(); if (pNodeAttribute) { if (pNodeAttribute->GetAttributeType() == FbxNodeAttribute::EType::eSkeleton) { if( m_pSkeleton ) { SkeletonBone* pBone = m_pSkeleton->FindBone(pNode->GetName()); if( pBone ) { AnimationKeyFrames* pAnimationKeyFrames = new AnimationKeyFrames(strTakeName); double fTime = 0; while( fTime <= fStop ) { FbxTime takeTime; takeTime.SetSecondDouble(fTime); FbxMatrix matAbsoluteTransform2 = GetAbsoluteTransformFromCurrentTake2(pNode, takeTime); FbxMatrix matParentAbsoluteTransform2 = GetAbsoluteTransformFromCurrentTake2(pNode->GetParent(), takeTime); FbxMatrix matInvParentAbsoluteTransform2 = matParentAbsoluteTransform2.Inverse(); FbxMatrix matTransform2 = matInvParentAbsoluteTransform2 * matAbsoluteTransform2; pAnimationKeyFrames->AddKeyFrame(matTransform2); fTime += 1.0f / fFrameRate; } pBone->AddAnimationKeyFrames(pAnimationKeyFrames); } } } else if (pNodeAttribute->GetAttributeType() == FbxNodeAttribute::EType::eMesh) { Model* pModel = m_Models.Find(pNode->GetName(), NULL); if( pModel ) { AnimationKeyFrames* pAnimationKeyFrames = new AnimationKeyFrames(strTakeName); double fTime = 0; while( fTime <= fStop ) { FbxTime takeTime; takeTime.SetSecondDouble(fTime); FbxMatrix matAbsoluteTransform2 = GetAbsoluteTransformFromCurrentTake2(pNode, takeTime); pAnimationKeyFrames->AddKeyFrame(matAbsoluteTransform2); fTime += 1.0f/fFrameRate; } pModel->AddAnimationKeyFrames(pAnimationKeyFrames); } } } for( int i = 0; i < pNode->GetChildCount(); ++i ) { ProcessAnimation(pNode->GetChild(i), strTakeName, fFrameRate, fStart, fStop); } }
bool UnFbx::FFbxImporter::ImportAnimation(USkeleton* Skeleton, UAnimSequence * DestSeq, const FString& FileName, TArray<FbxNode*>& SortedLinks, TArray<FbxNode*>& NodeArray, FbxAnimStack* CurAnimStack, const int32 ResampleRate, const FbxTimeSpan AnimTimeSpan) { // @todo : the length might need to change w.r.t. sampling keys FbxTime SequenceLength = AnimTimeSpan.GetDuration(); float PreviousSequenceLength = DestSeq->SequenceLength; // if you have one pose(thus 0.f duration), it still contains animation, so we'll need to consider that as MINIMUM_ANIMATION_LENGTH time length DestSeq->SequenceLength = FGenericPlatformMath::Max<float>(SequenceLength.GetSecondDouble(), MINIMUM_ANIMATION_LENGTH); if(PreviousSequenceLength > MINIMUM_ANIMATION_LENGTH && DestSeq->RawCurveData.FloatCurves.Num() > 0) { // The sequence already existed when we began the import. We need to scale the key times for all curves to match the new // duration before importing over them. This is to catch any user-added curves float ScaleFactor = DestSeq->SequenceLength / PreviousSequenceLength; for(FFloatCurve& Curve : DestSeq->RawCurveData.FloatCurves) { Curve.FloatCurve.ScaleCurve(0.0f, ScaleFactor); } } if (ImportOptions->bDeleteExistingMorphTargetCurves) { for (int32 CurveIdx=0; CurveIdx<DestSeq->RawCurveData.FloatCurves.Num(); ++CurveIdx) { auto& Curve = DestSeq->RawCurveData.FloatCurves[CurveIdx]; if (Curve.GetCurveTypeFlag(ACF_DrivesMorphTarget)) { DestSeq->RawCurveData.FloatCurves.RemoveAt(CurveIdx, 1, false); --CurveIdx; } } DestSeq->RawCurveData.FloatCurves.Shrink(); } // // import blend shape curves // { GWarn->BeginSlowTask( LOCTEXT("BeginImportMorphTargetCurves", "Importing Morph Target Curves"), true); for ( int32 NodeIndex = 0; 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); const int32 BlendShapeChannelCount = BlendShape->GetBlendShapeChannelCount(); FString BlendShapeName = UTF8_TO_TCHAR(MakeName(BlendShape->GetName())); for(int32 ChannelIndex = 0; ChannelIndex<BlendShapeChannelCount; ++ChannelIndex) { FbxBlendShapeChannel* Channel = BlendShape->GetBlendShapeChannel(ChannelIndex); if(Channel) { FString ChannelName = UTF8_TO_TCHAR(MakeName(Channel->GetName())); // Maya adds the name of the blendshape and an underscore to the front of the channel name, so remove it if(ChannelName.StartsWith(BlendShapeName)) { ChannelName = ChannelName.Right(ChannelName.Len() - (BlendShapeName.Len()+1)); } FbxAnimCurve* Curve = Geometry->GetShapeChannel(BlendShapeIndex, ChannelIndex, (FbxAnimLayer*)CurAnimStack->GetMember(0)); if (Curve && Curve->KeyGetCount() > 0) { FFormatNamedArguments Args; Args.Add(TEXT("BlendShape"), FText::FromString(ChannelName)); const FText StatusUpate = FText::Format(LOCTEXT("ImportingMorphTargetCurvesDetail", "Importing Morph Target Curves [{BlendShape}]"), Args); GWarn->StatusUpdate(NodeIndex + 1, NodeArray.Num(), StatusUpate); // now see if we have one already exists. If so, just overwrite that. if not, add new one. ImportCurveToAnimSequence(DestSeq, *ChannelName, Curve, ACF_DrivesMorphTarget | ACF_TriggerEvent, AnimTimeSpan, 0.01f /** for some reason blend shape values are coming as 100 scaled **/); } } } } } } GWarn->EndSlowTask(); } // // importing custom attribute START // if (ImportOptions->bImportCustomAttribute) { GWarn->BeginSlowTask( LOCTEXT("BeginImportMorphTargetCurves", "Importing Custom Attirubte Curves"), true); const int32 TotalLinks = SortedLinks.Num(); int32 CurLinkIndex=0; for(auto Node: SortedLinks) { FbxProperty Property = Node->GetFirstProperty(); while (Property.IsValid()) { FbxAnimCurveNode* CurveNode = Property.GetCurveNode(); // do this if user defined and animated and leaf node if( CurveNode && Property.GetFlag(FbxPropertyAttr::eUserDefined) && CurveNode->IsAnimated() && IsSupportedCurveDataType(Property.GetPropertyDataType().GetType()) ) { FString CurveName = UTF8_TO_TCHAR(CurveNode->GetName()); UE_LOG(LogFbx, Log, TEXT("CurveName : %s"), *CurveName ); int32 TotalCount = CurveNode->GetChannelsCount(); for (int32 ChannelIndex=0; ChannelIndex<TotalCount; ++ChannelIndex) { FbxAnimCurve * AnimCurve = CurveNode->GetCurve(ChannelIndex); FString ChannelName = CurveNode->GetChannelName(ChannelIndex).Buffer(); if (AnimCurve) { FString FinalCurveName; if (TotalCount == 1) { FinalCurveName = CurveName; } else { FinalCurveName = CurveName + "_" + ChannelName; } FFormatNamedArguments Args; Args.Add(TEXT("CurveName"), FText::FromString(FinalCurveName)); const FText StatusUpate = FText::Format(LOCTEXT("ImportingCustomAttributeCurvesDetail", "Importing Custom Attribute [{CurveName}]"), Args); GWarn->StatusUpdate(CurLinkIndex + 1, TotalLinks, StatusUpate); ImportCurveToAnimSequence(DestSeq, FinalCurveName, AnimCurve, ACF_DefaultCurve, AnimTimeSpan); } } } Property = Node->GetNextProperty(Property); } CurLinkIndex++; } GWarn->EndSlowTask(); } // importing custom attribute END const bool bSourceDataExists = (DestSeq->SourceRawAnimationData.Num() > 0); TArray<AnimationTransformDebug::FAnimationTransformDebugData> TransformDebugData; int32 TotalNumKeys = 0; const FReferenceSkeleton& RefSkeleton = Skeleton->GetReferenceSkeleton(); // import animation { GWarn->BeginSlowTask( LOCTEXT("BeginImportAnimation", "Importing Animation"), true); TArray<struct FRawAnimSequenceTrack>& RawAnimationData = bSourceDataExists? DestSeq->SourceRawAnimationData : DestSeq->RawAnimationData; DestSeq->TrackToSkeletonMapTable.Empty(); DestSeq->AnimationTrackNames.Empty(); RawAnimationData.Empty(); TArray<FName> FbxRawBoneNames; FillAndVerifyBoneNames(Skeleton, SortedLinks, FbxRawBoneNames, FileName); UnFbx::FFbxImporter* FbxImporter = UnFbx::FFbxImporter::GetInstance(); const bool bPreserveLocalTransform = FbxImporter->GetImportOptions()->bPreserveLocalTransform; // Build additional transform matrix UFbxAnimSequenceImportData* TemplateData = Cast<UFbxAnimSequenceImportData>(DestSeq->AssetImportData); FbxAMatrix FbxAddedMatrix; BuildFbxMatrixForImportTransform(FbxAddedMatrix, TemplateData); FMatrix AddedMatrix = Converter.ConvertMatrix(FbxAddedMatrix); const int32 NumSamplingKeys = FMath::FloorToInt(AnimTimeSpan.GetDuration().GetSecondDouble() * ResampleRate); const FbxTime TimeIncrement = (NumSamplingKeys > 1)? AnimTimeSpan.GetDuration() / (NumSamplingKeys - 1) : AnimTimeSpan.GetDuration(); for(int32 SourceTrackIdx = 0; SourceTrackIdx < FbxRawBoneNames.Num(); ++SourceTrackIdx) { int32 NumKeysForTrack = 0; // see if it's found in Skeleton FName BoneName = FbxRawBoneNames[SourceTrackIdx]; int32 BoneTreeIndex = RefSkeleton.FindBoneIndex(BoneName); // update status FFormatNamedArguments Args; Args.Add(TEXT("TrackName"), FText::FromName(BoneName)); Args.Add(TEXT("TotalKey"), FText::AsNumber(NumSamplingKeys)); Args.Add(TEXT("TrackIndex"), FText::AsNumber(SourceTrackIdx+1)); Args.Add(TEXT("TotalTracks"), FText::AsNumber(FbxRawBoneNames.Num())); const FText StatusUpate = FText::Format(LOCTEXT("ImportingAnimTrackDetail", "Importing Animation Track [{TrackName}] ({TrackIndex}/{TotalTracks}) - TotalKey {TotalKey}"), Args); GWarn->StatusForceUpdate(SourceTrackIdx + 1, FbxRawBoneNames.Num(), StatusUpate); if (BoneTreeIndex!=INDEX_NONE) { bool bSuccess = true; FRawAnimSequenceTrack RawTrack; RawTrack.PosKeys.Empty(); RawTrack.RotKeys.Empty(); RawTrack.ScaleKeys.Empty(); AnimationTransformDebug::FAnimationTransformDebugData NewDebugData; FbxNode* Link = SortedLinks[SourceTrackIdx]; FbxNode * LinkParent = Link->GetParent(); for(FbxTime CurTime = AnimTimeSpan.GetStart(); CurTime <= AnimTimeSpan.GetStop(); CurTime += TimeIncrement) { // save global trasnform FbxAMatrix GlobalMatrix = Link->EvaluateGlobalTransform(CurTime); // we'd like to verify this before going to Transform. // currently transform has tons of NaN check, so it will crash there FMatrix GlobalUEMatrix = Converter.ConvertMatrix(GlobalMatrix); if (GlobalUEMatrix.ContainsNaN()) { bSuccess = false; AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Error, FText::Format(LOCTEXT("Error_InvalidTransform", "Track {0} contains invalid transform. Could not import the track."), FText::FromName(BoneName))), FFbxErrors::Animation_TransformError); break; } FTransform GlobalTransform = Converter.ConvertTransform(GlobalMatrix); if (GlobalTransform.ContainsNaN()) { bSuccess = false; AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Error, FText::Format(LOCTEXT("Error_InvalidUnrealTransform", "Track {0} did not yeild valid transform. Please report this to animation team."), FText::FromName(BoneName))), FFbxErrors::Animation_TransformError); break; } // debug data, including import transformation FTransform AddedTransform(AddedMatrix); NewDebugData.SourceGlobalTransform.Add(GlobalTransform * AddedTransform); FTransform LocalTransform; if( !bPreserveLocalTransform && LinkParent) { // I can't rely on LocalMatrix. I need to recalculate quaternion/scale based on global transform if Parent exists FbxAMatrix ParentGlobalMatrix = Link->GetParent()->EvaluateGlobalTransform(CurTime); FTransform ParentGlobalTransform = Converter.ConvertTransform(ParentGlobalMatrix); LocalTransform = GlobalTransform.GetRelativeTransform(ParentGlobalTransform); NewDebugData.SourceParentGlobalTransform.Add(ParentGlobalTransform); } else { FbxAMatrix& LocalMatrix = Link->EvaluateLocalTransform(CurTime); FbxVector4 NewLocalT = LocalMatrix.GetT(); FbxVector4 NewLocalS = LocalMatrix.GetS(); FbxQuaternion NewLocalQ = LocalMatrix.GetQ(); LocalTransform.SetTranslation(Converter.ConvertPos(NewLocalT)); LocalTransform.SetScale3D(Converter.ConvertScale(NewLocalS)); LocalTransform.SetRotation(Converter.ConvertRotToQuat(NewLocalQ)); NewDebugData.SourceParentGlobalTransform.Add(FTransform::Identity); } if(TemplateData && BoneTreeIndex == 0) { // If we found template data earlier, apply the import transform matrix to // the root track. LocalTransform.SetFromMatrix(LocalTransform.ToMatrixWithScale() * AddedMatrix); } if (LocalTransform.ContainsNaN()) { bSuccess = false; AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Error, FText::Format(LOCTEXT("Error_InvalidUnrealLocalTransform", "Track {0} did not yeild valid local transform. Please report this to animation team."), FText::FromName(BoneName))), FFbxErrors::Animation_TransformError); break; } RawTrack.ScaleKeys.Add(LocalTransform.GetScale3D()); RawTrack.PosKeys.Add(LocalTransform.GetTranslation()); RawTrack.RotKeys.Add(LocalTransform.GetRotation()); NewDebugData.RecalculatedLocalTransform.Add(LocalTransform); ++NumKeysForTrack; } if (bSuccess) { //add new track int32 NewTrackIdx = RawAnimationData.Add(RawTrack); DestSeq->AnimationTrackNames.Add(BoneName); NewDebugData.SetTrackData(NewTrackIdx, BoneTreeIndex, BoneName); // add mapping to skeleton bone track DestSeq->TrackToSkeletonMapTable.Add(FTrackToSkeletonMap(BoneTreeIndex)); TransformDebugData.Add(NewDebugData); } } TotalNumKeys = FMath::Max( TotalNumKeys, NumKeysForTrack ); } DestSeq->NumFrames = TotalNumKeys; GWarn->EndSlowTask(); } // compress animation { GWarn->BeginSlowTask( LOCTEXT("BeginCompressAnimation", "Compress Animation"), true); GWarn->StatusForceUpdate(1, 1, LOCTEXT("CompressAnimation", "Compressing Animation")); // if source data exists, you should bake it to Raw to apply if(bSourceDataExists) { DestSeq->BakeTrackCurvesToRawAnimation(); } else { // otherwise just compress DestSeq->PostProcessSequence(); } // run debug mode AnimationTransformDebug::OutputAnimationTransformDebugData(TransformDebugData, TotalNumKeys, RefSkeleton); GWarn->EndSlowTask(); } return true; }
bool UnFbx::FFbxImporter::ImportCurve(const FbxAnimCurve* FbxCurve, FFloatCurve * Curve, const FbxTimeSpan &AnimTimeSpan, const float ValueScale/*=1.f*/) const { static float DefaultCurveWeight = FbxAnimCurveDef::sDEFAULT_WEIGHT; if ( FbxCurve && Curve ) { for ( int32 KeyIndex=0; KeyIndex<FbxCurve->KeyGetCount(); ++KeyIndex ) { FbxAnimCurveKey Key = FbxCurve->KeyGet(KeyIndex); FbxTime KeyTime = Key.GetTime() - AnimTimeSpan.GetStart(); float Value = Key.GetValue() * ValueScale; FKeyHandle NewKeyHandle = Curve->FloatCurve.AddKey(KeyTime.GetSecondDouble(), Value, true); FbxAnimCurveDef::ETangentMode KeyTangentMode = Key.GetTangentMode(); FbxAnimCurveDef::EInterpolationType KeyInterpMode = Key.GetInterpolation(); FbxAnimCurveDef::EWeightedMode KeyTangentWeightMode = Key.GetTangentWeightMode(); ERichCurveInterpMode NewInterpMode = RCIM_Linear; ERichCurveTangentMode NewTangentMode = RCTM_Auto; ERichCurveTangentWeightMode NewTangentWeightMode = RCTWM_WeightedNone; float LeaveTangent = 0.f; float ArriveTangent = 0.f; float LeaveTangentWeight = 0.f; float ArriveTangentWeight = 0.f; switch (KeyInterpMode) { case FbxAnimCurveDef::eInterpolationConstant://! Constant value until next key. NewInterpMode = RCIM_Constant; break; case FbxAnimCurveDef::eInterpolationLinear://! Linear progression to next key. NewInterpMode = RCIM_Linear; break; case FbxAnimCurveDef::eInterpolationCubic://! Cubic progression to next key. NewInterpMode = RCIM_Cubic; // get tangents { LeaveTangent = Key.GetDataFloat(FbxAnimCurveDef::eRightSlope); if ( KeyIndex > 0 ) { FbxAnimCurveKey PrevKey = FbxCurve->KeyGet(KeyIndex-1); ArriveTangent = PrevKey.GetDataFloat(FbxAnimCurveDef::eNextLeftSlope); } else { ArriveTangent = 0.f; } } break; } // when we import tangent, we only support break or user // since it's modified by DCC and we only assume these two are valid // auto does our own stuff, which doesn't work with what you see in DCC if (KeyTangentMode & FbxAnimCurveDef::eTangentBreak) { NewTangentMode = RCTM_Break; } else { NewTangentMode = RCTM_User; } // @fix me : weight of tangent is not used, but we'll just save this for future where we might use it. switch (KeyTangentWeightMode) { case FbxAnimCurveDef::eWeightedNone://! Tangent has default weights of 0.333; we define this state as not weighted. LeaveTangentWeight = ArriveTangentWeight = DefaultCurveWeight; NewTangentWeightMode = RCTWM_WeightedNone; break; case FbxAnimCurveDef::eWeightedRight: //! Right tangent is weighted. NewTangentWeightMode = RCTWM_WeightedLeave; LeaveTangentWeight = Key.GetDataFloat(FbxAnimCurveDef::eRightWeight); ArriveTangentWeight = DefaultCurveWeight; break; case FbxAnimCurveDef::eWeightedNextLeft://! Left tangent is weighted. NewTangentWeightMode = RCTWM_WeightedArrive; LeaveTangentWeight = DefaultCurveWeight; if ( KeyIndex > 0 ) { FbxAnimCurveKey PrevKey = FbxCurve->KeyGet(KeyIndex-1); ArriveTangentWeight = PrevKey.GetDataFloat(FbxAnimCurveDef::eNextLeftWeight); } else { ArriveTangentWeight = 0.f; } break; case FbxAnimCurveDef::eWeightedAll://! Both left and right tangents are weighted. NewTangentWeightMode = RCTWM_WeightedBoth; LeaveTangentWeight = Key.GetDataFloat(FbxAnimCurveDef::eRightWeight); if ( KeyIndex > 0 ) { FbxAnimCurveKey PrevKey = FbxCurve->KeyGet(KeyIndex-1); ArriveTangentWeight = PrevKey.GetDataFloat(FbxAnimCurveDef::eNextLeftWeight); } else { ArriveTangentWeight = 0.f; } break; } Curve->FloatCurve.SetKeyInterpMode(NewKeyHandle, NewInterpMode); Curve->FloatCurve.SetKeyTangentMode(NewKeyHandle, NewTangentMode); Curve->FloatCurve.SetKeyTangentWeightMode(NewKeyHandle, NewTangentWeightMode); FRichCurveKey& NewKey = Curve->FloatCurve.GetKey(NewKeyHandle); // apply 1/100 - that seems like the tangent unit difference with FBX NewKey.ArriveTangent = ArriveTangent * 0.01f; NewKey.LeaveTangent = LeaveTangent * 0.01f; NewKey.ArriveTangentWeight = ArriveTangentWeight; NewKey.LeaveTangentWeight = LeaveTangentWeight; } return true; } return false; }
FbxTimeSpan UnFbx::FFbxImporter::GetAnimationTimeSpan(FbxNode* RootNode, FbxAnimStack* AnimStack) { FBXImportOptions* ImportOption = GetImportOptions(); FbxTimeSpan AnimTimeSpan(FBXSDK_TIME_INFINITE, FBXSDK_TIME_MINUS_INFINITE); if (ImportOption) { if (ImportOption->AnimationLengthImportType == FBXALIT_AnimatedKey) { RootNode->GetAnimationInterval(AnimTimeSpan, AnimStack); } else if (ImportOption->AnimationLengthImportType == FBXALIT_ExportedTime) { AnimTimeSpan = AnimStack->GetLocalTimeSpan(); } else // then it's range { AnimTimeSpan = AnimStack->GetLocalTimeSpan(); FbxTimeSpan AnimatedInterval(FBXSDK_TIME_INFINITE, FBXSDK_TIME_MINUS_INFINITE); RootNode->GetAnimationInterval(AnimatedInterval, AnimStack); // find the most range that covers by both method, that'll be used for clamping FbxTime StartTime = FMath::Min<FbxTime>(AnimTimeSpan.GetStart(), AnimatedInterval.GetStart()); FbxTime StopTime = FMath::Max<FbxTime>(AnimTimeSpan.GetStop(),AnimatedInterval.GetStop()); // make inclusive time between localtimespan and animation interval AnimTimeSpan.SetStart(StartTime); AnimTimeSpan.SetStop(StopTime); // hopefully this is going to be whole frames // our game has DEFAULT_SAMPLERATE frames per second. FbxTime EachFrame = FBXSDK_TIME_ONE_SECOND/DEFAULT_SAMPLERATE; int32 StartFrame = StartTime.Get()/EachFrame.Get(); int32 StopFrame = StopTime.Get()/EachFrame.Get(); if (StartFrame != StopFrame) { FbxTime Duration = AnimTimeSpan.GetDuration(); ImportOption->AnimationRange.X = FMath::Clamp<int32>(ImportOption->AnimationRange.X, StartFrame, StopFrame); ImportOption->AnimationRange.Y = FMath::Clamp<int32>(ImportOption->AnimationRange.Y, StartFrame, StopFrame); FbxLongLong Interval = EachFrame.Get(); // now set new time if (StartFrame != ImportOption->AnimationRange.X) { FbxTime NewTime(ImportOption->AnimationRange.X*Interval); AnimTimeSpan.SetStart(NewTime); } if (StopFrame != ImportOption->AnimationRange.Y) { FbxTime NewTime(ImportOption->AnimationRange.Y*Interval); AnimTimeSpan.SetStop(NewTime); } } } } return AnimTimeSpan; }
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 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()); } } }
void FBXScene::ProcessAnimations(FbxScene* pScene) { m_pAnimationController = new AnimationController(); FbxNode* pRootNode = pScene->GetRootNode(); if(!pRootNode) return; float fFrameRate = (float)FbxTime::GetFrameRate(pScene->GetGlobalSettings().GetTimeMode()); FbxArray<FbxString*> takeArray; FbxDocument* pDocument = FbxCast<FbxDocument>(pScene); // dynamic_cast<FbxDocument*>(pScene); if( pDocument ) pDocument->FillAnimStackNameArray(takeArray); for( int i = 0; i < takeArray.GetCount(); ++i ) { FbxString* takeName = takeArray.GetAt(i); if( std::string(takeName->Buffer()) != "Default" ) { /// ARRRGHHH SÄTTER ALLTID FÖRSTA HÄR!!!!!!!!!!!!!!!!!! FbxTakeInfo* lCurrentTakeInfo = pScene->GetTakeInfo(takeName->Buffer()); FbxAnimStack* lAnimStack = FbxCast<FbxAnimStack>(pScene->GetSrcObject<FbxAnimStack>(i)); pScene->GetEvaluator()->SetContext(lAnimStack); FbxTime KStart; FbxTime KStop; if(lCurrentTakeInfo) { KStart = lCurrentTakeInfo->mLocalTimeSpan.GetStart(); KStop = lCurrentTakeInfo->mLocalTimeSpan.GetStop(); } else { // Take the time line value FbxTimeSpan lTimeLineTimeSpan; pScene->GetGlobalSettings().GetTimelineDefaultTimeSpan(lTimeLineTimeSpan); KStart = lTimeLineTimeSpan.GetStart(); KStop = lTimeLineTimeSpan.GetStop(); } float fStart = (float)KStart.GetSecondDouble(); float fStop = (float)KStop.GetSecondDouble(); if( fStart < fStop ) { int nKeyFrames = int((fStop-fStart)*fFrameRate); Animation* pAnimation = new Animation(takeName->Buffer(), nKeyFrames, fFrameRate); m_pAnimationController->AddAnimation(pAnimation); ProcessAnimation(pRootNode, takeName->Buffer(), fFrameRate, fStart, fStop); } } delete takeName; } takeArray.Clear(); }
bool slopkeepTest(FbxAnimCurve* curve, FbxAnimCurveKey prevkey, FbxAnimCurveKey currkey, FbxAnimCurveKey nextkey) { FbxTime prevt = prevkey.GetTime(); FbxTime currt = currkey.GetTime(); FbxTime nextt = nextkey.GetTime(); float slope1 = (currkey.GetValue() - prevkey.GetValue()) / (currt.GetSecondDouble() - prevt.GetSecondDouble()); float slope2 = (nextkey.GetValue() - currkey.GetValue()) / (nextt.GetSecondDouble() - currt.GetSecondDouble()); output2 << setw(20) << "slope : " << setw(10) << setprecision(3) << slope1 << "," << setw(10) << slope2; if (abs(slope1 - slope2) > 0.005) { output2 << " keepinSlope" << endl; return true; } output2 << endl; float deriv1, deriv2, deriv3, deriv4; deriv1 = prevkey.GetDataFloat(FbxAnimCurveDef::EDataIndex::eRightSlope); deriv2 = prevkey.GetDataFloat(FbxAnimCurveDef::EDataIndex::eNextLeftSlope); deriv3 = currkey.GetDataFloat(FbxAnimCurveDef::EDataIndex::eRightSlope); deriv4 = currkey.GetDataFloat(FbxAnimCurveDef::EDataIndex::eNextLeftSlope); FbxTime mili; mili.SetMilliSeconds(1); output3 << "deriv1 : " << deriv1 << ", val : " << prevkey.GetValue() << ", +1mili value : " << curve->EvaluateRightDerivative(prevt + mili) << endl; float derivl11, derivl12, derivl21, derivl22; float derivr11, derivr12, derivr21, derivr22; FbxTime term1, term2; term1.SetSecondDouble((currt.GetSecondDouble() - prevt.GetSecondDouble()) / 3); term2.SetSecondDouble((nextt.GetSecondDouble() - currt.GetSecondDouble()) / 3); derivl11 = curve->EvaluateLeftDerivative(prevt + term1); derivl12 = curve->EvaluateLeftDerivative(prevt + term1 + term1); derivl21 = curve->EvaluateLeftDerivative(currt + term2); derivl22 = curve->EvaluateLeftDerivative(currt + term2 + term2); derivr11 = curve->EvaluateRightDerivative(prevt + term1); derivr12 = curve->EvaluateRightDerivative(prevt + term1 + term1); derivr21 = curve->EvaluateRightDerivative(currt + term2); derivr22 = curve->EvaluateRightDerivative(currt + term2 + term2); output2 << setw(20) << "deriv : " << setprecision(3) << deriv1 <<"," << deriv2 <<"," << deriv3 <<"," << deriv4 <<"," << derivl11<<"," << derivl12<<"," << derivl21<<"," << derivl22<<"," << derivr11<<"," << derivr12<<"," << derivr21<<"," << derivr22<<"," ; if ( prevkey.GetInterpolation() == FbxAnimCurveDef::eInterpolationConstant && currkey.GetInterpolation() == FbxAnimCurveDef::eInterpolationConstant && nextkey.GetInterpolation() == FbxAnimCurveDef::eInterpolationConstant ) { if ( abs(prevkey.GetValue() - currkey.GetValue()) < comp_level_horizon && abs(currkey.GetValue() - nextkey.GetValue()) < comp_level_horizon ) return false; } else if ( prevkey.GetInterpolation() == FbxAnimCurveDef::eInterpolationCubic && currkey.GetInterpolation() == FbxAnimCurveDef::eInterpolationCubic && nextkey.GetInterpolation() == FbxAnimCurveDef::eInterpolationCubic ) { if ( abs(deriv1 - deriv2) < comp_level_deriv && abs(deriv2 - deriv3) < comp_level_deriv && abs(deriv3 - deriv4) < comp_level_deriv ) { float nearCurr = (deriv1*(currt.GetMilliSeconds() - prevt.GetMilliSeconds())) + prevkey.GetValue(); float nearNext = (deriv3*(nextt.GetMilliSeconds() - currt.GetMilliSeconds())) + currkey.GetValue(); if ( abs(nearCurr - currkey.GetValue()) < comp_level_integError && abs(nearNext - nextkey.GetValue()) < comp_level_integError ) return false; } } return true; /* if ( abs(deriv1 - deriv2) < 0.005 && abs(deriv2 - deriv3) < 0.005 && abs(deriv3 - deriv4) < 0.005 && abs(deriv4 - derivl11) < 0.005 && abs(derivl11 - derivr11) < 0.005 && abs(derivr11 - derivl12) < 0.005 && abs(derivl12 - derivr12) < 0.005 && abs(derivr12 - derivl21) < 0.005 && abs(derivl21 - derivr21) < 0.005 && abs(derivr21 - derivl22) < 0.005 && abs(derivl22 - derivr22) < 0.005 ) { } else { output2 << "keepinDeriv" << endl; return true; } output2 << "removable key" << endl; */ return false; }
BabylonCamera::BabylonCamera(BabylonNode& babnode) { auto node = babnode.fbxNode(); std::string ansiName = node->GetName(); name = std::wstring(ansiName.begin(), ansiName.end()); id = getNodeId(node); auto parent = node->GetParent(); if (parent) { parentId = getNodeId(parent); } auto camera = node->GetCamera(); if (!camera) { return; } type = L"FreeCamera"; auto targetNode = node->GetTarget(); if (targetNode) { lockedTargetId = getNodeId(targetNode); } else { target = camera->InterestPosition.Get(); } position = babnode.localTranslate(); rotationQuaternion = babnode.localRotationQuat(); fov = camera->FieldOfViewY * Euler2Rad; minZ = camera->FrontPlaneDistance.Get(); maxZ = camera->BackPlaneDistance.Get(); auto hasAnimStack = node->GetScene()->GetSrcObjectCount<FbxAnimStack>() > 0; if (!hasAnimStack){ return; } auto animStack = node->GetScene()->GetSrcObject<FbxAnimStack>(0); FbxString animStackName = animStack->GetName(); FbxTakeInfo* takeInfo = node->GetScene()->GetTakeInfo(animStackName); auto animTimeMode = GlobalSettings::Current().AnimationsTimeMode; auto animFrameRate = GlobalSettings::Current().AnimationsFrameRate(); auto startFrame = takeInfo->mLocalTimeSpan.GetStart().GetFrameCount(animTimeMode); auto endFrame = takeInfo->mLocalTimeSpan.GetStop().GetFrameCount(animTimeMode); auto animLengthInFrame = endFrame - startFrame + 1; auto posAnim = std::make_shared<BabylonAnimation<babylon_vector3>>(BabylonAnimationBase::loopBehavior_Cycle, animFrameRate, L"position", L"position", true, 0, animLengthInFrame, true); auto rotAnim = std::make_shared<BabylonAnimation<babylon_vector4>>(BabylonAnimationBase::loopBehavior_Cycle, animFrameRate, L"rotation", L"rotation", true, 0, animLengthInFrame, true); auto targetAnim = std::make_shared<BabylonAnimation<babylon_vector3>>(BabylonAnimationBase::loopBehavior_Cycle, animFrameRate, L"target", L"target", true, 0, animLengthInFrame, true); for (auto ix = 0ll; ix < animLengthInFrame; ix++){ FbxTime currTime; currTime.SetFrame(startFrame + ix, animTimeMode); babylon_animation_key<babylon_vector3> poskey; babylon_animation_key<babylon_vector4> rotkey; poskey.frame = ix; rotkey.frame = ix; poskey.values = babnode.localTranslate(currTime); rotkey.values = babnode.localRotationQuat(currTime); posAnim->appendKey(poskey); rotAnim->appendKey(rotkey); if (lockedTargetId.size() == 0){ babylon_animation_key<babylon_vector3> targetKey; targetKey.frame = ix; targetKey.values = camera->InterestPosition.EvaluateValue(currTime); targetAnim->appendKey(targetKey); } } if (!posAnim->isConstant()){ animations.push_back(posAnim); } if (!rotAnim->isConstant()){ quatAnimations.push_back(rotAnim); } if (!targetAnim->isConstant()){ animations.push_back(targetAnim); } }
void FBXSceneEncoder::loadAnimationChannels(FbxAnimLayer* animLayer, FbxNode* fbxNode, Animation* animation) { const char* name = fbxNode->GetName(); //Node* node = _gamePlayFile.getNode(name); // Determine which properties are animated on this node // Find the transform at each key frame // TODO: Ignore properties that are not animated (scale, rotation, translation) // This should result in only one animation channel per animated node. float startTime = FLT_MAX, stopTime = -1.0f, frameRate = -FLT_MAX; bool tx = false, ty = false, tz = false, rx = false, ry = false, rz = false, sx = false, sy = false, sz = false; FbxAnimCurve* animCurve = NULL; animCurve = getCurve(fbxNode->LclTranslation, animLayer, FBXSDK_CURVENODE_COMPONENT_X); if (animCurve) { tx = true; findMinMaxTime(animCurve, &startTime, &stopTime, &frameRate); } animCurve = getCurve(fbxNode->LclTranslation, animLayer, FBXSDK_CURVENODE_COMPONENT_Y); if (animCurve) { ty = true; findMinMaxTime(animCurve, &startTime, &stopTime, &frameRate); } animCurve = getCurve(fbxNode->LclTranslation, animLayer, FBXSDK_CURVENODE_COMPONENT_Z); if (animCurve) { tz = true; findMinMaxTime(animCurve, &startTime, &stopTime, &frameRate); } animCurve = getCurve(fbxNode->LclRotation, animLayer, FBXSDK_CURVENODE_COMPONENT_X); if (animCurve) { rx = true; findMinMaxTime(animCurve, &startTime, &stopTime, &frameRate); } animCurve = getCurve(fbxNode->LclRotation, animLayer, FBXSDK_CURVENODE_COMPONENT_Y); if (animCurve) { ry = true; findMinMaxTime(animCurve, &startTime, &stopTime, &frameRate); } animCurve = getCurve(fbxNode->LclRotation, animLayer, FBXSDK_CURVENODE_COMPONENT_Z); if (animCurve) { rz = true; findMinMaxTime(animCurve, &startTime, &stopTime, &frameRate); } animCurve = getCurve(fbxNode->LclScaling, animLayer, FBXSDK_CURVENODE_COMPONENT_X); if (animCurve) { sx = true; findMinMaxTime(animCurve, &startTime, &stopTime, &frameRate); } animCurve = getCurve(fbxNode->LclScaling, animLayer, FBXSDK_CURVENODE_COMPONENT_Y); if (animCurve) { sy = true; findMinMaxTime(animCurve, &startTime, &stopTime, &frameRate); } animCurve = getCurve(fbxNode->LclScaling, animLayer, FBXSDK_CURVENODE_COMPONENT_Z); if (animCurve) { sz = true; findMinMaxTime(animCurve, &startTime, &stopTime, &frameRate); } if (!(sx || sy || sz || rx || ry || rz || tx || ty || tz)) return; // no animation channels assert(startTime != FLT_MAX); assert(stopTime >= 0.0f); // Determine which animation channels to create vector<unsigned int> channelAttribs; if (sx && sy && sz) { if (rx || ry || rz) { if (tx && ty && tz) { channelAttribs.push_back(Transform::ANIMATE_SCALE_ROTATE_TRANSLATE); } else { channelAttribs.push_back(Transform::ANIMATE_SCALE_ROTATE); if (tx) channelAttribs.push_back(Transform::ANIMATE_TRANSLATE_X); if (ty) channelAttribs.push_back(Transform::ANIMATE_TRANSLATE_Y); if (tz) channelAttribs.push_back(Transform::ANIMATE_TRANSLATE_Z); } } else { if (tx && ty && tz) { channelAttribs.push_back(Transform::ANIMATE_SCALE_TRANSLATE); } else { channelAttribs.push_back(Transform::ANIMATE_SCALE); if (tx) channelAttribs.push_back(Transform::ANIMATE_TRANSLATE_X); if (ty) channelAttribs.push_back(Transform::ANIMATE_TRANSLATE_Y); if (tz) channelAttribs.push_back(Transform::ANIMATE_TRANSLATE_Z); } } } else { if (rx || ry || rz) { if (tx && ty && tz) { channelAttribs.push_back(Transform::ANIMATE_ROTATE_TRANSLATE); } else { channelAttribs.push_back(Transform::ANIMATE_ROTATE); if (tx) channelAttribs.push_back(Transform::ANIMATE_TRANSLATE_X); if (ty) channelAttribs.push_back(Transform::ANIMATE_TRANSLATE_Y); if (tz) channelAttribs.push_back(Transform::ANIMATE_TRANSLATE_Z); } } else { if (tx && ty && tz) { channelAttribs.push_back(Transform::ANIMATE_TRANSLATE); } else { if (tx) channelAttribs.push_back(Transform::ANIMATE_TRANSLATE_X); if (ty) channelAttribs.push_back(Transform::ANIMATE_TRANSLATE_Y); if (tz) channelAttribs.push_back(Transform::ANIMATE_TRANSLATE_Z); } } if (sx) channelAttribs.push_back(Transform::ANIMATE_SCALE_X); if (sy) channelAttribs.push_back(Transform::ANIMATE_SCALE_Y); if (sz) channelAttribs.push_back(Transform::ANIMATE_SCALE_Z); } unsigned int channelCount = channelAttribs.size(); assert(channelCount > 0); // Allocate channel list const int channelStart = animation->getAnimationChannelCount(); for (unsigned int i = 0; i < channelCount; ++i) { AnimationChannel* channel = new AnimationChannel(); channel->setTargetId(name); channel->setInterpolation(AnimationChannel::LINEAR); channel->setTargetAttribute(channelAttribs[i]); animation->add(channel); } // Evaulate animation curve in increments of frameRate and populate channel data. FbxAMatrix fbxMatrix; Matrix matrix; double increment = 1000.0 / (double)frameRate; int frameCount = (int)ceil((double)(stopTime - startTime) / increment) + 1; // +1 because stop time is inclusive // If the animation for this node only has only 1 key frame and it is equal to the node's transform then ignore it. // This is a work around for a bug in the Blender FBX exporter. if (frameCount == 1 && fbxMatrix == fbxNode->EvaluateLocalTransform(0)) { return; } for (int frame = 0; frame < frameCount; ++frame) { double time = startTime + (frame * (double)increment); // Note: We used to clamp time to stop time, but FBX sdk does not always produce // and accurate stopTime (sometimes it is rounded down for some reason), so I'm // disabling this clamping for now as it seems more accurate under normal circumstances. //time = std::min(time, (double)stopTime); // Evalulate the animation at this time FbxTime kTime; kTime.SetMilliSeconds((FbxLongLong)time); fbxMatrix = fbxNode->EvaluateLocalTransform(kTime); copyMatrix(fbxMatrix, matrix); // Decompose the evalulated transformation matrix into separate // scale, rotation and translation. Vector3 scale; Quaternion rotation; Vector3 translation; matrix.decompose(&scale, &rotation, &translation); rotation.normalize(); // Append keyframe data to all channels for (unsigned int i = channelStart, channelEnd = channelStart + channelCount; i < channelEnd; ++i) { appendKeyFrame(fbxNode, animation->getAnimationChannel(i), (float)time, scale, rotation, translation); } } if (_groupAnimation != animation) { _gamePlayFile.addAnimation(animation); } }