11、通过MeshDescription(FMeshDescription)构建RenderData(FStaticMeshRenderData)、通过RenderData构建UStaticMesh

文章介绍了如何在后台线程通过MeshDescription构建UStaticMesh以避免主线程阻塞。首先使用FStaticMeshLODResources的扩展方法,并指定MaterialIndex,然后使用MeshDescription构建RenderData和FStaticMeshRenderData,最后通过FStaticMeshRenderData创建UStaticMesh,确保碰撞检测的简单性。
摘要由CSDN通过智能技术生成

前言:为什么要通过这样的方式构建StaticMesh呢,需求是这样的:多线程创建海量UStaticMesh。但是UStaticMesh只能在GameThread线程创建,但是GameThread线程是主线程,如果含有大量耗时操作会导致线程阻塞,有没有一种方式在后台线程构建呢?这就是本文要解决的问题。为什么不通过MeshDescription直接构建呢?因为UStaticMesh的BuildFromMeshDescriptions不是静态方法,如果在主线程构建,还有有同样的问题,阻塞主线程。
(以下方法构建的UStaticMesh仅具有简单碰撞)

1、通过MeshDescription构建RenderData(发生在后台线程,包括MeshDescirption的构建也是在后台线程,只是这里不在给出了)
1)通过MeshDescription构建FStaticMeshLODResources

这个方法实际就是UStaticMesh的实例方法BuildFromMeshDescription,但是进行了改动,改动部分主要是增加了MaterialIndex参数,原始方法是自动计算的,但是这里进行了指定,因为自动计算需要UStaticMesh参与,但是此时还没有UStaticMesh。

void BuildFromMeshDescription(const FMeshDescription& MeshDescription, const int32 MaterialIndex, FStaticMeshLODResources& LODResources)
{
	
	FStaticMeshConstAttributes MeshDescriptionAttributes(MeshDescription);

	// Fill vertex buffers

	int32 NumVertexInstances = MeshDescription.VertexInstances().GetArraySize();
	int32 NumTriangles = MeshDescription.Triangles().Num();

	if (NumVertexInstances == 0 || NumTriangles == 0)
	{
		return;
	}

	TArray<FStaticMeshBuildVertex> StaticMeshBuildVertices;
	StaticMeshBuildVertices.SetNum(NumVertexInstances);

	TVertexAttributesConstRef<FVector3f> VertexPositions = MeshDescriptionAttributes.GetVertexPositions();
	TVertexInstanceAttributesConstRef<FVector3f> VertexInstanceNormals = MeshDescriptionAttributes.GetVertexInstanceNormals();
	TVertexInstanceAttributesConstRef<FVector3f> VertexInstanceTangents = MeshDescriptionAttributes.GetVertexInstanceTangents();
	TVertexInstanceAttributesConstRef<float> VertexInstanceBinormalSigns = MeshDescriptionAttributes.GetVertexInstanceBinormalSigns();
	TVertexInstanceAttributesConstRef<FVector4f> VertexInstanceColors = MeshDescriptionAttributes.GetVertexInstanceColors();
	TVertexInstanceAttributesConstRef<FVector2f> VertexInstanceUVs = MeshDescriptionAttributes.GetVertexInstanceUVs();

	for (FVertexInstanceID VertexInstanceID : MeshDescription.VertexInstances().GetElementIDs())
	{
		FStaticMeshBuildVertex& StaticMeshVertex = StaticMeshBuildVertices[VertexInstanceID.GetValue()];

		StaticMeshVertex.Position = VertexPositions[MeshDescription.GetVertexInstanceVertex(VertexInstanceID)];
		StaticMeshVertex.TangentX = VertexInstanceTangents[VertexInstanceID];
		StaticMeshVertex.TangentY = FVector3f::CrossProduct(VertexInstanceNormals[VertexInstanceID], VertexInstanceTangents[VertexInstanceID]).GetSafeNormal() * VertexInstanceBinormalSigns[VertexInstanceID];
		StaticMeshVertex.TangentZ = VertexInstanceNormals[VertexInstanceID];

		for (int32 UVIndex = 0; UVIndex < VertexInstanceUVs.GetNumChannels(); ++UVIndex)
		{
			StaticMeshVertex.UVs[UVIndex] = VertexInstanceUVs.Get(VertexInstanceID, UVIndex);
		}
	}

	bool bHasVertexColors = false;
	if (VertexInstanceColors.IsValid())
	{
		for (FVertexInstanceID VertexInstanceID : MeshDescription.VertexInstances().GetElementIDs())
		{
			FStaticMeshBuildVertex& StaticMeshVertex = StaticMeshBuildVertices[VertexInstanceID.GetValue()];

			FLinearColor Color(VertexInstanceColors[VertexInstanceID]);
			if (Color != FLinearColor::White)
			{
				bHasVertexColors = true;
				StaticMeshVertex.Color = Color.ToFColor(true);
			}
			else
			{
				StaticMeshVertex.Color = FColor::White;
			}
		}
	}

	LODResources.VertexBuffers.PositionVertexBuffer.Init(StaticMeshBuildVertices);

	FStaticMeshVertexBufferFlags StaticMeshVertexBufferFlags;
	StaticMeshVertexBufferFlags.bNeedsCPUAccess = true;
	StaticMeshVertexBufferFlags.bUseBackwardsCompatibleF16TruncUVs = false;
	LODResources.VertexBuffers.StaticMeshVertexBuffer.Init(StaticMeshBuildVertices, VertexInstanceUVs.GetNumChannels(), StaticMeshVertexBufferFlags);

	LODResources.bHasColorVertexData = bHasVertexColors;
	FColorVertexBuffer& ColorVertexBuffer = LODResources.VertexBuffers.ColorVertexBuffer;
	if (bHasVertexColors)
	{
		ColorVertexBuffer.Init(StaticMeshBuildVertices);
	}
	else
	{
		ColorVertexBuffer.InitFromSingleColor(FColor::White, NumVertexInstances);
	}

	// Fill index buffer and sections array

	int32 NumPolygonGroups = MeshDescription.PolygonGroups().Num();

	TPolygonGroupAttributesConstRef<FName> MaterialSlotNames = MeshDescriptionAttributes.GetPolygonGroupMaterialSlotNames();

	TArray<uint32> IndexBuffer;
	IndexBuffer.SetNumZeroed(NumTriangles * 3);

	FStaticMeshSectionArray& Sections = LODResources.Sections;

	int32 SectionIndex = 0;
	int32 IndexBufferIndex = 0;
	EIndexBufferStride::Type IndexBufferStride = EIndexBufferStride::Force16Bit;

	for (FPolygonGroupID PolygonGroupID : MeshDescription.PolygonGroups().GetElementIDs())
	{
		// Skip empty polygon groups - we do not want to build empty sections
		if (MeshDescription.GetNumPolygonGroupPolygons(PolygonGroupID) == 0)
		{
			continue;
		}

		FStaticMeshSection& Section = Sections.AddDefaulted_GetRef();
		Section.FirstIndex = IndexBufferIndex;

		int32 TriangleCount = 0;
		uint32 MinVertexIndex = TNumericLimits<uint32>::Max();
		uint32 MaxVertexIndex = TNumericLimits<uint32>::Min();

		for (FTriangleID TriangleID : MeshDescription.GetPolygonGroupTriangles(PolygonGroupID))
		{
			for (FVertexInstanceID TriangleVertexInstanceIDs : MeshDescription.GetTriangleVertexInstances(TriangleID))
			{
				uint32 VertexIndex = static_cast<uint32>(TriangleVertexInstanceIDs.GetValue());
				MinVertexIndex = FMath::Min(MinVertexIndex, VertexIndex);
				MaxVertexIndex = FMath::Max(MaxVertexIndex, VertexIndex);
				IndexBuffer[IndexBufferIndex] = VertexIndex;
				IndexBufferIndex++;
			}

			TriangleCount++;
		}

		Section.NumTriangles = TriangleCount;
		Section.MinVertexIndex = MinVertexIndex;
		Section.MaxVertexIndex = MaxVertexIndex;


		Section.MaterialIndex = MaterialIndex;
		Section.bEnableCollision = true;
		Section.bCastShadow = true;

		if (MaxVertexIndex > TNumericLimits<uint16>::Max())
		{
			IndexBufferStride = EIndexBufferStride::Force32Bit;
		}

		SectionIndex++;
	}
	check(IndexBufferIndex == NumTriangles * 3);

	LODResources.IndexBuffer.SetIndices(IndexBuffer, IndexBufferStride);

	// Fill depth only index buffer

	TArray<uint32> DepthOnlyIndexBuffer(IndexBuffer);
	for (uint32& Index : DepthOnlyIndexBuffer)
	{
		// Compress all vertex instances into the same instance for each vertex
		Index = MeshDescription.GetVertexVertexInstanceIDs(MeshDescription.GetVertexInstanceVertex(FVertexInstanceID(Index)))[0].GetValue();
	}

	LODResources.bHasDepthOnlyIndices = true;
	LODResources.DepthOnlyIndexBuffer.SetIndices(DepthOnlyIndexBuffer, IndexBufferStride);
	LODResources.DepthOnlyNumTriangles = NumTriangles;
	LODResources.bHasColorVertexData = true;
	
	// Fill reversed index buffer
	TArray<uint32> ReversedIndexBuffer(IndexBuffer);
	for (int32 ReversedIndexBufferIndex = 0; ReversedIndexBufferIndex < IndexBuffer.Num(); ReversedIndexBufferIndex += 3)
	{
		Swap(ReversedIndexBuffer[ReversedIndexBufferIndex + 0], ReversedIndexBuffer[ReversedIndexBufferIndex + 2]);
	}

	LODResources.AdditionalIndexBuffers = new FAdditionalStaticMeshIndexBuffers();
	LODResources.bHasReversedIndices = true;
	LODResources.AdditionalIndexBuffers->ReversedIndexBuffer.SetIndices(ReversedIndexBuffer, IndexBufferStride);
	
	// Fill reversed depth index buffer
	TArray<uint32> ReversedDepthOnlyIndexBuffer(DepthOnlyIndexBuffer);
	for (int32 ReversedIndexBufferIndex = 0; ReversedIndexBufferIndex < IndexBuffer.Num(); ReversedIndexBufferIndex += 3)
	{
		Swap(ReversedDepthOnlyIndexBuffer[ReversedIndexBufferIndex + 0], ReversedDepthOnlyIndexBuffer[ReversedIndexBufferIndex + 2]);
	}
	
	LODResources.bHasReversedDepthOnlyIndices = true;
	LODResources.AdditionalIndexBuffers->ReversedDepthOnlyIndexBuffer.SetIndices(ReversedIndexBuffer, IndexBufferStride);

	
}

2)通过MeshDescription集合构建FStaticMeshRenderData
实际上这个方法也是来自UStaticMesh的实例方法BuildRenderDataFromMeshDescriptions,但是进行了删减

FStaticMeshRenderData* BuildRenderDataFromMeshDescriptions(const TArray<const FMeshDescription*>& MeshDescriptions, const UStaticMesh::FBuildMeshDescriptionsParams& Params)
{

	FStaticMeshRenderData* RenderData = new FStaticMeshRenderData;
	const int32 NewNumLODs = MeshDescriptions.Num();
	RenderData->AllocateLODResources(NewNumLODs);
	FStaticMeshLODResourcesArray& LODResourcesArray = RenderData->LODResources;
	for (int32 LODIndex = 0; LODIndex < LODResourcesArray.Num(); ++LODIndex)
	{
		LODResourcesArray[LODIndex].IndexBuffer.TrySetAllowCPUAccess(Params.bAllowCpuAccess);
		if (Params.PerLODOverrides.IsValidIndex(LODIndex))
		{
			const UStaticMesh::FBuildMeshDescriptionsLODParams& LODParams = Params.PerLODOverrides[LODIndex];
			LODResourcesArray[LODIndex].VertexBuffers.StaticMeshVertexBuffer.SetUseHighPrecisionTangentBasis(LODParams.bUseHighPrecisionTangentBasis);
			LODResourcesArray[LODIndex].VertexBuffers.StaticMeshVertexBuffer.SetUseFullPrecisionUVs(LODParams.bUseFullPrecisionUVs);
		}
	}
	for (int32 LODIndex = 0; LODIndex < NewNumLODs; LODIndex++)
	{
		check(MeshDescriptions[LODIndex] != nullptr);
		FStaticMeshLODResources& LODResources = RenderData->LODResources[LODIndex];
		LODResources.bBuffersInlined = true;
		BuildFromMeshDescription(*MeshDescriptions[LODIndex], 0, LODResources);
	}
	RenderData->Bounds = MeshDescriptions[0]->GetBounds();
	for (int32 LOD = 0; LOD < NewNumLODs; ++LOD)
	{
		// @todo: some way of customizing LOD screen size and/or calculate it based on mesh bounds
		if (true)
		{
			const float LODPowerBase = 0.75f;
			RenderData->ScreenSize[LOD].Default = FMath::Pow(LODPowerBase, LOD);
		}
		else
		{
			// Possible model for flexible LODs
			const float MaxDeviation = 100.0f; // specify
			const float PixelError = UE_SMALL_NUMBER;
			const float ViewDistance = (MaxDeviation * 960.0f) / PixelError;

			// Generate a projection matrix.
			const float HalfFOV = UE_PI * 0.25f;
			const float ScreenWidth = 1920.0f;
			const float ScreenHeight = 1080.0f;
			const FPerspectiveMatrix ProjMatrix(HalfFOV, ScreenWidth, ScreenHeight, 1.0f);

			RenderData->ScreenSize[LOD].Default = ComputeBoundsScreenSize(FVector::ZeroVector, RenderData->Bounds.SphereRadius, FVector(0.0f, 0.0f, ViewDistance + RenderData->Bounds.SphereRadius), ProjMatrix);
		}
	}


	return RenderData;
}

3)通过MeshDescription构建FStaticMeshRenderData
实际上通过2)中的方法进行构建的

FStaticMeshRenderData* BuildRenderDataFromMeshDescription(const FMeshDescription& MeshDescription)
{
	TArray<const FMeshDescription*> meshDescPtrs;
	meshDescPtrs.Emplace(&MeshDescription);
	UStaticMesh::FBuildMeshDescriptionsParams mdParams;
	/*以下参数都是为了快速构建RenderData*/
	// Do not commit since we only need the render data and commit is slow
	mdParams.bCommitMeshDescription = false;
	// Do not mark the package dirty since MarkPackageDirty is not thread safe
	mdParams.bMarkPackageDirty = false;
	mdParams.bUseHashAsGuid = true;
	mdParams.bBuildSimpleCollision = true;
	mdParams.bFastBuild = true;//快速构建
	mdParams.bAllowCpuAccess = true;
	return BuildRenderDataFromMeshDescriptions(meshDescPtrs, mdParams);
}

2、通过FStaticMeshRenderData构建UStaticMesh

TObjectPtr<UStaticMesh> UMeshUtils::BuildStaticMeshFromRenderData(TObjectPtr<UObject> Outer, FStaticMeshRenderData* RenderData)
{
	// 创建 UStaticMesh 对象并设置渲染数据
	UStaticMesh* Mesh = NewObject<UStaticMesh>(Outer);
	Mesh->SetRenderData(std::move(TUniquePtr<FStaticMeshRenderData>(RenderData)));
	Mesh->InitResources();
	Mesh->CalculateExtendedBounds();
	Mesh->CreateBodySetup();
	if (UBodySetup* bodySetup = Mesh->GetBodySetup())
	{
		bodySetup->InvalidatePhysicsData();
		FKBoxElem BoxElem;
		BoxElem.Center = RenderData->Bounds.Origin;
		BoxElem.X = RenderData->Bounds.BoxExtent.X * 2.0f;
		BoxElem.Y = RenderData->Bounds.BoxExtent.Y * 2.0f;
		BoxElem.Z = RenderData->Bounds.BoxExtent.Z * 2.0f;
		bodySetup->AggGeom.BoxElems.Add(BoxElem);
		bodySetup->CreatePhysicsMeshes();
		bodySetup->CollisionTraceFlag = CTF_UseSimpleAsComplex;
	}
	return Mesh;
}
  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值