// FRenderResource interface.
	virtual void InitRHI() override
	{
		uint32 TextureStride = sizeof(FVector2D);
		EPixelFormat TextureFormat = PF_G32R32F;

		if (Use16bitTexCoord)
		{
			TextureStride = sizeof(FVector2DHalf);
			TextureFormat = PF_G16R16F;
		}

		if (!buffersAllocated)
		{
			PositionBuffer.VertexBufferRHI = AllocVertexBuffer(sizeof(FVector), Vertices.Num());
			TangentBuffer.VertexBufferRHI = AllocVertexBuffer(sizeof(FPackedNormal), 2 * Vertices.Num());
			TexCoordBuffer.VertexBufferRHI = AllocVertexBuffer(TextureStride, NumTexCoords * Vertices.Num());
			ColorBuffer.VertexBufferRHI = AllocVertexBuffer(sizeof(FColor), Vertices.Num());

			TangentBufferSRV = RHICreateShaderResourceView(TangentBuffer.VertexBufferRHI, 4, PF_R8G8B8A8);
			TexCoordBufferSRV = RHICreateShaderResourceView(TexCoordBuffer.VertexBufferRHI, TextureStride, TextureFormat);
			ColorBufferSRV = RHICreateShaderResourceView(ColorBuffer.VertexBufferRHI, 4, PF_R8G8B8A8);
			PositionBufferSRV = RHICreateShaderResourceView(PositionBuffer.VertexBufferRHI, sizeof(float), PF_R32_FLOAT);
			buffersAllocated = true;
		}

		void* TexCoordBufferData = RHILockVertexBuffer(TexCoordBuffer.VertexBufferRHI, 0, NumTexCoords * TextureStride * Vertices.Num(), RLM_WriteOnly);
		FVector2D* TexCoordBufferData32 = !Use16bitTexCoord ? static_cast<FVector2D*>(TexCoordBufferData) : nullptr;
		FVector2DHalf* TexCoordBufferData16 = Use16bitTexCoord ? static_cast<FVector2DHalf*>(TexCoordBufferData) : nullptr;

		// Copy the vertex data into the vertex buffers.
		FVector* PositionBufferData = static_cast<FVector*>(RHILockVertexBuffer(PositionBuffer.VertexBufferRHI, 0, sizeof(FVector) * Vertices.Num(), RLM_WriteOnly));
		FPackedNormal* TangentBufferData = static_cast<FPackedNormal*>(RHILockVertexBuffer(TangentBuffer.VertexBufferRHI, 0, 2 * sizeof(FPackedNormal) * Vertices.Num(), RLM_WriteOnly));
		FColor* ColorBufferData = static_cast<FColor*>(RHILockVertexBuffer(ColorBuffer.VertexBufferRHI, 0, sizeof(FColor) * Vertices.Num(), RLM_WriteOnly));

		for (int32 i = 0; i < Vertices.Num(); i++)
		{
			PositionBufferData[i] = Vertices[i].Position;
			TangentBufferData[2 * i + 0] = Vertices[i].TangentX;
			TangentBufferData[2 * i + 1] = Vertices[i].TangentZ;
			ColorBufferData[i] = Vertices[i].Color;

			for (uint32 j = 0; j < NumTexCoords; j++)
			{
				if (Use16bitTexCoord)
				{
					TexCoordBufferData16[NumTexCoords * i + j] = FVector2DHalf(Vertices[i].TextureCoordinate[j]);
				}
				else
				{
					TexCoordBufferData32[NumTexCoords * i + j] = Vertices[i].TextureCoordinate[j];
				}
			}
		}

		RHIUnlockVertexBuffer(PositionBuffer.VertexBufferRHI);
		RHIUnlockVertexBuffer(TangentBuffer.VertexBufferRHI);
		RHIUnlockVertexBuffer(TexCoordBuffer.VertexBufferRHI);
		RHIUnlockVertexBuffer(ColorBuffer.VertexBufferRHI);
	}
/*-----------------------------------------------------------------------------
	TGPUSkinAPEXClothVertexFactory::ClothShaderType
-----------------------------------------------------------------------------*/
void FGPUBaseSkinAPEXClothVertexFactory::ClothShaderType::UpdateClothSimulData(const TArray<FVector4>& InSimulPositions, const TArray<FVector4>& InSimulNormals)
{
	uint32 NumSimulVerts = InSimulPositions.Num();

	if (GRHIFeatureLevel_DEPRECATED >= ERHIFeatureLevel::SM4)
	{
		FMath::Min(NumSimulVerts, (uint32)MAX_APEXCLOTH_VERTICES_FOR_VB);

		uint32 VectorArraySize = NumSimulVerts * sizeof(FVector4);
		uint32 PooledArraySize = ClothSimulDataBufferPool.PooledSizeForCreationArguments(VectorArraySize);
		if(!IsValidRef(ClothSimulPositionBuffer) || PooledArraySize != ClothSimulPositionBuffer.VertexBufferRHI->GetSize())
		{
			if(IsValidRef(ClothSimulPositionBuffer))
			{
				ClothSimulDataBufferPool.ReleasePooledResource(ClothSimulPositionBuffer);
			}
			ClothSimulPositionBuffer = ClothSimulDataBufferPool.CreatePooledResource(VectorArraySize);
			check(IsValidRef(ClothSimulPositionBuffer));
		}

		if(!IsValidRef(ClothSimulNormalBuffer) || PooledArraySize != ClothSimulNormalBuffer.VertexBufferRHI->GetSize())
		{
			if(IsValidRef(ClothSimulNormalBuffer))
			{
				ClothSimulDataBufferPool.ReleasePooledResource(ClothSimulNormalBuffer);
			}
			ClothSimulNormalBuffer = ClothSimulDataBufferPool.CreatePooledResource(VectorArraySize);
			check(IsValidRef(ClothSimulNormalBuffer));
		}

		if(NumSimulVerts)
		{
			float* Data = (float*)RHILockVertexBuffer(ClothSimulPositionBuffer.VertexBufferRHI, 0, VectorArraySize, RLM_WriteOnly);
			checkSlow(Data);
			FMemory::Memcpy(Data, InSimulPositions.GetData(), NumSimulVerts * sizeof(FVector4));
			RHIUnlockVertexBuffer(ClothSimulPositionBuffer.VertexBufferRHI);

			Data = (float*)RHILockVertexBuffer(ClothSimulNormalBuffer.VertexBufferRHI, 0, VectorArraySize, RLM_WriteOnly);
			checkSlow(Data);
			FMemory::Memcpy(Data, InSimulNormals.GetData(), NumSimulVerts * sizeof(FVector4));
			RHIUnlockVertexBuffer(ClothSimulNormalBuffer.VertexBufferRHI);
		}
	}
	else
	{
		UpdateClothUniformBuffer(InSimulPositions, InSimulNormals);
	}
}
/** 
* Initialize the dynamic RHI for this rendering resource 
*/
void FMorphVertexBuffer::InitDynamicRHI()
{
	// LOD of the skel mesh is used to find number of vertices in buffer
	FStaticLODModel& LodModel = SkelMeshResource->LODModels[LODIdx];

	// Create the buffer rendering resource
	uint32 Size = LodModel.NumVertices * sizeof(FMorphGPUSkinVertex);
	FRHIResourceCreateInfo CreateInfo;
	VertexBufferRHI = RHICreateVertexBuffer(Size,BUF_Dynamic,CreateInfo);

	// Lock the buffer.
	FMorphGPUSkinVertex* Buffer = (FMorphGPUSkinVertex*) RHILockVertexBuffer(VertexBufferRHI,0,Size,RLM_WriteOnly);

	// zero all deltas (NOTE: DeltaTangentZ is FPackedNormal, so we can't just FMemory::Memzero)
	for (uint32 VertIndex=0; VertIndex < LodModel.NumVertices; ++VertIndex)
	{
		Buffer[VertIndex].DeltaPosition = FVector::ZeroVector;
		Buffer[VertIndex].DeltaTangentZ = FPackedNormal::ZeroNormal;
	}

	// Unlock the buffer.
	RHIUnlockVertexBuffer(VertexBufferRHI);
	
	// hasn't been updated yet
	bHasBeenUpdated = false;
}
	void UpdateRenderData() const
	{
		// Copy the vertex data into the vertex buffer.
		void* VertexBufferData = RHILockVertexBuffer(VertexBufferRHI, 0, Vertices.Num() * sizeof(FDynamicMeshVertex), RLM_WriteOnly);
		FMemory::Memcpy(VertexBufferData, Vertices.GetData(), Vertices.Num() * sizeof(FDynamicMeshVertex));
		RHIUnlockVertexBuffer(VertexBufferRHI);
	}
void FFinalSkinVertexBuffer::InitVertexData(FStaticLODModel& LodModel)
{
	// Create the buffer rendering resource
	uint32 Size = LodModel.NumVertices * sizeof(FFinalSkinVertex);

	FRHIResourceCreateInfo CreateInfo;
	void* Buffer = nullptr;
	VertexBufferRHI = RHICreateAndLockVertexBuffer(Size,BUF_Dynamic, CreateInfo, Buffer);	

	// Initialize the vertex data
	// All chunks are combined into one (rigid first, soft next)
	check(LodModel.VertexBufferGPUSkin.GetNumVertices() == LodModel.NumVertices);

	FFinalSkinVertex* DestVertex = (FFinalSkinVertex*)Buffer;
	for( uint32 VertexIdx=0; VertexIdx < LodModel.NumVertices; VertexIdx++ )
	{
		const TGPUSkinVertexBase<bExtraBoneInfluencesT>* SrcVertex = LodModel.VertexBufferGPUSkin.GetVertexPtr<bExtraBoneInfluencesT>(VertexIdx);

		DestVertex->Position = LodModel.VertexBufferGPUSkin.GetVertexPositionFast<bExtraBoneInfluencesT>(VertexIdx);
		DestVertex->TangentX = SrcVertex->TangentX;
		// w component of TangentZ should already have sign of the tangent basis determinant
		DestVertex->TangentZ = SrcVertex->TangentZ;

		FVector2D UVs = LodModel.VertexBufferGPUSkin.GetVertexUVFast<bExtraBoneInfluencesT>(VertexIdx,0);
		DestVertex->U = UVs.X;
		DestVertex->V = UVs.Y;

		DestVertex++;
	}

	// Unlock the buffer.
	RHIUnlockVertexBuffer(VertexBufferRHI);
}
	virtual void InitRHI() override
	{
		FRHIResourceCreateInfo CreateInfo;
		VertexBufferRHI = RHICreateVertexBuffer(Vertices.Num() * sizeof(FDynamicMeshVertex), BUF_Static, CreateInfo);
		// Copy the vertex data into the vertex buffer.
		void* VertexBufferData = RHILockVertexBuffer(VertexBufferRHI, 0, Vertices.Num() * sizeof(FDynamicMeshVertex), RLM_WriteOnly);
		FMemory::Memcpy(VertexBufferData, Vertices.GetData(), Vertices.Num() * sizeof(FDynamicMeshVertex));
		RHIUnlockVertexBuffer(VertexBufferRHI);
	}
	virtual void InitRHI() override
	{
		FRHIResourceCreateInfo CreateInfo;
		VertexBufferRHI = RHICreateVertexBuffer(sizeof(FVector4) * 2, BUF_Static, CreateInfo);
		FVector4* DummyContents = (FVector4*)RHILockVertexBuffer(VertexBufferRHI,0,sizeof(FVector4)*2,RLM_WriteOnly);
		DummyContents[0] = FVector4(0.0f, 0.0f, 0.0f, 0.0f);
		DummyContents[1] = FVector4(1.0f, 1.0f, 1.0f, 1.0f);
		RHIUnlockVertexBuffer(VertexBufferRHI);
	}
	virtual void InitRHI()
	{
		VertexBufferRHI = RHICreateVertexBuffer(Vertices.Num() * sizeof(FDynamicMeshVertex), NULL, BUF_Static);

		// Copy the vertex data into the vertex buffer.
		void* VertexBufferData = RHILockVertexBuffer(VertexBufferRHI, 0, Vertices.Num() * sizeof(FDynamicMeshVertex), RLM_WriteOnly);
		FMemory::Memcpy(VertexBufferData, Vertices.GetTypedData(), Vertices.Num() * sizeof(FDynamicMeshVertex));
		RHIUnlockVertexBuffer(VertexBufferRHI);
	}
    virtual void InitRHI() override
    {
        FRHIResourceCreateInfo CreateInfo;
        void* VertexBufferData = nullptr;
        VertexBufferRHI = RHICreateAndLockVertexBuffer(Vertices.Num() * sizeof(FDynamicMeshVertex), BUF_Static, CreateInfo, VertexBufferData);

        // Copy the vertex data into the vertex buffer.
        FMemory::Memcpy(VertexBufferData, Vertices.GetData(), Vertices.Num() * sizeof(FDynamicMeshVertex));
        RHIUnlockVertexBuffer(VertexBufferRHI);
    }
	/**
	 * Initialize the RHI for this rendering resource
	 */
	virtual void InitRHI() override
	{
		// create a static vertex buffer
		FRHIResourceCreateInfo CreateInfo;
		void* BufferData = nullptr;
		VertexBufferRHI = RHICreateAndLockVertexBuffer(sizeof(FVector2D) * 4, BUF_Static | BUF_ShaderResource, CreateInfo, BufferData);
		FMemory::Memzero(BufferData, sizeof(FVector2D) * 4);
		RHIUnlockVertexBuffer(VertexBufferRHI);
		
		VertexBufferSRV = RHICreateShaderResourceView(VertexBufferRHI, sizeof(FVector2D), PF_G32R32F);
	}
/** 
* Initialize the RHI for this rendering resource 
*/
void FLandscapeVertexBufferMobile::InitRHI()
{
	// create a static vertex buffer
	FRHIResourceCreateInfo CreateInfo;
	void* VertexData = nullptr;
	VertexBufferRHI = RHICreateAndLockVertexBuffer(DataSize, BUF_Static, CreateInfo, VertexData);
	
	// Copy stored platform data
	FMemory::Memcpy(VertexData, (uint8*)Data, DataSize);
	RHIUnlockVertexBuffer(VertexBufferRHI);
}
	virtual void InitRHI() override
	{
#if ENGINE_MAJOR_VERSION >= 4 && ENGINE_MINOR_VERSION >= 3
		FRHIResourceCreateInfo CreateInfo;
		VertexBufferRHI = RHICreateVertexBuffer(Vertices.Num() * sizeof(FDynamicMeshVertex), BUF_Static, CreateInfo);
#else
		VertexBufferRHI = RHICreateVertexBuffer(Vertices.Num() * sizeof(FDynamicMeshVertex), NULL, BUF_Static);
#endif
		// Copy the vertex data into the vertex buffer.
		void* VertexBufferData = RHILockVertexBuffer(VertexBufferRHI, 0, Vertices.Num() * sizeof(FDynamicMeshVertex), RLM_WriteOnly);
		FMemory::Memcpy(VertexBufferData, Vertices.GetData(), Vertices.Num() * sizeof(FDynamicMeshVertex));
		RHIUnlockVertexBuffer(VertexBufferRHI);
	}
/**
 * Creates a vertex buffer holding texture coordinates for the four corners of a sprite.
 */
void FParticleTexCoordVertexBuffer::InitRHI()
{
	const uint32 Size = sizeof(FVector2D) * 4 * MAX_PARTICLES_PER_INSTANCE;
	VertexBufferRHI = RHICreateVertexBuffer( Size, NULL, BUF_Static );
	FVector2D* Vertices = (FVector2D*)RHILockVertexBuffer( VertexBufferRHI, 0, Size, RLM_WriteOnly );
	for (uint32 SpriteIndex = 0; SpriteIndex < MAX_PARTICLES_PER_INSTANCE; ++SpriteIndex)
	{
		Vertices[SpriteIndex*4 + 0] = FVector2D(0.0f, 0.0f);
		Vertices[SpriteIndex*4 + 1] = FVector2D(0.0f, 1.0f);
		Vertices[SpriteIndex*4 + 2] = FVector2D(1.0f, 1.0f);
		Vertices[SpriteIndex*4 + 3] = FVector2D(1.0f, 0.0f);
	}
	RHIUnlockVertexBuffer( VertexBufferRHI );
}
示例#14
0
void FStreetMapVertexBuffer::InitRHI()
{
	if( Vertices.Num() > 0 )
	{
		// Allocate our vertex buffer
		FRHIResourceCreateInfo CreateInfo;
		VertexBufferRHI = RHICreateVertexBuffer( Vertices.Num() * sizeof( Vertices[0] ), BUF_Static, CreateInfo );
		
		// Load the vertex buffer with data
		void* VertexBufferData = RHILockVertexBuffer( VertexBufferRHI, 0, Vertices.Num() * sizeof( Vertices[0] ), RLM_WriteOnly );
		FMemory::Memcpy( VertexBufferData, Vertices.GetData(), Vertices.Num() * sizeof( FStreetMapVertex ) );
		RHIUnlockVertexBuffer( VertexBufferRHI );
	}
}
/**
 * Creates a vertex buffer holding texture coordinates for the four corners of a sprite.
 */
void FParticleTexCoordVertexBuffer::InitRHI()
{
    const uint32 Size = sizeof(FVector2D) * 4 * MAX_PARTICLES_PER_INSTANCE;
    FRHIResourceCreateInfo CreateInfo;
    void* BufferData = nullptr;
    VertexBufferRHI = RHICreateAndLockVertexBuffer(Size, BUF_Static, CreateInfo, BufferData);
    FVector2D* Vertices = (FVector2D*)BufferData;
    for (uint32 SpriteIndex = 0; SpriteIndex < MAX_PARTICLES_PER_INSTANCE; ++SpriteIndex)
    {
        Vertices[SpriteIndex*4 + 0] = FVector2D(0.0f, 0.0f);
        Vertices[SpriteIndex*4 + 1] = FVector2D(0.0f, 1.0f);
        Vertices[SpriteIndex*4 + 2] = FVector2D(1.0f, 1.0f);
        Vertices[SpriteIndex*4 + 3] = FVector2D(1.0f, 0.0f);
    }
    RHIUnlockVertexBuffer( VertexBufferRHI );
}
示例#16
0
	virtual void InitRHI()
	{
		FRHIResourceCreateInfo CreateInfo;
		VertexBufferRHI = RHICreateVertexBuffer(12 * sizeof(FPackedNormal), BUF_Dynamic, CreateInfo);
		// Copy the vertex data into the vertex buffer.
		FPackedNormal* TangentBufferData = (FPackedNormal*)RHILockVertexBuffer(VertexBufferRHI, 0, 12 * sizeof(FPackedNormal), RLM_WriteOnly);
		for(int32 FaceIndex = 0;FaceIndex < 6;++FaceIndex)
		{
			const FVector UnprojectedTangentX = FVector(+1,-1,0).GetSafeNormal();
			const FVector UnprojectedTangentY(-1,-1,-1);
			const FVector FaceNormal = FaceNormals[FaceIndex].ToFloat();
			const FVector ProjectedFaceTangentX = (UnprojectedTangentX - FaceNormal * (UnprojectedTangentX | FaceNormal)).GetSafeNormal();
			*TangentBufferData++ = ProjectedFaceTangentX;
			*TangentBufferData++ = FVector4(FaceNormal, FMath::Sign(UnprojectedTangentY | (FaceNormal ^ ProjectedFaceTangentX)));
		}
		RHIUnlockVertexBuffer(VertexBufferRHI);
	}
void FGPUBaseSkinVertexFactory::ShaderDataType::UpdateBoneData()
{
	uint32 NumBones = BoneMatrices.Num();
	check(NumBones <= MaxGPUSkinBones);
	if (GRHIFeatureLevel_DEPRECATED >= ERHIFeatureLevel::SM4)
	{
		static FSharedPoolPolicyData PoolPolicy;
		uint32 NumVectors = NumBones*3;
		check(NumVectors <= (MaxGPUSkinBones*3));
		uint32 VectorArraySize = NumVectors * sizeof(FVector4);
		uint32 PooledArraySize = BoneBufferPool.PooledSizeForCreationArguments(VectorArraySize);
		if(!IsValidRef(BoneBuffer) || PooledArraySize != BoneBuffer.VertexBufferRHI->GetSize())
		{
			if(IsValidRef(BoneBuffer))
			{
				BoneBufferPool.ReleasePooledResource(BoneBuffer);
			}
			BoneBuffer = BoneBufferPool.CreatePooledResource(VectorArraySize);
			check(IsValidRef(BoneBuffer));
		}
		if(NumBones)
		{
#if PLATFORM_SUPPORTS_RHI_THREAD
			check(VectorArraySize == NumBones * sizeof(BoneMatrices[0]));
			if (GRHIThread)
			{
				GRHICommandList.GetImmediateCommandList().UpdateVertexBuffer(BoneBuffer.VertexBufferRHI, BoneMatrices.GetData(), NumBones * sizeof(BoneMatrices[0]));
			}
			else
#endif
			{
				float* Data = (float*)RHILockVertexBuffer(BoneBuffer.VertexBufferRHI, 0, VectorArraySize, RLM_WriteOnly);
				checkSlow(Data);
				FMemory::Memcpy(Data, BoneMatrices.GetData(), NumBones * sizeof(BoneMatrices[0]));
				RHIUnlockVertexBuffer(BoneBuffer.VertexBufferRHI);
			}
		}
	}
	else
	{
		check(NumBones * BoneMatrices.GetTypeSize() <= sizeof(GBoneUniformStruct));
		FMemory::Memcpy(&GBoneUniformStruct, BoneMatrices.GetData(), NumBones * sizeof(BoneMatrices[0]));
		UniformBuffer = RHICreateUniformBuffer(&GBoneUniformStruct, FBoneMatricesUniformShaderParameters::StaticStruct.GetLayout(), UniformBuffer_MultiFrame);
	}
}
示例#18
0
	/** 
	* Initialize the RHI for this rendering resource 
	*/
	virtual void InitRHI() override
	{
		// used with a tristrip, so only 4 vertices are needed
		uint32 Size = 4 * sizeof(FMaterialTileVertex);
		// create vertex buffer
		FRHIResourceCreateInfo CreateInfo;
		VertexBufferRHI = RHICreateVertexBuffer(Size,BUF_Static,CreateInfo);
		// lock it
		void* Buffer = RHILockVertexBuffer(VertexBufferRHI,0,Size,RLM_WriteOnly);
		// first vertex element
		FMaterialTileVertex* DestVertex = (FMaterialTileVertex*)Buffer;

		// fill out the verts
		DestVertex[0].Initialize(1, -1, 1, 1);
		DestVertex[1].Initialize(1, 1, 1, 0);
		DestVertex[2].Initialize(-1, -1, 0, 1);
		DestVertex[3].Initialize(-1, 1, 0, 0);

		// Unlock the buffer.
		RHIUnlockVertexBuffer(VertexBufferRHI);        
	}
/**
 * Creates a vertex buffer holding texture coordinates for eight corners of a polygon.
 */
void FParticleEightTexCoordVertexBuffer::InitRHI()
{
    const uint32 Size = sizeof(FVector2D) * 8 * MAX_PARTICLES_PER_INSTANCE;
    FRHIResourceCreateInfo CreateInfo;
    void* BufferData = nullptr;
    VertexBufferRHI = RHICreateAndLockVertexBuffer(Size, BUF_Static, CreateInfo, BufferData);
    FVector2D* Vertices = (FVector2D*)BufferData;
    for (uint32 SpriteIndex = 0; SpriteIndex < MAX_PARTICLES_PER_INSTANCE; ++SpriteIndex)
    {
        // The contents of this buffer does not matter, whenever it is used, cutout geometry will override
        Vertices[SpriteIndex*8 + 0] = FVector2D(0.0f, 0.0f);
        Vertices[SpriteIndex*8 + 1] = FVector2D(0.0f, 1.0f);
        Vertices[SpriteIndex*8 + 2] = FVector2D(1.0f, 1.0f);
        Vertices[SpriteIndex*8 + 3] = FVector2D(1.0f, 0.0f);
        Vertices[SpriteIndex*8 + 4] = FVector2D(1.0f, 0.0f);
        Vertices[SpriteIndex*8 + 5] = FVector2D(1.0f, 0.0f);
        Vertices[SpriteIndex*8 + 6] = FVector2D(1.0f, 0.0f);
        Vertices[SpriteIndex*8 + 7] = FVector2D(1.0f, 0.0f);
    }
    RHIUnlockVertexBuffer( VertexBufferRHI );
}
void FFinalSkinVertexBuffer::InitVertexData(FStaticLODModel& LodModel)
{
	// this used to be check, but during clothing importing (when replacing Apex asset)
	// it comes here with incomplete data causing crash during that intermediate state
	// so I'm changing to ensure, and update won't do anything since it contains Invalid VertexBufferRHI
	if (ensure(LodModel.VertexBufferGPUSkin.GetNumVertices() == LodModel.NumVertices))
	{
		// Create the buffer rendering resource
		uint32 Size = LodModel.NumVertices * sizeof(FFinalSkinVertex);

		// Initialize the vertex data
		// All chunks are combined into one (rigid first, soft next)
		FRHIResourceCreateInfo CreateInfo;
		void* Buffer = nullptr;
		VertexBufferRHI = RHICreateAndLockVertexBuffer(Size, BUF_Dynamic, CreateInfo, Buffer);

		FFinalSkinVertex* DestVertex = (FFinalSkinVertex*)Buffer;
		for (uint32 VertexIdx = 0; VertexIdx < LodModel.NumVertices; VertexIdx++)
		{
			const TGPUSkinVertexBase<bExtraBoneInfluencesT>* SrcVertex = LodModel.VertexBufferGPUSkin.GetVertexPtr<bExtraBoneInfluencesT>(VertexIdx);

			DestVertex->Position = LodModel.VertexBufferGPUSkin.GetVertexPositionFast<bExtraBoneInfluencesT>(VertexIdx);
			DestVertex->TangentX = SrcVertex->TangentX;
			// w component of TangentZ should already have sign of the tangent basis determinant
			DestVertex->TangentZ = SrcVertex->TangentZ;

			FVector2D UVs = LodModel.VertexBufferGPUSkin.GetVertexUVFast<bExtraBoneInfluencesT>(VertexIdx, 0);
			DestVertex->U = UVs.X;
			DestVertex->V = UVs.Y;

			DestVertex++;
		}

		// Unlock the buffer.
		RHIUnlockVertexBuffer(VertexBufferRHI);
	}
}
	void AddElements(TArray<FDynamicMeshVertex> &Elements)
	{
		FDynamicMeshVertex* VertexBufferData = (FDynamicMeshVertex*)RHILockVertexBuffer(VertexBufferRHI, 0, BOSize * sizeof(FDynamicMeshVertex), RLM_WriteOnly);
		FMemory::Memcpy(VertexBufferData, &Elements[0], Elements.Num() * sizeof(FDynamicMeshVertex));
		RHIUnlockVertexBuffer(VertexBufferRHI);
	}