示例#1
0
// 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;
}
示例#2
0
		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 );
	}
}
示例#4
0
		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;
			}
		}
示例#5
0
	FbxAMatrix& FbxLoader::GetRootMatrixGlobalAtTime(double time)
	{
		FbxTime ftime;
		ftime.SetSecondDouble(time);
		ftime = m_animEvaluator->ValidateTime(ftime);
		return m_animEvaluator->GetNodeGlobalTransform(m_firstNode, ftime);
	}
示例#6
0
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);
}
示例#7
0
	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);
	}
示例#8
0
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;
	}
示例#10
0
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);
}
示例#11
0
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 );
	}
}
示例#12
0
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);
	}
}
示例#13
0
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);
	}
}
示例#14
0
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());
}
示例#15
0
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();
    }
}
示例#16
0
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;
	}
示例#18
0
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);
	}



}
示例#19
0
bool frameCompare( FbxTime i , FbxTime j ) { return ( i.GetFrameCount() < j.GetFrameCount() ); }
示例#20
0
//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);
		}

	}
}
示例#21
0
文件: FBXScene.cpp 项目: Malow/NDYGFX
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);
	}
}
示例#22
0
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;
}
示例#23
0
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;
}
示例#24
0
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;
}
示例#25
0
		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
		}
示例#26
0
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, &currentKeyIndex);
						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());
		}
	}
}
示例#27
0
文件: FBXScene.cpp 项目: Malow/NDYGFX
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();
}
示例#28
0
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;
}
示例#29
0
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);
	}
}
示例#30
0
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);
    }
}