reNode* reFBXAsset::importNode(FbxNode* fbxNode, reNode* parent)
{
	reNode* node = new reNode;
	node->name(fbxNode->GetName());
	FbxMatrix mat = fbxNode->EvaluateGlobalTransform(FbxTime(0));

	FbxNode *fbxParent = fbxNode->GetParent();
	if (!fbxParent)
	{
		node->transform (reTransform(fromFBXMatrix(mat)));
	}	
	else
	{
		FbxMatrix pmat = fbxParent->EvaluateGlobalTransform(FbxTime(0));
		node->transform(reTransform(fromFBXMatrix(pmat.Inverse()) * fromFBXMatrix(mat)));
	}

	if (fbxNode->GetNodeAttribute() )
	{
		FbxNodeAttribute::EType attrType = (fbxNode->GetNodeAttribute()->GetAttributeType());
		switch (attrType)
		{
		case FbxNodeAttribute::eMesh:      
			reMesh* mesh = importMesh(fbxNode);
			node->renderables->add(mesh);			
			break;	
		}
	}

	for(int i = 0; i < fbxNode->GetChildCount(); i++) 
	{
		reNode* child = importNode(fbxNode->GetChild(i), node);
		node->children->add(child);
	}
	return node;
};
Beispiel #2
0
int main(int argc, char** argv) {
	if (argc < 2) {
		printf("Usage: emdfbx model.emd skeleton.esk output.fbx\n       Can include multiple emd and esk files into one fbx.");
		getchar();
		return 1;
	}

	LibXenoverse::initializeDebuggingLog();

	vector<string> model_filenames;
	vector<string> skeleton_filenames;
	vector<string> animation_filenames;
	string export_filename = "";

	for (int i = 1; i < argc; i++)  {
		string parameter = ToString(argv[i]);

		string extension = LibXenoverse::extensionFromFilename(parameter);

		if (extension == "emd") {
			model_filenames.push_back(parameter);
		}

		if (extension == "esk") {
			skeleton_filenames.push_back(parameter);
		}

		if (extension == "ean") {
			animation_filenames.push_back(parameter);
		}

		if (extension == "fbx") {
			export_filename = parameter;
		}
	}

	if (!export_filename.size()) {
		if (model_filenames.size()) {
			export_filename = model_filenames[0] + ".fbx";
		}
		else if (skeleton_filenames.size()) {
			export_filename = skeleton_filenames[0] + ".fbx";
		}
		else {
			export_filename = "Out.fbx";
		}
	}

	// Create FBX Manager
	FbxManager *sdk_manager = FbxManager::Create();
	FbxIOSettings *ios = FbxIOSettings::Create(sdk_manager, IOSROOT);
	ios->SetBoolProp(EXP_FBX_EMBEDDED, true);
	sdk_manager->SetIOSettings(ios);

	// Create Scene
	vector<FbxNode *> global_fbx_bones;
	global_fbx_bones.reserve(300);
	vector<size_t> global_fbx_bones_index;													//TODO find a better way to link skeleton and animations
	global_fbx_bones_index.reserve(300);

	vector<FbxAnimCurveNode *> global_fbx_animation;
	global_fbx_animation.reserve(300);

	FbxScene *scene = FbxScene::Create(sdk_manager, "EMDFBXScene");


	// Load Shaders and convert it to fx file (will be use by fbx).
	vector<string> shader_names;
	shader_names.push_back("adam_shader/shader_age_vs.emb");								//must specified vs folloxed by ps shaders
	shader_names.push_back("adam_shader/shader_age_ps.emb");
	shader_names.push_back("adam_shader/shader_default_vs.emb");
	shader_names.push_back("adam_shader/shader_default_ps.emb");
	

	bool needs_install_shaders = false;
	for (size_t i = 0; i < shader_names.size(); i++) {
		if (!LibXenoverse::fileCheck(shader_names[i])) {
			needs_install_shaders = true;
			break;
		}
	}

	if (needs_install_shaders) {
		printf("Shaders not found. Please use Xenoviewer to prepare shaders in bin folder.");
		return -1;
	}

	for (size_t i = 0; i+1 < shader_names.size(); i+=2) {
		
		LibXenoverse::EMB *shader_pack_vs = new LibXenoverse::EMB();
		LibXenoverse::EMB *shader_pack_ps = new LibXenoverse::EMB();

		if (!shader_pack_vs->load(shader_names[i])) {
			delete shader_pack_vs;
			printf("Couldn't load Shader Pack %s. File is either missing, open by another application, or corrupt.", shader_names[i].c_str());
			continue;
		}
		if (!shader_pack_ps->load(shader_names[i+1])) {
			delete shader_pack_vs;
			delete shader_pack_ps;
			printf("Couldn't load Shader Pack %s. File is either missing, open by another application, or corrupt.", shader_names[i].c_str());
			continue;
		}

		shader_pack_vs->exportShadersToFx(shader_pack_vs, shader_pack_ps);					//convert all shaders in fx file with defaults program parameters (like Ogre's version).
	}



	//build FBX skeleton
	for (size_t i = 0; i < skeleton_filenames.size(); i++) {
		LibXenoverse::ESK *esk_skeleton = new LibXenoverse::ESK();
		esk_skeleton->load(skeleton_filenames[i]);
		vector<FbxNode *> fbx_bones = esk_skeleton->createFBXSkeleton(scene);

		for (size_t j = 0; j < fbx_bones.size(); j++) {
			global_fbx_bones.push_back(fbx_bones[j]);
			global_fbx_bones_index.push_back(j);											//kepp index of bone to link with animations.
		}
	}



	//build FBX animations
	for (size_t i = 0; i < animation_filenames.size(); i++) {
		
		string filename = animation_filenames.at(i);
		string ean_name = LibXenoverse::nameFromFilenameNoExtension(filename, true);

		//vector<FbxAnimCurveNode *> global_fbx_animation;

		LibXenoverse::EAN *animation = new LibXenoverse::EAN();
		if (animation->load(filename)) {

			std::vector<FbxAnimStack *> list_AnimStack;
			size_t nbAnims = animation->getAnimations().size();
			for (size_t j = 0; j < nbAnims; j++) {													//we create only one stack and one layer by animation. each will animate all bones of all skeleton.

				LibXenoverse::EANAnimation *anim_tmp = &(animation->getAnimations().at(j));

				FbxAnimStack* lAnimStack = FbxAnimStack::Create(scene, anim_tmp->getName().c_str());
				FbxAnimLayer* lAnimLayer = FbxAnimLayer::Create(scene, (anim_tmp->getName() + "_Layer0").c_str());
				lAnimStack->AddMember(lAnimLayer);

				list_AnimStack.push_back(lAnimStack);
			}

			size_t k = 0;
			for (vector<FbxNode *>::iterator it = global_fbx_bones.begin(); it != global_fbx_bones.end(); it++) {

				vector<FbxAnimCurveNode *> fbx_anim = animation->exportFBXAnimations(scene, list_AnimStack, *it, global_fbx_bones_index.at(k));

				for (size_t j = 0; j < fbx_anim.size(); j++) {
					global_fbx_animation.push_back(fbx_anim[j]);
				}
				k++;
			}
		}
		else {
			delete animation;
			animation = NULL;
		}
	}




	for (size_t i = 0; i < model_filenames.size(); i++) {
		string node_name = LibXenoverse::nameFromFilenameNoExtension(model_filenames[i]);
		string path = model_filenames[i].substr(0, model_filenames[i].size() - LibXenoverse::nameFromFilename(model_filenames[i]).size());

		LibXenoverse::EMD *emd_model = new LibXenoverse::EMD();
		emd_model->load(model_filenames[i]);

		// Fill Scene
		FbxNode *lMeshNode = NULL;

		// Create Node
		lMeshNode = FbxNode::Create(scene, node_name.c_str());
		lMeshNode->LclTranslation.Set(FbxVector4(0, 0, 0));
		lMeshNode->LclScaling.Set(FbxVector4(1, 1, 1));
		lMeshNode->LclRotation.Set(FbxVector4(0, 0, 0));

		// Create and attach Mesh
		FbxMesh *lMesh = emd_model->exportFBX(scene, lMeshNode, path);
		lMeshNode->SetNodeAttribute(lMesh);

		if (global_fbx_bones.size()) {
			emd_model->exportFBXSkin(scene, lMesh, global_fbx_bones, lMeshNode->EvaluateGlobalTransform());
		}

		// Add node to scene
		FbxNode *lRootNode = scene->GetRootNode();
		lRootNode->AddChild(lMeshNode);

	}

	// Export FBX
	int lFileFormat = sdk_manager->GetIOPluginRegistry()->GetNativeWriterFormat();
	FbxExporter* lExporter = FbxExporter::Create(sdk_manager, "");
	bool lExportStatus = lExporter->Initialize(export_filename.c_str(), lFileFormat, sdk_manager->GetIOSettings());

	if (!lExportStatus) {
		printf("Call to FbxExporter::Initialize() failed.\n");
		printf("Error returned: %s\n\n", lExporter->GetStatus().GetErrorString());
		return 1;
	}

	scene->GetGlobalSettings().SetAxisSystem(FbxAxisSystem::eMax);
	scene->GetGlobalSettings().SetSystemUnit(FbxSystemUnit::m);
	

	// Export scene
	lExporter->Export(scene);
	lExporter->Destroy();
	return 0;
}
/**
 * Add a bind pose to the scene based on the FbxMesh and skinning settings of the given node
 */
void FFbxExporter::CreateBindPose(FbxNode* MeshRootNode)
{
	if (!MeshRootNode)
	{
		return;
	}

	// In the bind pose, we must store all the link's global matrix at the time of the bind.
	// Plus, we must store all the parent(s) global matrix of a link, even if they are not
	// themselves deforming any model.

	// Create a bind pose with the link list

	FbxArray<FbxNode*> lClusteredFbxNodes;
	int                       i, j;

	if (MeshRootNode->GetNodeAttribute())
	{
		int lSkinCount=0;
		int lClusterCount=0;
		switch (MeshRootNode->GetNodeAttribute()->GetAttributeType())
		{
		case FbxNodeAttribute::eMesh:
		case FbxNodeAttribute::eNurbs:
		case FbxNodeAttribute::ePatch:

			lSkinCount = ((FbxGeometry*)MeshRootNode->GetNodeAttribute())->GetDeformerCount(FbxDeformer::eSkin);
			//Go through all the skins and count them
			//then go through each skin and get their cluster count
			for(i=0; i<lSkinCount; ++i)
			{
				FbxSkin *lSkin=(FbxSkin*)((FbxGeometry*)MeshRootNode->GetNodeAttribute())->GetDeformer(i, FbxDeformer::eSkin);
				lClusterCount+=lSkin->GetClusterCount();
			}
			break;
		}
		//if we found some clusters we must add the node
		if (lClusterCount)
		{
			//Again, go through all the skins get each cluster link and add them
			for (i=0; i<lSkinCount; ++i)
			{
				FbxSkin *lSkin=(FbxSkin*)((FbxGeometry*)MeshRootNode->GetNodeAttribute())->GetDeformer(i, FbxDeformer::eSkin);
				lClusterCount=lSkin->GetClusterCount();
				for (j=0; j<lClusterCount; ++j)
				{
					FbxNode* lClusterNode = lSkin->GetCluster(j)->GetLink();
					AddNodeRecursively(lClusteredFbxNodes, lClusterNode);
				}

			}

			// Add the patch to the pose
			lClusteredFbxNodes.Add(MeshRootNode);
		}
	}

	// Now create a bind pose with the link list
	if (lClusteredFbxNodes.GetCount())
	{
		// A pose must be named. Arbitrarily use the name of the patch node.
		FbxPose* lPose = FbxPose::Create(Scene, MeshRootNode->GetName());

		// default pose type is rest pose, so we need to set the type as bind pose
		lPose->SetIsBindPose(true);

		for (i=0; i<lClusteredFbxNodes.GetCount(); i++)
		{
			FbxNode*  lKFbxNode   = lClusteredFbxNodes.GetAt(i);
			FbxMatrix lBindMatrix = lKFbxNode->EvaluateGlobalTransform();

			lPose->Add(lKFbxNode, lBindMatrix);
		}

		// Add the pose to the scene
		Scene->AddPose(lPose);
	}
}
/**
 * Adds Fbx Clusters necessary to skin a skeletal mesh to the bones in the BoneNodes list
 */
void FFbxExporter::BindMeshToSkeleton(const USkeletalMesh* SkelMesh, FbxNode* MeshRootNode, TArray<FbxNode*>& BoneNodes)
{
	const FSkeletalMeshResource* SkelMeshResource = SkelMesh->GetImportedResource();
	const FStaticLODModel& SourceModel = SkelMeshResource->LODModels[0];
	const int32 VertexCount = SourceModel.NumVertices;

	FbxAMatrix MeshMatrix;

	FbxScene* lScene = MeshRootNode->GetScene();
	if( lScene ) 
	{
		MeshMatrix = MeshRootNode->EvaluateGlobalTransform();
	}
	
	FbxGeometry* MeshAttribute = (FbxGeometry*) MeshRootNode->GetNodeAttribute();
	FbxSkin* Skin = FbxSkin::Create(Scene, "");
	
	const int32 BoneCount = BoneNodes.Num();
	for(int32 BoneIndex = 0; BoneIndex < BoneCount; ++BoneIndex)
	{
		FbxNode* BoneNode = BoneNodes[BoneIndex];

		// Create the deforming cluster
		FbxCluster *CurrentCluster = FbxCluster::Create(Scene,"");
		CurrentCluster->SetLink(BoneNode);
		CurrentCluster->SetLinkMode(FbxCluster::eTotalOne);

		// Add all the vertices that are weighted to the current skeletal bone to the cluster
		// NOTE: the bone influence indices contained in the vertex data are based on a per-chunk
		// list of verts.  The convert the chunk bone index to the mesh bone index, the chunk's
		// boneMap is needed
		int32 VertIndex = 0;
		const int32 SectionCount = SourceModel.Sections.Num();
		for(int32 SectionIndex = 0; SectionIndex < SectionCount; ++SectionIndex)
		{
			const FSkelMeshSection& Section = SourceModel.Sections[SectionIndex];

			for(int32 SoftIndex = 0; SoftIndex < Section.SoftVertices.Num(); ++SoftIndex)
			{
				const FSoftSkinVertex& Vert = Section.SoftVertices[SoftIndex];

				for(int32 InfluenceIndex = 0; InfluenceIndex < MAX_TOTAL_INFLUENCES; ++InfluenceIndex)
				{
					int32 InfluenceBone		= Section.BoneMap[ Vert.InfluenceBones[InfluenceIndex] ];
					float InfluenceWeight	= Vert.InfluenceWeights[InfluenceIndex] / 255.f;

					if(InfluenceBone == BoneIndex && InfluenceWeight > 0.f)
					{
						CurrentCluster->AddControlPointIndex(VertIndex, InfluenceWeight);
					}
				}

				++VertIndex;
			}
		}

		// Now we have the Patch and the skeleton correctly positioned,
		// set the Transform and TransformLink matrix accordingly.
		CurrentCluster->SetTransformMatrix(MeshMatrix);

		FbxAMatrix LinkMatrix;
		if( lScene )
		{
			LinkMatrix = BoneNode->EvaluateGlobalTransform();
		}

		CurrentCluster->SetTransformLinkMatrix(LinkMatrix);

		// Add the clusters to the mesh by creating a skin and adding those clusters to that skin.
		Skin->AddCluster(CurrentCluster);
	}

	// Add the skin to the mesh after the clusters have been added
	MeshAttribute->AddDeformer(Skin);
}
Beispiel #5
0
CC_FILE_ERROR FBXFilter::loadFile(const char* filename, ccHObject& container, bool alwaysDisplayLoadDialog/*=true*/, bool* coordinatesShiftEnabled/*=0*/, CCVector3d* coordinatesShift/*=0*/)
{
	// Initialize the SDK manager. This object handles memory management.
	FbxManager* lSdkManager = FbxManager::Create();

	// Create the IO settings object.
	FbxIOSettings *ios = FbxIOSettings::Create(lSdkManager, IOSROOT);
	lSdkManager->SetIOSettings(ios);
	
	// Import options determine what kind of data is to be imported.
	// True is the default, but here we’ll set some to true explicitly, and others to false.
	//(*(lSdkManager->GetIOSettings())).SetBoolProp(IMP_FBX_MATERIAL,        true);
	//(*(lSdkManager->GetIOSettings())).SetBoolProp(IMP_FBX_TEXTURE,         true);
	
	// Create an importer using the SDK manager.
	FbxImporter* lImporter = FbxImporter::Create(lSdkManager,"");

	CC_FILE_ERROR result = CC_FERR_NO_ERROR;
	
	// Use the first argument as the filename for the importer.
	if (!lImporter->Initialize(filename, -1, lSdkManager->GetIOSettings()))
	{ 
		ccLog::Warning(QString("[FBX] Error: %1").arg(lImporter->GetStatus().GetErrorString()));
		result = CC_FERR_READING;
	}
	else
	{
		// Create a new scene so that it can be populated by the imported file.
		FbxScene* lScene = FbxScene::Create(lSdkManager,"myScene");

		// Import the contents of the file into the scene.
		if (lImporter->Import(lScene))
		{
			// Print the nodes of the scene and their attributes recursively.
			// Note that we are not printing the root node because it should
			// not contain any attributes.
			FbxNode* lRootNode = lScene->GetRootNode();
			std::vector<FbxNode*> nodes;
			nodes.push_back(lRootNode);

			while (!nodes.empty())
			{
				FbxNode* lNode = nodes.back();
				nodes.pop_back();

				const char* nodeName = lNode->GetName();
#ifdef _DEBUG
				ccLog::Print(QString("Node: %1 - %2 properties").arg(nodeName).arg(lNode->GetNodeAttributeCount()));
#endif
				// scan the node's attributes.
				for(int i=0; i<lNode->GetNodeAttributeCount(); i++)
				{
					FbxNodeAttribute* pAttribute = lNode->GetNodeAttributeByIndex(i);
					FbxNodeAttribute::EType type = pAttribute->GetAttributeType();
#ifdef _DEBUG
					ccLog::Print(QString("\tProp. #%1").arg(GetAttributeTypeName(type)));
#endif

					switch(type)
					{ 
					case FbxNodeAttribute::eMesh:
						{
							ccMesh* mesh = FromFbxMesh(static_cast<FbxMesh*>(pAttribute),alwaysDisplayLoadDialog,coordinatesShiftEnabled,coordinatesShift);
							if (mesh)
							{
								//apply transformation
								FbxAMatrix& transform = lNode->EvaluateGlobalTransform();
								ccGLMatrix mat;
								float* data = mat.data();
								for (int c=0; c<4; ++c)
								{
									FbxVector4 C = transform.GetColumn(c);
									*data++ = static_cast<float>(C[0]);
									*data++ = static_cast<float>(C[1]);
									*data++ = static_cast<float>(C[2]);
									*data++ = static_cast<float>(C[3]);
								}
								mesh->applyGLTransformation_recursive(&mat);

								if (mesh->getName().isEmpty())
									mesh->setName(nodeName);

								container.addChild(mesh);
							}
						}
						break;

					case FbxNodeAttribute::eUnknown: 
					case FbxNodeAttribute::eNull:
					case FbxNodeAttribute::eMarker:
					case FbxNodeAttribute::eSkeleton:
					case FbxNodeAttribute::eNurbs:
					case FbxNodeAttribute::ePatch:
					case FbxNodeAttribute::eCamera:
					case FbxNodeAttribute::eCameraStereo:
					case FbxNodeAttribute::eCameraSwitcher:
					case FbxNodeAttribute::eLight:
					case FbxNodeAttribute::eOpticalReference:
					case FbxNodeAttribute::eOpticalMarker:
					case FbxNodeAttribute::eNurbsCurve:
					case FbxNodeAttribute::eTrimNurbsSurface:
					case FbxNodeAttribute::eBoundary:
					case FbxNodeAttribute::eNurbsSurface:
					case FbxNodeAttribute::eShape:
					case FbxNodeAttribute::eLODGroup:
					case FbxNodeAttribute::eSubDiv:
					default:
						//not handled yet
						break;
					}
				}

				// Recursively add the children.
				for(int j=0; j<lNode->GetChildCount(); j++)
				{
					nodes.push_back(lNode->GetChild(j));
				}
			}
		}
	}

	// The file is imported, so get rid of the importer.
	lImporter->Destroy();
	// Destroy the SDK manager and all the other objects it was handling.
	lSdkManager->Destroy();

	return container.getChildrenNumber() == 0 ? CC_FERR_NO_LOAD : CC_FERR_NO_ERROR;
}
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;
}
Beispiel #7
0
void ExportFbxMesh(const Value& obj)
{
	string name = obj["Name"].GetString();

	FbxNode* pNode = FbxNode::Create(pManager, name.c_str());
	FbxMesh* pMesh = FbxMesh::Create(pManager, name.c_str());
	pNode->AddNodeAttribute(pMesh);
	pScene->GetRootNode()->AddChild(pNode);

	int numVertex = obj["NumVertex"].GetInt();
	
	{
		pMesh->InitControlPoints(numVertex);
		FbxVector4* lControlPoints = pMesh->GetControlPoints();
		const Value& pos = obj["Position"];
		for (int i = 0; i < numVertex; i++)
		{
			double x = pos[i * 3 + 0].GetDouble();
			x = -x;
			double y = pos[i * 3 + 1].GetDouble();
			double z = pos[i * 3 + 2].GetDouble();
			lControlPoints[i] = FbxVector4(x, y, z);
		}
	}

	{
		FbxGeometryElementNormal* lGeometryElementNormal = pMesh->CreateElementNormal();
		lGeometryElementNormal->SetMappingMode(FbxGeometryElement::eByControlPoint);
		lGeometryElementNormal->SetReferenceMode(FbxGeometryElement::eDirect);
		FbxLayerElementArrayTemplate<FbxVector4>& array = lGeometryElementNormal->GetDirectArray();

		const Value& normal = obj["Normal"];
		for (int i = 0; i < numVertex; i++)
		{
			double x = normal[i * 3 + 0].GetDouble();
			x = -x;
			double y = normal[i * 3 + 1].GetDouble();
			double z = normal[i * 3 + 2].GetDouble();
			array.Add(FbxVector4(x, y, z));
		}
	}

	{
		FbxGeometryElementUV* lUVDiffuseElement = pMesh->CreateElementUV("DiffuseUV");
		FBX_ASSERT(lUVDiffuseElement != NULL);
		lUVDiffuseElement->SetMappingMode(FbxGeometryElement::eByControlPoint);
		lUVDiffuseElement->SetReferenceMode(FbxGeometryElement::eDirect);
		FbxLayerElementArrayTemplate<FbxVector2>& array = lUVDiffuseElement->GetDirectArray();

		const Value& v = obj["UV0"];
		for (int i = 0; i < numVertex; i++)
		{
			double x = v[i * 2 + 0].GetDouble();
			double y = v[i * 2 + 1].GetDouble();
			array.Add(FbxVector2(x, y));
		}
	}

	{
		const Value& color = obj["Color"];
		if (!color.IsNull())
		{
			FbxGeometryElementVertexColor* pColorElement = pMesh->CreateElementVertexColor();
			FBX_ASSERT(pColorElement != NULL);
			pColorElement->SetMappingMode(FbxGeometryElement::eByControlPoint);
			pColorElement->SetReferenceMode(FbxGeometryElement::eDirect);
			FbxLayerElementArrayTemplate<FbxColor>& array = pColorElement->GetDirectArray();

			for (int i = 0; i < numVertex; i++)
			{
				double r = color[i * 4 + 0].GetDouble();
				double g = color[i * 4 + 1].GetDouble();
				double b = color[i * 4 + 2].GetDouble();
				double a = color[i * 4 + 3].GetDouble();
				array.Add(FbxColor(r, g, b, a));
			}
		}
	}

	{
		const Value& Indeices = obj["Indeices"];

		for (uint32_t subMesh = 0; subMesh < Indeices.Size(); subMesh++)
		{
			const Value& index0 = Indeices[subMesh];
			int numIndex = index0.Size();
			printf("index %d\n", numIndex);
			for (int i = 0; i < numIndex / 3; i++)
			{
				pMesh->BeginPolygon(-1, -1, subMesh);
				int index[3] = {
					index0[i * 3 + 0].GetInt(),
					index0[i * 3 + 1].GetInt(),
					index0[i * 3 + 2].GetInt(),
				};

				pMesh->AddPolygon(index[0]);
				pMesh->AddPolygon(index[2]);
				pMesh->AddPolygon(index[1]);
				pMesh->EndPolygon();
			}
		}
	}

	//////////////////////////////////////////////////////////////////////////

	// export skin
	const Value& boneIndex = obj["BoneIndex"];

	if (!boneIndex.IsNull())
	{
		if (fbxBones.empty())
		{
			printf("no bones, can not export skin");
			return;
		}

		const Value& boneWeight = obj["BoneWeight"];
		vector<FbxCluster*> clusters(fbxBones.size(), NULL);
		for (uint32_t i = 0; i < fbxBones.size(); i++) {
			FbxCluster* pCluster = FbxCluster::Create(pScene, "");
			pCluster->SetLink(fbxBones[i]);
			pCluster->SetLinkMode(FbxCluster::eTotalOne);
			clusters[i] = pCluster;
		}

		for (int i = 0; i < numVertex; i++) {
			for (int j = 0; j < 4; j++) {
				int bone = boneIndex[i * 4 + j].GetInt();
				double weight = boneWeight[i * 4 + j].GetDouble();
				clusters[bone]->AddControlPointIndex(i, weight);
			}
		}

		FbxSkin* lSkin = FbxSkin::Create(pScene, "");

		FbxScene* p = pNode->GetScene();
		FbxAMatrix modelMatrix = pNode->EvaluateGlobalTransform();
		for (uint32_t i = 0; i < clusters.size(); i++) {
			clusters[i]->SetTransformMatrix(modelMatrix);
			FbxAMatrix boneMatrix = fbxBones[i]->EvaluateGlobalTransform();
			clusters[i]->SetTransformLinkMatrix(boneMatrix);
			lSkin->AddCluster(clusters[i]);
		}

		pMesh->AddDeformer(lSkin);
	}

}
Beispiel #8
0
void ExportBones(const Value& obj)
{
	const Value& boneName = obj["BoneName"];
	if (boneName.IsNull())
	{
		return;
	}

	//string name = obj["Name"].GetString();

	const Value& parentIndex = obj["ParentIndex"];
	const Value& boneTransform = obj["BoneTransform"];

	fbxBones.resize(boneName.Size());
	for (uint32_t i = 0; i < fbxBones.size(); i++)
	{
		FbxNode* boneNode = FbxNode::Create(pScene, boneName[i].GetString());
		fbxBones[i] = boneNode;
		double x, y, z, w;
		x = boneTransform[i * 7 + 0].GetDouble();
		x = -x;
		y = boneTransform[i * 7 + 1].GetDouble();
		z = boneTransform[i * 7 + 2].GetDouble();
		boneNode->LclTranslation.Set(FbxDouble3(x, y, z));

		x = boneTransform[i * 7 + 3].GetDouble();
		y = boneTransform[i * 7 + 4].GetDouble();
		z = boneTransform[i * 7 + 5].GetDouble();
		w = boneTransform[i * 7 + 6].GetDouble();

		FbxSkeleton* pSkeleton = FbxSkeleton::Create(pScene, boneName[i].GetString());
		int parent = parentIndex[i].GetInt();
		if (parent == -1) {
			pSkeleton->SetSkeletonType(FbxSkeleton::eRoot);
		}
		else {
			pSkeleton->SetSkeletonType(FbxSkeleton::eLimbNode);
			pSkeleton->Size.Set(0.1);
		}

		boneNode->SetNodeAttribute(pSkeleton);
	}

	for (uint32_t i = 0; i < fbxBones.size(); i++) {
		int parent = parentIndex[i].GetInt();
		if (parent == -1) {
			pScene->GetRootNode()->AddChild(fbxBones[i]);
		}
		else {
			fbxBones[parent]->AddChild(fbxBones[i]);
		}
	}

	FbxPose* lPose = FbxPose::Create(pScene, boneName[0].GetString());
	// default pose type is rest pose, so we need to set the type as bind pose
	lPose->SetIsBindPose(true);

	for (uint32_t i = 0; i < fbxBones.size(); i++) {
		FbxNode*  lKFbxNode = fbxBones[i];
		FbxMatrix lBindMatrix = lKFbxNode->EvaluateGlobalTransform();
		lPose->Add(lKFbxNode, lBindMatrix);
	}

	pScene->AddPose(lPose);

}