virtual bool CookConvex(FName Format, const TArray<FVector>& SrcBuffer, TArray<uint8>& OutBuffer, bool bDeformableMesh = false) const override
	{
#if WITH_PHYSX
		PxPlatform::Enum PhysXFormat = PxPlatform::ePC;
		bool bIsPhysXFormatValid = GetPhysXFormat(Format, PhysXFormat);
		check(bIsPhysXFormatValid);

		PxConvexMeshDesc PConvexMeshDesc;
		PConvexMeshDesc.points.data = SrcBuffer.GetData();
		PConvexMeshDesc.points.count = SrcBuffer.Num();
		PConvexMeshDesc.points.stride = sizeof(FVector);
		if (bDeformableMesh)
		{
			PConvexMeshDesc.flags = PxConvexFlag::eCOMPUTE_CONVEX | PxConvexFlag::eINFLATE_CONVEX;
		}
		else
		{
			PConvexMeshDesc.flags = PxConvexFlag::eCOMPUTE_CONVEX;
		}

		// Set up cooking
		const PxCookingParams Params = PhysXCooking->getParams();
		PxCookingParams NewParams = Params;
		NewParams.targetPlatform = PhysXFormat;
		if (bDeformableMesh)
		{
			// Meshes which can be deformed need different cooking parameters to inhibit vertex welding and add an extra skin around the collision mesh for safety.
			// We need to set the meshWeldTolerance to zero, even when disabling 'clean mesh' as PhysX will attempt to perform mesh cleaning anyway according to this meshWeldTolerance
			// if the convex hull is not well formed.
			// Set the skin thickness as a proportion of the overall size of the mesh as PhysX's internal tolerances also use the overall size to calculate the epsilon used.
			const FBox Bounds(SrcBuffer);
			const float MaxExtent = (Bounds.Max - Bounds.Min).Size();
			NewParams.skinWidth = MaxExtent / 512.0f;
			NewParams.meshPreprocessParams = PxMeshPreprocessingFlags(PxMeshPreprocessingFlag::eDISABLE_CLEAN_MESH);
			NewParams.areaTestEpsilon = 0.0f;
			NewParams.meshWeldTolerance = 0.0f;
			PhysXCooking->setParams(NewParams);
		}

		// Cook the convex mesh to a temp buffer
		TArray<uint8> CookedMeshBuffer;
		FPhysXOutputStream Buffer(&CookedMeshBuffer);
		bool Result = PhysXCooking->cookConvexMesh(PConvexMeshDesc, Buffer);
		
		// Return default cooking params to normal
		if (bDeformableMesh)
		{
			PhysXCooking->setParams(Params);
		}

		if( Result && CookedMeshBuffer.Num() > 0 )
		{
			// Append the cooked data into cooked buffer
			OutBuffer.Append( CookedMeshBuffer );
			return true;
		}
#endif		// WITH_PHYSX
		return false;
	}
Esempio n. 2
0
void ConvertFbxFile(string inFilename, string outFilename, vector<AnimClip>& animClips, CollisionGeneration generateCollision)
{
	Mesh mesh;

	{
		//Get mesh from FileReader
		FbxFileReader fbxFile(inFilename);	
		mesh = fbxFile.GetMesh();

		std::cout << "\nProcessing FBX file " << inFilename << "...\n\n";
		//Extract vertex attributes and skeleton
		mesh.ExtractData();
		
		std::cout << "Done.\nOptimizing vertex attributes... ";
		//Remove duplicates and use indirect arrays
		mesh.Optimize();

		std::cout << "Done.\nExtracting bone transforms... ";
		//Get bone transforms
		for(auto& animClip : animClips)
			for(auto& transformAtTime : animClip.TransformsAtTimeStamps)
				transformAtTime.second = mesh.GetBoneTransforms(transformAtTime.first);
	}

	std::cout << "Done.\nChecking if vertices are linked to more than 4 bones... ";
	//Make sure none of the vertices is skinned to more than 4 bones
	for(auto& elem : mesh.BlendInformation.data)
		while(elem.BlendIndices.size() > 4)
		{
			elem.BlendIndices.pop_back();
			elem.BlendWeights.pop_back();
		}

	std::cout << "Done.\nBuilding vertex- and indexbuffers... ";
	// Construct vertexbuffer/indexBuffer
	vector<Vertex> vertexBuffer;
	vector<unsigned int> indexBuffer;
	for(unsigned int i=0; i < mesh.Positions.indices.size(); ++i){
		Vertex newVert;
		newVert.iPosition = mesh.Positions.indices[i];

		newVert.iTexCoord =		mesh.TexCoords.indices.empty()			? 0 : mesh.TexCoords.indices[i];
		newVert.iNormal =		mesh.Normals.indices.empty()			? 0 : mesh.Normals.indices[i];
		newVert.iTangent =		mesh.Tangents.indices.empty()			? 0 : mesh.Tangents.indices[i];
		newVert.iBinormal =		mesh.Binormals.indices.empty()			? 0 : mesh.Binormals.indices[i];
		newVert.iVertexColor =	mesh.Colors.indices.empty()				? 0 : mesh.Colors.indices[i];
		newVert.iAnimData =		mesh.BlendInformation.indices.empty()	? 0 : mesh.BlendInformation.indices[i];

		auto it = find(vertexBuffer.begin(), vertexBuffer.end(), newVert);

		if(it==vertexBuffer.end()){
			vertexBuffer.push_back(newVert);
			it = vertexBuffer.end() - 1;
		}

		indexBuffer.push_back(it-vertexBuffer.begin());
	}

	std::cout << "Done.\nWriting mesh data... ";
	//Write a binary file containing all of the mesh & skeleton data

	unsigned int version=1, nrOfUVChannels=mesh.Normals.data.empty() ?0:1, vertexFormat=0;

	//Build vertex format
	vertexFormat |= mesh.Normals.data.empty()			? 0 : 1 << 0;
	vertexFormat |= mesh.Tangents.data.empty()			? 0 : 1 << 1;
	vertexFormat |= mesh.Binormals.data.empty()			? 0 : 1 << 2;
	vertexFormat |= mesh.Colors.data.empty()			? 0 : 1 << 3;
	vertexFormat |= mesh.BlendInformation.data.empty()	? 0 : 1 << 4;
		
	//Create an output file
	BinaryWriter oFile(outFilename + ".ttmesh");

	oFile.Write<unsigned short>(version); //version number
	oFile.Write<unsigned char>(vertexFormat); // vertex format
	oFile.Write<unsigned char>(nrOfUVChannels); // nr of texcoord channels
	
	oFile.Write<unsigned int>(mesh.Positions.data.size()); // nr of positions

	// nr of texcoords
	if(nrOfUVChannels > 0)
		oFile.Write<unsigned int>(mesh.TexCoords.data.size());
	
	// nr of normals
	if(vertexFormat & 1 << 0)
		oFile.Write<unsigned int>(mesh.Normals.data.size()); 
	
	// nr of tangents
	if(vertexFormat & 1 << 1)
		oFile.Write<unsigned int>(mesh.Tangents.data.size()); 
	
	// nr of binormals
	if(vertexFormat & 1 << 2)
		oFile.Write<unsigned int>(mesh.Binormals.data.size()); 
	
	// nr of vertex colors
	if(vertexFormat & 1 << 3)
		oFile.Write<unsigned int>(mesh.Colors.data.size()); 
	
	//nr of blendweights/-indices
	if(vertexFormat & 1 << 4)
		oFile.Write<unsigned int>(mesh.BlendInformation.data.size());
	
	oFile.Write<unsigned int>(vertexBuffer.size()); // nr of vertices
	oFile.Write<unsigned int>(indexBuffer.size()); // nr of indices

	for(auto& elem : mesh.Positions.data) //positions
		oFile.Write<FbxVector4>(elem);
	
	for(auto& elem : mesh.TexCoords.data) //texCoords
		oFile.Write<FbxVector2>(FbxVector2(elem.mData[0],1-elem.mData[1]));
	
	for(auto& elem : mesh.Normals.data) //normals
		oFile.Write<FbxVector4>(elem);

	for(auto& elem : mesh.Tangents.data) //tangents
		oFile.Write<FbxVector4>(elem);

	for(auto& elem : mesh.Binormals.data) //binormals
		oFile.Write<FbxVector4>(elem);
	
	for(auto& elem : mesh.Colors.data) //vertex colors
		oFile.Write<FbxColor>(elem);
	
	for(auto& elem : mesh.BlendInformation.data){ 
		//blend indices
		oFile.Write<unsigned int>(elem.BlendIndices.size() );
		for(auto index : elem.BlendIndices)
			oFile.Write<unsigned int>(index);

		//blend weights
		for(auto weight : elem.BlendWeights)
			oFile.Write<float>(static_cast<float>(weight) );
	}

	//Vertex buffer
	for(auto& vertex : vertexBuffer){
		oFile.Write<unsigned int>(vertex.iPosition);
		if(nrOfUVChannels > 0)
			oFile.Write<unsigned int>(vertex.iTexCoord);
		if(vertexFormat & 1 << 0)
			oFile.Write<unsigned int>(vertex.iNormal);
		if(vertexFormat & 1 << 1)
			oFile.Write<unsigned int>(vertex.iTangent);
		if(vertexFormat & 1 << 2)
			oFile.Write<unsigned int>(vertex.iBinormal);
		if(vertexFormat & 1 << 3)
			oFile.Write<unsigned int>(vertex.iVertexColor);
		if(vertexFormat & 1 << 4)
			oFile.Write<unsigned int>(vertex.iAnimData);
	}

	//Index buffer
	for(auto index : indexBuffer)
		oFile.Write<unsigned int>(index);

	std::cout << "Done.\nWriting skeleton data... ";
	//Bones (#, names, bindposes)
	oFile.Write<unsigned int>( mesh.Skeleton.size() );
	for(auto& bone : mesh.Skeleton){
		oFile.Write<std::string>(bone.Name);
		oFile.Write<FbxAMatrix>(bone.BindPose);
	}
	std::cout << "Done.\nWriting bone animations... ";
	//animClips (#, name, fps, nrOfKeys, keyTime0, boneTransforms0, keyTime1, boneTransforms1...)
	oFile.Write<unsigned int>( animClips.size() );
	for(auto& animClip : animClips){
		oFile.Write<std::string>(animClip.Name);
		oFile.Write<float>(animClip.FramesPerSecond);
		oFile.Write<unsigned int>( animClip.TransformsAtTimeStamps.size() );

		for(auto& animKey : animClip.TransformsAtTimeStamps){
			oFile.Write<float>(static_cast<float>(animKey.first) );
			
			for(auto& boneTransform : animKey.second)
				oFile.Write<FbxAMatrix>(boneTransform);
		}
	}

	//Check if we need to generate a collision mesh
	if(generateCollision == CollisionGeneration::None)
	{
		std::cout << "Done.\n\nOperation succeeded!\n\n";
		return;
	}

	std::cout << "Done.\nWriting PhysX data... ";
	//Initialize cooker
	PxCookingParams params{ PxTolerancesScale() };
	PxCooking* pCooker = PxCreateCooking(PX_PHYSICS_VERSION, PxGetFoundation(), params);

	//Build a vertex buffer for PhysX (containing only vertex positions), also copy index buffer, casting to PxU32

	unsigned int nrOfVerts = mesh.Positions.data.size();
	unsigned int nrOfIndices = mesh.Positions.indices.size();
	PxVec3* pVertices	= new PxVec3[nrOfVerts];
	PxU32* pIndices		= new PxU32[nrOfIndices];

	for(unsigned int i=0; i<nrOfVerts; ++i){
		auto pos = mesh.Positions.data[i];
		pVertices[i] = PxVec3( static_cast<PxReal>(pos.mData[0]), static_cast<PxReal>(pos.mData[1]), static_cast<PxReal>(pos.mData[2]) );
	}

	for(unsigned int i=0; i<nrOfIndices; ++i)
		pIndices[i] = static_cast<PxU32>(mesh.Positions.indices[i]);
	
	PxTriangleMeshDesc triMeshDesc;
	PxConvexMeshDesc convexMeshDesc;

	//Build physical model 
	switch(generateCollision){
	case CollisionGeneration::Concave: 
		//Fill desc
		triMeshDesc.points.count		= nrOfVerts;
		triMeshDesc.triangles.count		= nrOfIndices / 3;
		triMeshDesc.points.stride		= sizeof(PxVec3);
		triMeshDesc.triangles.stride	= 3 * sizeof(PxU32);
		triMeshDesc.points.data			= pVertices;
		triMeshDesc.triangles.data		= pIndices;
		//Cook
		pCooker->cookTriangleMesh(triMeshDesc, UserStream((outFilename + ".ttcol").c_str(), false));
		break;
	case CollisionGeneration::Convex:
		//Fill desc
		convexMeshDesc.points.count		= nrOfVerts;    
		convexMeshDesc.triangles.count	= nrOfIndices / 3;    
		convexMeshDesc.points.stride	= sizeof(PxVec3);    
		convexMeshDesc.triangles.stride = 3*sizeof(PxU32);    
		convexMeshDesc.points.data		= pVertices;
		convexMeshDesc.triangles.data	= pIndices;    
		convexMeshDesc.flags.set(PxConvexFlag::Enum::eCOMPUTE_CONVEX);
		//Cook
		pCooker->cookConvexMesh(convexMeshDesc, UserStream( (outFilename + ".ttcol").c_str(), false));
		break;
	};

	//Stop cooking
	pCooker->release();

	std::cout << "Done.\n\nOperation succeeded!\n\n";
}
    virtual EPhysXCookingResult CookConvex(FName Format, int32 RuntimeCookFlags, const TArray<FVector>& SrcBuffer, TArray<uint8>& OutBuffer, bool bDeformableMesh = false) const override
    {
        EPhysXCookingResult CookResult = EPhysXCookingResult::Failed;

#if WITH_PHYSX
        PxPlatform::Enum PhysXFormat = PxPlatform::ePC;
        bool bIsPhysXFormatValid = GetPhysXFormat(Format, PhysXFormat);
        check(bIsPhysXFormatValid);

        PxConvexMeshDesc PConvexMeshDesc;
        PConvexMeshDesc.points.data = SrcBuffer.GetData();
        PConvexMeshDesc.points.count = SrcBuffer.Num();
        PConvexMeshDesc.points.stride = sizeof(FVector);
        PConvexMeshDesc.flags = PxConvexFlag::eCOMPUTE_CONVEX;

        // Set up cooking
        const PxCookingParams Params = PhysXCooking->getParams();
        PxCookingParams NewParams = Params;
        NewParams.targetPlatform = PhysXFormat;

        if(RuntimeCookFlags & ERuntimePhysxCookOptimizationFlags::SuppressFaceRemapTable)
        {
            NewParams.suppressTriangleMeshRemapTable = true;
        }

        if (bDeformableMesh)
        {
            // Meshes which can be deformed need different cooking parameters to inhibit vertex welding and add an extra skin around the collision mesh for safety.
            // We need to set the meshWeldTolerance to zero, even when disabling 'clean mesh' as PhysX will attempt to perform mesh cleaning anyway according to this meshWeldTolerance
            // if the convex hull is not well formed.
            // Set the skin thickness as a proportion of the overall size of the mesh as PhysX's internal tolerances also use the overall size to calculate the epsilon used.
            const FBox Bounds(SrcBuffer);
            const float MaxExtent = (Bounds.Max - Bounds.Min).Size();

            NewParams.meshPreprocessParams = PxMeshPreprocessingFlags(PxMeshPreprocessingFlag::eDISABLE_CLEAN_MESH);
            NewParams.meshWeldTolerance = 0.0f;
        }

        PhysXCooking->setParams(NewParams);

        // Cook the convex mesh to a temp buffer
        TArray<uint8> CookedMeshBuffer;
        FPhysXOutputStream Buffer(&CookedMeshBuffer);
        if (PhysXCooking->cookConvexMesh(PConvexMeshDesc, Buffer))
        {
            CookResult = EPhysXCookingResult::Succeeded;
        }
        else
        {
            if (!(PConvexMeshDesc.flags & PxConvexFlag::eINFLATE_CONVEX))
            {
                // We failed to cook without inflating convex. Let's try again with inflation
                //This is not ideal since it makes the collision less accurate. It's needed if given verts are extremely close.
                PConvexMeshDesc.flags |= PxConvexFlag::eINFLATE_CONVEX;
                if (PhysXCooking->cookConvexMesh(PConvexMeshDesc, Buffer))
                {
                    CookResult = EPhysXCookingResult::SucceededWithInflation;
                }
            }
        }

        // Return default cooking params to normal
        if (bDeformableMesh)
        {
            PhysXCooking->setParams(Params);
        }

        if (CookedMeshBuffer.Num() == 0)
        {
            CookResult = EPhysXCookingResult::Failed;
        }

        if (CookResult != EPhysXCookingResult::Failed)
        {
            // Append the cooked data into cooked buffer
            OutBuffer.Append( CookedMeshBuffer );
        }
#endif		// WITH_PHYSX

        return CookResult;
    }