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;
	}
Example #2
0
bool GameObject::CreateTriangleMesh( CPhysX const * pPhysX )
{
	bool bResult = false;
	if( pPhysX && pPhysX->GetPhysics() && !m_BufVertices.empty() && !m_BufIndices.empty() )
	{
		uint NumVerticies = m_BufVertices.size() / 3;
		uint NumTriangles = m_BufIndices.size()  / 3;

		//Create pointer for vertices
		physx::PxVec3* verts = new physx::PxVec3[ NumVerticies ];
		int ii = -1;

		for( uint i = 0; i < NumVerticies; ++i )
		{
			++ii;
			verts[ i ].x = m_BufVertices[   ii ];
			verts[ i ].y = m_BufVertices[ ++ii ];
			verts[ i ].z = m_BufVertices[ ++ii ];
		}

		//Create pointer for indices
		physx::PxU16 *tris = new physx::PxU16[ m_BufIndices.size() ];

		for( uint i = 0; i < m_BufIndices.size(); ++i )		
			tris[ i ] = m_BufIndices[ i ];

		// Build physical model
		physx::PxTriangleMeshDesc TriMeshDesc;

		TriMeshDesc.points.count  = NumVerticies;		
		TriMeshDesc.points.stride = sizeof(physx::PxVec3);
		TriMeshDesc.points.data	  = verts;

		TriMeshDesc.triangles.count  = NumTriangles;
		TriMeshDesc.triangles.stride = 3 * sizeof(physx::PxU16);	
		TriMeshDesc.triangles.data   = tris;

		TriMeshDesc.flags = physx::PxMeshFlag::e16_BIT_INDICES;// | physx::PxMeshFlag::eFLIPNORMALS;

		PxToolkit::MemoryOutputStream writeBuffer;
		PxCooking* pCooking = pPhysX->GetCooking();

		if( pCooking && TriMeshDesc.isValid() )
			if( pCooking->cookTriangleMesh( TriMeshDesc, writeBuffer ) )
			{
				PxToolkit::MemoryInputData readBuffer( writeBuffer.getData(), writeBuffer.getSize() );
				m_pTriangleMesh = pPhysX->GetPhysics()->createTriangleMesh( readBuffer );
				bResult = true;
			}

			delete[] verts;
			delete[] tris;
	}

	return bResult;
}
    virtual bool CookTriMesh(FName Format, int32 RuntimeCookFlags, const TArray<FVector>& SrcVertices, const TArray<FTriIndices>& SrcIndices, const TArray<uint16>& SrcMaterialIndices, const bool FlipNormals, TArray<uint8>& OutBuffer, bool bDeformableMesh = false) const override
    {
#if WITH_PHYSX
        PxPlatform::Enum PhysXFormat = PxPlatform::ePC;
        bool bIsPhysXFormatValid = GetPhysXFormat(Format, PhysXFormat);
        check(bIsPhysXFormatValid);

        PxTriangleMeshDesc PTriMeshDesc;
        PTriMeshDesc.points.data = SrcVertices.GetData();
        PTriMeshDesc.points.count = SrcVertices.Num();
        PTriMeshDesc.points.stride = sizeof(FVector);
        PTriMeshDesc.triangles.data = SrcIndices.GetData();
        PTriMeshDesc.triangles.count = SrcIndices.Num();
        PTriMeshDesc.triangles.stride = sizeof(FTriIndices);
        PTriMeshDesc.materialIndices.data = SrcMaterialIndices.GetData();
        PTriMeshDesc.materialIndices.stride = sizeof(PxMaterialTableIndex);
        PTriMeshDesc.flags = FlipNormals ? PxMeshFlag::eFLIPNORMALS : (PxMeshFlags)0;

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

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

        if (bDeformableMesh)
        {
            // In the case of a deformable mesh, we have to change the cook params
            NewParams.meshPreprocessParams = PxMeshPreprocessingFlag::eDISABLE_CLEAN_MESH;
        }

        PhysXCooking->setParams(NewParams);


        // Cook TriMesh Data
        FPhysXOutputStream Buffer(&OutBuffer);
        bool Result = PhysXCooking->cookTriangleMesh(PTriMeshDesc, Buffer);

        if (bDeformableMesh)	//restore old params
        {
            NewParams.meshPreprocessParams = OldCookingFlags;
            PhysXCooking->setParams(NewParams);
        }
        return Result;
#else
        return false;
#endif		// WITH_PHYSX
    }
	virtual bool CookHeightField(FName Format, FIntPoint HFSize, float Thickness, const void* Samples, uint32 SamplesStride, TArray<uint8>& OutBuffer) const override
	{
#if WITH_PHYSX
		PxPlatform::Enum PhysXFormat = PxPlatform::ePC;
		bool bIsPhysXFormatValid = GetPhysXFormat(Format, PhysXFormat);
		check(bIsPhysXFormatValid);

		PxHeightFieldDesc HFDesc;
		HFDesc.format = PxHeightFieldFormat::eS16_TM;
		HFDesc.nbColumns = HFSize.X;
		HFDesc.nbRows = HFSize.Y;
		HFDesc.samples.data = Samples;
		HFDesc.samples.stride = SamplesStride;
		HFDesc.flags = PxHeightFieldFlag::eNO_BOUNDARY_EDGES;
		HFDesc.thickness = Thickness;

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

		// Cook to a temp buffer
		TArray<uint8> CookedBuffer;
		FPhysXOutputStream Buffer(&CookedBuffer);

		if (PhysXCooking->cookHeightField(HFDesc, Buffer) && CookedBuffer.Num() > 0)
		{
			// Append the cooked data into cooked buffer
			OutBuffer.Append(CookedBuffer);
			return true;
		}
		return false;
#else
		return false;
#endif		// WITH_PHYSX
	}
Example #5
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";
}
Example #6
0
	/**
	 * Attempts to cook a triangle or convex mesh from the provided mesh data. Will log a warning and return false if it is
	 * unable to cook the mesh. If the method returns true the resulting convex mesh will be output in the @p data buffer,
	 * and its size in @p size. The data buffer will be allocated used the generic allocator and is up to the caller to 
	 * free it.
	 */
	bool cookMesh(const SPtr<MeshData>& meshData, PhysicsMeshType type, UINT8** data, UINT32& size)
	{
		if (meshData == nullptr)
			return false;

		PxCooking* cooking = gPhysX().getCooking();
		if (cooking == nullptr)
		{
			LOGWRN("Attempting to cook a physics mesh but cooking is not enabled globally.");
			return false;
		}

		SPtr<VertexDataDesc> vertexDesc = meshData->getVertexDesc();
		if (!vertexDesc->hasElement(VES_POSITION))
		{
			LOGWRN("Provided PhysicsMesh mesh data has no vertex positions.");
			return false;
		}

		if (type == PhysicsMeshType::Convex)
		{
			if(!cookConvex(cooking, meshData, data, size))
			{
				LOGWRN("Failed cooking a convex mesh. Perpahs it is too complex? Maximum number of convex vertices is 256.");
				return false;
			}
		}
		else
		{
			PxTriangleMeshDesc meshDesc;
			meshDesc.points.count = meshData->getNumVertices();
			meshDesc.points.stride = vertexDesc->getVertexStride();
			meshDesc.points.data = meshData->getElementData(VES_POSITION);

			meshDesc.triangles.count = meshData->getNumIndices() / 3;
			meshDesc.flags |= PxMeshFlag::eFLIPNORMALS;

			IndexType indexType = meshData->getIndexType();
			if (indexType == IT_32BIT)
			{
				meshDesc.triangles.stride = 3 * sizeof(PxU32);
				meshDesc.triangles.data = meshData->getIndices32();
			}
			else
			{
				meshDesc.triangles.stride = 3 * sizeof(PxU16);
				meshDesc.triangles.data = meshData->getIndices16();
				meshDesc.flags |= PxMeshFlag::e16_BIT_INDICES;
			}

			PxDefaultMemoryOutputStream output;
			if (!cooking->cookTriangleMesh(meshDesc, output))
				return false;

			size = output.getSize();
			*data = (UINT8*)bs_alloc(size);

			memcpy(*data, output.getData(), size);
		}

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