2337 lines
		
	
	
		
			80 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			2337 lines
		
	
	
		
			80 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
---
 | 
						||
title: Untitled
 | 
						||
date: 2025-06-03 10:19:25
 | 
						||
excerpt: 
 | 
						||
tags: 
 | 
						||
rating: ⭐
 | 
						||
---
 | 
						||
# 前言
 | 
						||
1. ShaderWorldPCGInterop:
 | 
						||
2. ShaderWorld:
 | 
						||
3. ShaderWorldCore:
 | 
						||
 | 
						||
# ShaderWorld
 | 
						||
- Class 
 | 
						||
	- Actor
 | 
						||
		- ShaderWorldActor.h:[[#AShaderWorldActor]]
 | 
						||
		- SWorld.h:[[#ASWorld]]
 | 
						||
 | 
						||
# USWorldSubsystem
 | 
						||
主要管理:
 | 
						||
- TArray<USWContextBase*> SW_Contexts 
 | 
						||
- 渲染相关变量:RT_Ready、RenderThreadPoked、RenderThreadResponded处理。
 | 
						||
# ASWorld
 | 
						||
 | 
						||
# AShaderWorldActor
 | 
						||
## 大致启动流程
 | 
						||
大致启动流程:
 | 
						||
- BeginPlay()
 | 
						||
	1. 设置SWCamera Location。
 | 
						||
	2. 清空所有数据。
 | 
						||
	3. InitiateWorld()
 | 
						||
		1. 更新相关变量:GenerateCollision_last、VerticalRangeMeters_last、WorldHasBounds_OnRebuild。**Segmented_Initialized** 、**bExportPhysicalMaterialID_cached**
 | 
						||
		2. 设置高度生成用材质变量 Generator;bool bHadGeneratorAtRebuildTime = IsValid(Generator);
 | 
						||
		3. LOD_Num与WorldDimensionMeters计算。
 | 
						||
		4. 重新生成若干数组:
 | 
						||
			1. LODs_DimensionsMeters
 | 
						||
			2. ClipMapToUpdateAndMove
 | 
						||
			3. ClipMapToUpdate
 | 
						||
			4. NeedSegmentedUpdate
 | 
						||
			5. AdaptiveTopology
 | 
						||
		5. 相关线程安全变量:
 | 
						||
			1. Shareable_ID_FarLOD
 | 
						||
			2. UpdateHOverTime
 | 
						||
		6. ComputeOptimalVirtualCacheSize()
 | 
						||
		7. for (int32 i = (WorldHasBounds_OnRebuild ? -1 : 0); i < LOD_Num; i++)
 | 
						||
		8. Toggle Async creation of Clipmap Meshes
 | 
						||
		9. Async TaskGraph ClipMapMeshCreation
 | 
						||
- Tick()
 | 
						||
	- 初始化
 | 
						||
		1. 取得USWorldSubsystem指针,并且进行初始化。
 | 
						||
	- ReadbacksManagement()
 | 
						||
	- CollisionManagement()
 | 
						||
	- SpawnablesManagement()
 | 
						||
	- TerrainAndSpawnablesManagement()
 | 
						||
 | 
						||
### ReadbacksManagement()
 | 
						||
主要用于回读RetrieveHeightAt()调用后GPU计算出来的Position信息。在**用于计算HeightReadBack的渲染线程、RHI、GPU都执行完成**后,从ReadBackHeightData的像素点(最多25个)中读取Z轴信息,之后和PointsPendingReadBacks中的X、Y轴信息合并成一个Vector3 Position。
 | 
						||
 | 
						||
- ThreadSafe
 | 
						||
	 - bool
 | 
						||
		 - **bProcessingHeightRetrieval**
 | 
						||
		 - **bProcessingHeightRetrievalRT**
 | 
						||
	 - FSWShareableSamplePoints
 | 
						||
		 - PointsPendingReadBacks
 | 
						||
	 - FSWColorRead
 | 
						||
		 - ReadBackHeightData
 | 
						||
 - FRenderCommandFence
 | 
						||
	 - HeightReadBackFence
 | 
						||
 | 
						||
```c++
 | 
						||
void AShaderWorldActor::ReadbacksManagement()
 | 
						||
{
 | 
						||
	if (!bProcessingHeightRetrieval.IsValid())
 | 
						||
		bProcessingHeightRetrieval = MakeShared<FThreadSafeBool, ESPMode::ThreadSafe>();
 | 
						||
	if (!bProcessingHeightRetrievalRT.IsValid())
 | 
						||
		bProcessingHeightRetrievalRT = MakeShared<FThreadSafeBool, ESPMode::ThreadSafe>();
 | 
						||
	
 | 
						||
	//PointsPendingReadBacks存储从GPU回读的采样坐标位置;在RetrieveHeightAt()调用HeightReadBackFence.BeginFence(true)开启渲染栅栏,不开启这段逻辑不会执行。
 | 
						||
	if ((*bProcessingHeightRetrieval.Get()) && (*bProcessingHeightRetrievalRT.Get()) && HeightReadBackFence.IsFenceComplete() && PointsPendingReadBacks.IsValid())
 | 
						||
	{
 | 
						||
		// ReadBackHeightData uin8 贴图数据
 | 
						||
		if (!ReadBackHeightData.IsValid())
 | 
						||
			return;
 | 
						||
		
 | 
						||
		//For now we do Compute samples on a rendertarget 5x5, therefore 25 positions evaluated per request.
 | 
						||
		//每次请求可以读取25个点。
 | 
						||
		const int NumOfVertex = 25;
 | 
						||
 | 
						||
		PositionsOfReadBacks.Empty();
 | 
						||
		PositionsOfReadBacks.AddUninitialized(25);
 | 
						||
 | 
						||
		uint8* ReadData8 = (uint8*)ReadBackHeightData->ReadData.GetData();
 | 
						||
			uint16 MaterialIndice = 0;
 | 
						||
		for (int32 k = 0; k < NumOfVertex; k++)
 | 
						||
		{
 | 
						||
			FVector3f& PositionSample = PositionsOfReadBacks[k];
 | 
						||
 | 
						||
			if (RendererAPI == EGeoRenderingAPI::OpenGL)
 | 
						||
			{
 | 
						||
				int X = k % 5;
 | 
						||
				int Y = k / 5;
 | 
						||
 | 
						||
				int index = X + Y * 5;
 | 
						||
 | 
						||
				Y = (5 - 1) - Y;
 | 
						||
 | 
						||
				index = X + Y * 5;
 | 
						||
 | 
						||
				PositionSample = FVector3f(PointsPendingReadBacks->PositionsXY[2 * k], PointsPendingReadBacks->PositionsXY[2 * k + 1], GetHeightFromGPURead(&ReadData8[index * 4], MaterialIndice) / HeightScale);
 | 
						||
			}
 | 
						||
			else
 | 
						||
				PositionSample = FVector3f(PointsPendingReadBacks->PositionsXY[2 * k], PointsPendingReadBacks->PositionsXY[2 * k + 1], GetHeightFromGPURead(&ReadData8[k * 4], MaterialIndice) / HeightScale);
 | 
						||
 | 
						||
		}
 | 
						||
 | 
						||
		if(HeightRetrieveDelegate.ExecuteIfBound(PositionsOfReadBacks))
 | 
						||
		{
 | 
						||
			//SW_LOG("HeightRetrieveDelegate.ExecuteIfBound(PositionsOfReadBacks) PositionsOfReadBacks[0] %s",*PositionsOfReadBacks[0].ToString())
 | 
						||
		}
 | 
						||
		else
 | 
						||
		{
 | 
						||
			//SW_LOG("Fail HeightRetrieveDelegate.ExecuteIfBound(PositionsOfReadBacks)")
 | 
						||
		}
 | 
						||
 | 
						||
		bProcessingHeightRetrieval->AtomicSet(false);
 | 
						||
		bProcessingHeightRetrievalRT->AtomicSet(false);
 | 
						||
	}
 | 
						||
 | 
						||
	//For now MinMax is here
 | 
						||
	//处理FClipMapMeshElement的最大最小队列?
 | 
						||
	if (UseSegmented())
 | 
						||
	{
 | 
						||
		if(WorldHasBounds_OnRebuild)
 | 
						||
		{
 | 
						||
			FClipMapMeshElement& MeshEl = FarLOD_BoundedWorld;
 | 
						||
 | 
						||
			if (MeshEl.MinMaxQueue.Num() > 0)
 | 
						||
			{
 | 
						||
				MeshEl.ProcessMinMaxQueue(SWorldSubsystem);
 | 
						||
			}
 | 
						||
		}
 | 
						||
		
 | 
						||
		for (FClipMapMeshElement& MeshEl : Meshes)
 | 
						||
		{
 | 
						||
 | 
						||
			if (MeshEl.MinMaxQueue.Num() > 0)
 | 
						||
			{
 | 
						||
				MeshEl.ProcessMinMaxQueue(SWorldSubsystem);
 | 
						||
			}
 | 
						||
		}
 | 
						||
	}
 | 
						||
}
 | 
						||
```
 | 
						||
 | 
						||
### CollisionManagement()
 | 
						||
- ThreadSafe
 | 
						||
	 - bool
 | 
						||
		 - ***bProcessingGroundCollision***:处理地面碰撞。
 | 
						||
		 - bPreprocessingCollisionUpdate
 | 
						||
		 - EditRebuild:传递Bool值给 rebuild。
 | 
						||
	 - FSWCollisionManagementShareableData
 | 
						||
		 - CollisionShareable
 | 
						||
 - FRenderCommandFence
 | 
						||
	- 1
 | 
						||
- bool 
 | 
						||
	- RedbuildCollisionContext
 | 
						||
- Array
 | 
						||
	- CollisionReadToProcess:
 | 
						||
	- CollisionWorkQueue:类型为FCollisionProcessingWork,碰撞处理任务队列,将回读的
 | 
						||
- FCollisionMeshElement
 | 
						||
	- ReadBackCompletion:碰撞数据GPU回读是否完成。
 | 
						||
 | 
						||
 | 
						||
相关条件
 | 
						||
if ((*bPreprocessingCollisionUpdate.Get()) ||  
 | 
						||
    CollisionShareable->CollisionMeshToUpdate.Num() > 0 ||  
 | 
						||
    CollisionShareable->CollisionMeshToRenameMoveUpdate.Num() > 0 ||  
 | 
						||
    CollisionShareable->CollisionMeshToCreate.Num() > 0)  
 | 
						||
    return;
 | 
						||
 | 
						||
	if (CollisionUpdateTimeAcu <= 1.f / 10.f || CollisionWorkQueue.Num() > 0 || !CollisionProcess.IsFenceComplete())
 | 
						||
 | 
						||
 | 
						||
```c++
 | 
						||
void AShaderWorldActor::CollisionManagement(float& DeltaT)
 | 
						||
{
 | 
						||
	SCOPED_NAMED_EVENT_TEXT("AShaderWorldActor::CollisionManagement", FColor::Magenta);
 | 
						||
	SW_FCT_CYCLE()
 | 
						||
 | 
						||
	/*
 | 
						||
	 * Can we execute compute shader?
 | 
						||
	 * Do we need to rebuild collision?
 | 
						||
	 * Did ShaderWorld toggled collision generation On/Off?
 | 
						||
	 * Are we pending a full rebuild of the Shader World?
 | 
						||
	 */
 | 
						||
	if (!SetupCollisions())
 | 
						||
		return;
 | 
						||
 | 
						||
	/*
 | 
						||
	 * Using collision updates, update Collision meshes
 | 
						||
	 */
 | 
						||
	if (!CollisionFinalizeWork())
 | 
						||
		return;
 | 
						||
 | 
						||
	if (CollisionProcess.IsFenceComplete())
 | 
						||
	{
 | 
						||
		/*
 | 
						||
		 * Convert Compute shader results to actionable collision updates
 | 
						||
		 */
 | 
						||
		if (!CollisionPreprocessGPU())
 | 
						||
		{
 | 
						||
			CollisionProcess.BeginFence(true);
 | 
						||
			return;
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	/*
 | 
						||
	 * Process GPU work queue by launching GPU tasks to evaluate the collision of new tiles
 | 
						||
	 */
 | 
						||
	CollisionGPU();
 | 
						||
 | 
						||
	// Timer
 | 
						||
	// 碰撞生成与Update计时
 | 
						||
	{
 | 
						||
		CollisionUpdateTimeAcu += DeltaT;
 | 
						||
 | 
						||
		//CollisionUpdateTimeAcu如果超过0.1s,或者CollisionWorkQueue的任务都完成了。CollisionProcess已经完成。
 | 
						||
		if (CollisionUpdateTimeAcu <= 1.f / 10.f || CollisionWorkQueue.Num() > 0 || !CollisionProcess.IsFenceComplete())
 | 
						||
			return;
 | 
						||
 | 
						||
		CollisionUpdateTimeAcu = 0.f;
 | 
						||
	}
 | 
						||
 | 
						||
	/*
 | 
						||
	 * Gather relevant collision tiles
 | 
						||
	 * If an old tile is not relevant anymore, release it.
 | 
						||
	 * If a new location needs to be computed: allocate a tile and add its relevant computation work to the GPU work queue
 | 
						||
	 */
 | 
						||
	CollisionCPU();
 | 
						||
}
 | 
						||
```
 | 
						||
 | 
						||
#### SetupCollisions()
 | 
						||
设置相关变量。
 | 
						||
```c++
 | 
						||
bool AShaderWorldActor::SetupCollisions()
 | 
						||
{
 | 
						||
	SW_FCT_CYCLE()
 | 
						||
 | 
						||
	if (!bHadGeneratorAtRebuildTime)
 | 
						||
		return false;
 | 
						||
 | 
						||
	if (!Shareable_ID.IsValid() || Meshes.Num() <= 0)
 | 
						||
		return false;
 | 
						||
 | 
						||
	//初始化线程安全变量
 | 
						||
	if (!bProcessingGroundCollision.IsValid())
 | 
						||
		bProcessingGroundCollision = MakeShared<FThreadSafeBool, ESPMode::ThreadSafe>();
 | 
						||
	if (!bPreprocessingCollisionUpdate.IsValid())
 | 
						||
		bPreprocessingCollisionUpdate = MakeShared<FThreadSafeBool, ESPMode::ThreadSafe>();
 | 
						||
 | 
						||
	if (EditRebuild)
 | 
						||
	{
 | 
						||
		EditRebuild.AtomicSet(false);
 | 
						||
		rebuild = true;
 | 
						||
	}
 | 
						||
 | 
						||
	if (rebuild)
 | 
						||
		RedbuildCollisionContext = true;
 | 
						||
	//重建相关变量初始化
 | 
						||
	if (RedbuildCollisionContext)
 | 
						||
	{
 | 
						||
		if (!(*bProcessingGroundCollision.Get()) && !(*bPreprocessingCollisionUpdate.Get())
 | 
						||
			&& CollisionProcess.IsFenceComplete()
 | 
						||
			&& (CollisionMesh.Num() <= 0)
 | 
						||
			&& (UsedCollisionMesh.Num() <= 0))
 | 
						||
		{
 | 
						||
 | 
						||
			CollisionShareable = nullptr;
 | 
						||
 | 
						||
			CollisionWorkQueue.Empty();
 | 
						||
			CollisionReadToProcess.Empty();
 | 
						||
 | 
						||
			RedbuildCollisionContext = false;
 | 
						||
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	if (RedbuildCollisionContext)
 | 
						||
		return false;
 | 
						||
 | 
						||
	if (!GenerateCollision)
 | 
						||
		return false;
 | 
						||
 | 
						||
	//创建全线程碰撞生成相关数据结构体CollisionShareable
 | 
						||
	if (!CollisionShareable.IsValid())
 | 
						||
		CollisionShareable = MakeShared<FSWCollisionManagementShareableData, ESPMode::ThreadSafe>(CollisionResolution, CollisionVerticesPerPatch);
 | 
						||
 | 
						||
	if ((*bProcessingGroundCollision.Get()) || (*bPreprocessingCollisionUpdate.Get()) || !CameraSet || (Meshes.Num() == 0))
 | 
						||
		return false;
 | 
						||
 | 
						||
	//Let the data layer be computed before generating collisions and trying to extract material IDs
 | 
						||
	if (bExportPhysicalMaterialID && (WorldCycle < 2))
 | 
						||
		return false;
 | 
						||
 | 
						||
 | 
						||
	if (CollisionVisibleChanged)
 | 
						||
	{
 | 
						||
		if (GetWorld())
 | 
						||
		{
 | 
						||
			for (auto& ColM : CollisionMesh)
 | 
						||
			{
 | 
						||
				if (auto& Mesh = ColM.Value.Mesh)
 | 
						||
				{
 | 
						||
					Mesh->SetMeshSectionVisible(0, CollisionVisible);
 | 
						||
				}
 | 
						||
			}
 | 
						||
		}
 | 
						||
		CollisionVisibleChanged.AtomicSet(false);
 | 
						||
	}
 | 
						||
 | 
						||
	return true;
 | 
						||
}
 | 
						||
```
 | 
						||
 | 
						||
#### CollisionFinalizeWork() 笔记为3.8.6版本非最新
 | 
						||
结束碰撞生成任务,true为完成,false为任务超时未完成。**主要的逻辑是将之前的数据设置为脏,并且更新FShaderWProceduralMeshSceneProxy,5.4 Finaly版本里这里已经移除了碰撞相关逻辑**
 | 
						||
- TMap<FName,FCollisionMeshElement> CollisionMesh:碰撞Name => 碰撞Mesh Map。
 | 
						||
	- FCollisionMeshElement:管理单个碰撞数据的结构体。
 | 
						||
- UBodySetup:虚幻引擎中一个重要的物理相关类,主要用于定义和存储物体的物理属性和碰撞设置
 | 
						||
 | 
						||
```c++
 | 
						||
bool AShaderWorldActor::CollisionFinalizeWork()
 | 
						||
{
 | 
						||
	SCOPED_NAMED_EVENT_TEXT("AShaderWorldActor::CollisionFinalizeWork()", FColor::Magenta);
 | 
						||
	SW_FCT_CYCLE()
 | 
						||
	
 | 
						||
	if (!CollisionReadToProcess.IsEmpty())
 | 
						||
		return true;
 | 
						||
 | 
						||
	const double GameThreadBudget_ms = SWGameThreadBudgetCollision_ms.GetValueOnGameThread();
 | 
						||
 | 
						||
	double TimeStart = FPlatformTime::Seconds();
 | 
						||
 | 
						||
	//判断处理队列是否为空
 | 
						||
	if(CollisionWorkQueue.Num()<=0)
 | 
						||
		return true;
 | 
						||
 | 
						||
	{
 | 
						||
		FScopeLock CollisionMeshArrayAccess(&CollisionMeshAccessLock);
 | 
						||
		for (int i = CollisionWorkQueue.Num() - 1; i >= 0; i--)
 | 
						||
		{
 | 
						||
			//判断生成时间是否是否超过预设数值,如果超过就直接返回。默认为1 ms。
 | 
						||
			if ((FPlatformTime::Seconds() - TimeStart) * 1000.0 > GameThreadBudget_ms)
 | 
						||
				return false;
 | 
						||
 | 
						||
			//将生成的碰撞数据赋予给对应的碰撞Mesh,最后移除任务队列中的任务。
 | 
						||
			FCollisionProcessingWork& Work = CollisionWorkQueue[i];
 | 
						||
			ensure(CollisionMesh.Find(Work.MeshID));
 | 
						||
			FCollisionMeshElement& Mesh = CollisionMesh[Work.MeshID];
 | 
						||
			Mesh.Mesh->UpdateSectionTriMesh(Work.DestB);
 | 
						||
			CollisionWorkQueue.RemoveAt(i);
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	CollisionWorkQueue.Empty();
 | 
						||
	return true;
 | 
						||
}
 | 
						||
 | 
						||
void UShaderWorldCollisionComponent::UpdateSectionTriMesh(TSharedPtr<FSWShareableVerticePositionBuffer, ESPMode::ThreadSafe>& Positions)
 | 
						||
{	
 | 
						||
	//当UBodySetup更新队列还有任务时,执行改函数会将FSWShareableVerticePositionBuffer加入到UpdatesReceivedDuringCompute数组中并且退出。
 | 
						||
	if (AsyncBodySetupQueue.Num() > 0)
 | 
						||
	{
 | 
						||
#if SWDEBUG
 | 
						||
		SW_LOG("Collision Update received during alreayd occuring computation %s",*GetName())
 | 
						||
#endif
 | 
						||
 | 
						||
		UpdatesReceivedDuringCompute.Add(Positions);
 | 
						||
 | 
						||
		return;
 | 
						||
	}
 | 
						||
	
 | 
						||
	UpdatesReceivedDuringCompute.Empty();
 | 
						||
	
 | 
						||
	//使用FSWShareableVerticePositionBuffer数据来更新当前UShaderWorldCollisionComponent
 | 
						||
	bool EnsureSameBuffers = ProcMeshSections.Num() > 0 && ProcMeshSections[0].PositionBuffer.IsValid() && (ProcMeshSections[0].PositionBuffer->Positions3f.Num() == 0 || ProcMeshSections[0].PositionBuffer->Positions3f.Num() == Positions->Positions.Num());
 | 
						||
	if(!EnsureSameBuffers)
 | 
						||
	{
 | 
						||
#if SWDEBUG
 | 
						||
		UE_LOG(LogTemp,Warning,TEXT("Error UpdateSectionTriMesh : buffers incompatible"));
 | 
						||
#endif
 | 
						||
 | 
						||
	}
 | 
						||
	else
 | 
						||
	{
 | 
						||
		ProcMeshSections[0].PositionBuffer.Reset();
 | 
						||
		ProcMeshSections[0].Normals.Reset();
 | 
						||
 | 
						||
		ProcMeshSections[0].PositionBuffer = Positions;
 | 
						||
		ProcMeshSections[0].SectionLocalBox = Positions->Bound;
 | 
						||
		ProcMeshSections[0].bEnableCollision = true;
 | 
						||
 | 
						||
		Async(EAsyncExecution::TaskGraph, [WeakThis = MakeWeakObjectPtr(this), PBuffer = Positions, IndexB = ProcMeshSections[0].IndexBuffer]
 | 
						||
		{
 | 
						||
				TSharedPtr<TArray<FVector>, ESPMode::ThreadSafe> Normals = SWComputeNormalsForPatch(PBuffer, IndexB);
 | 
						||
 | 
						||
				AsyncTask(ENamedThreads::GameThread, [WeakThis,PBuffer, Normals]()
 | 
						||
				{
 | 
						||
						if (WeakThis.IsValid())
 | 
						||
						{
 | 
						||
							if (UShaderWorldCollisionComponent* Comp = Cast<UShaderWorldCollisionComponent>(WeakThis.Get()))
 | 
						||
							{
 | 
						||
								//塞入Normal
 | 
						||
								if(IsValid(Comp))
 | 
						||
									Comp->ReceiveComputedNormals(PBuffer, Normals);
 | 
						||
							}
 | 
						||
						}
 | 
						||
				});
 | 
						||
		});
 | 
						||
	}
 | 
						||
	
 | 
						||
	//Materials
 | 
						||
	// Pass new positions to trimesh
 | 
						||
	UpdateCollision();//1. 异步Cook UBodySetup 2. 删除原本的碰撞 3. UseBodySetup->CreatePhysicsMeshesAsync(),使用UBodySetup异步创建新的碰撞网格。
 | 
						||
	UpdateLocalBounds();//更新UShaderWorldCollisionComponent的LocalBoundingBox
 | 
						||
	UpdateNavigation();
 | 
						||
 | 
						||
	if (ProcMeshSections.Num() > 0 && (ProcMeshSections[0].bSectionVisible || (GetWorld() && !GetWorld()->IsGameWorld())))
 | 
						||
	{
 | 
						||
		// If we have a valid proxy and it is not pending recreation
 | 
						||
		if (SceneProxy && !IsRenderStateDirty())
 | 
						||
		{
 | 
						||
			//SW_LOG("Update trimesh SceneProxy && !IsRenderStateDirty()")
 | 
						||
			// Create data to update section
 | 
						||
			FShaderWColProcMeshSectionUpdateData* SectionData = new FShaderWColProcMeshSectionUpdateData;
 | 
						||
			SectionData->TargetSection = 0;
 | 
						||
			SectionData->NewPositionBuffer = ProcMeshSections[0].PositionBuffer;
 | 
						||
 | 
						||
			//更新SceneProxy FShaderWProceduralMeshSceneProxy的NewPositionBuffer,也就是UpdateSection
 | 
						||
			if(AShaderWorldActor* owner = Cast<AShaderWorldActor>(GetOwner()))
 | 
						||
			{
 | 
						||
				{
 | 
						||
					// Enqueue command to send to render thread
 | 
						||
					FShaderWProceduralMeshSceneProxy* ProcMeshSceneProxy = (FShaderWProceduralMeshSceneProxy*)(SceneProxy && !IsRenderStateDirty() ? SceneProxy : nullptr);
 | 
						||
					ENQUEUE_RENDER_COMMAND(FGeoCProcMeshSectionUpdate)
 | 
						||
						([ProcMeshSceneProxy, SectionData](FRHICommandListImmediate& RHICmdList)
 | 
						||
						{
 | 
						||
							if(ProcMeshSceneProxy)
 | 
						||
								ProcMeshSceneProxy->UpdateSection_RenderThread(SectionData);
 | 
						||
						});
 | 
						||
				}				
 | 
						||
			}
 | 
						||
			else
 | 
						||
			{
 | 
						||
				//SW_LOG("Update trimesh !owner")
 | 
						||
			}
 | 
						||
		}
 | 
						||
		else
 | 
						||
		{
 | 
						||
			//SW_LOG("Update trimesh !(SceneProxy && !IsRenderStateDirty())")
 | 
						||
		}
 | 
						||
 | 
						||
		MarkRenderTransformDirty();
 | 
						||
	}
 | 
						||
}
 | 
						||
```
 | 
						||
 | 
						||
#### CollisionPreprocessGPU()
 | 
						||
GPU生成碰撞 的预处理阶段。
 | 
						||
将从GPU回读的碰撞顶点Z轴数据***uint8* ReadData8*** 反序列化成double,之后再还原成***FCollisionProcessingWork.DestB***的Positions、Normals、Positions3f、MaterialIndices、Bound。
 | 
						||
 | 
						||
```c++
 | 
						||
bool AShaderWorldActor::CollisionPreprocessGPU()
 | 
						||
{
 | 
						||
	SCOPED_NAMED_EVENT_TEXT("AShaderWorldActor::CollisionPreprocessGPU()", FColor::Magenta);
 | 
						||
	SW_FCT_CYCLE()
 | 
						||
 | 
						||
	for (int32 CollID = CollisionReadToProcess.Num() - 1; CollID >= 0; CollID--)
 | 
						||
	{
 | 
						||
		const FName& ElID = CollisionReadToProcess[CollID];
 | 
						||
 | 
						||
 | 
						||
		if (!CollisionMesh.Find(ElID))
 | 
						||
		{
 | 
						||
			CollisionWorkQueue.Empty();
 | 
						||
			CollisionReadToProcess.RemoveAt(CollID);
 | 
						||
			continue;
 | 
						||
		}
 | 
						||
 | 
						||
		//判断FCollisionMeshElement是否有效,以及是否将碰撞数据回读完成,如果完成,则将数据添加到碰撞处理队列CollisionWorkQueue,并且从碰撞回读队列CollisionReadToProcess
 | 
						||
		FCollisionMeshElement& Mesh = *CollisionMesh.Find(ElID);
 | 
						||
		if ((*Mesh.ReadBackCompletion.Get()))
 | 
						||
		{
 | 
						||
			ensure(Mesh.Mesh);
 | 
						||
 | 
						||
			if(FGeoCProcMeshSection* Section = Mesh.Mesh->GetProcMeshSection(0))
 | 
						||
			{
 | 
						||
				if(CollisionMesh.Contains(CollisionBufferHolder))
 | 
						||
				{
 | 
						||
					CollisionWorkQueue.Emplace(
 | 
						||
						ElID
 | 
						||
						, Mesh.HeightData
 | 
						||
						, (*CollisionMesh.Find(CollisionBufferHolder)).Mesh->VerticesTemplate
 | 
						||
						, (*CollisionMesh.Find(CollisionBufferHolder)).Mesh->TrianglesTemplate);
 | 
						||
				}
 | 
						||
				else
 | 
						||
				{
 | 
						||
					SW_LOG("!CollisionMesh.Contains(CollisionBufferHolder)")
 | 
						||
				}
 | 
						||
 | 
						||
			}
 | 
						||
			else
 | 
						||
			{
 | 
						||
				SW_LOG("CollisionPreprocessGPU :: Mesh.Mesh->GetProcMeshSection(0) = nullptr")
 | 
						||
			}
 | 
						||
			
 | 
						||
			CollisionReadToProcess.RemoveAt(CollID);
 | 
						||
		}
 | 
						||
	}
 | 
						||
	//回读队列处理完毕后之后执行后续逻辑
 | 
						||
	if (!CollisionReadToProcess.IsEmpty())
 | 
						||
	{
 | 
						||
		return false;
 | 
						||
	}
 | 
						||
 | 
						||
	//开始执行碰撞处理队列逻辑
 | 
						||
	CollisionReadToProcess.Empty();
 | 
						||
	if (CollisionWorkQueue.Num() > 0)
 | 
						||
	{
 | 
						||
		(*bProcessingGroundCollision.Get()) = true;
 | 
						||
 | 
						||
		AShaderWorldActor* SWContext = this;
 | 
						||
		//异步并行 反序列化Ground碰撞数据
 | 
						||
		Async(EAsyncExecution::TaskGraph, [Completion = bProcessingGroundCollision, RenderAPI = RendererAPI, VerticesPerPatch = CollisionVerticesPerPatch, Work = CollisionWorkQueue]
 | 
						||
			{
 | 
						||
 | 
						||
				ParallelFor(Work.Num(), [&](int32 WorkIndex)
 | 
						||
					{						
 | 
						||
						
 | 
						||
						const FCollisionProcessingWork& WorkEl = Work[WorkIndex];
 | 
						||
 | 
						||
 | 
						||
						if (!WorkEl.Read.IsValid() || !WorkEl.SourceB.IsValid() || !WorkEl.DestB.IsValid())
 | 
						||
							return;
 | 
						||
 | 
						||
						const int NumOfVertex = WorkEl.SourceB->Positions.Num();
 | 
						||
 | 
						||
						WorkEl.DestB->Positions.SetNum(NumOfVertex);
 | 
						||
						WorkEl.DestB->Positions3f.SetNum(NumOfVertex);
 | 
						||
						WorkEl.DestB->MaterialIndices.SetNum(NumOfVertex);
 | 
						||
						WorkEl.DestB->Bound = FBox(EForceInit::ForceInit);
 | 
						||
 | 
						||
						FVector LocationfVertice_WS(0);
 | 
						||
						uint16 MaterialIndice = 0;
 | 
						||
 | 
						||
						//取得从GPU回读的序列化数据
 | 
						||
						uint8* ReadData8 = (uint8*)WorkEl.Read->ReadData.GetData();
 | 
						||
 | 
						||
						TSet<int32>& TrianglesAffectedByHoles = WorkEl.DestB->TrianglesAffectedByHoles;
 | 
						||
						TrianglesAffectedByHoles.Empty();
 | 
						||
 | 
						||
						//#TODO ISPC slower ?
 | 
						||
#if 0 // INTEL_ISPC
 | 
						||
							if (RenderAPI == EGeoRenderingAPI::OpenGL)
 | 
						||
							{
 | 
						||
								ispc::ShaderWorld_HeightFromGPUReadOpenGL(NumOfVertex, VerticesPerPatch, ReadData8,(ispc::FVector*)WorkEl.SourceB->Positions.GetData(), (ispc::FVector*)WorkEl.DestB->Positions.GetData(), (ispc::FVector3f*)WorkEl.DestB->Positions3f.GetData(), WorkEl.DestB->MaterialIndices.GetData());
 | 
						||
								
 | 
						||
							}
 | 
						||
							else
 | 
						||
							{
 | 
						||
								ispc::ShaderWorld_HeightFromGPURead(NumOfVertex, VerticesPerPatch, ReadData8, (ispc::FVector3f*)WorkEl.SourceB->Positions3f.GetData(), (ispc::FVector*)WorkEl.DestB->Positions.GetData(), (ispc::FVector3f*)WorkEl.DestB->Positions3f.GetData(), WorkEl.DestB->MaterialIndices.GetData());
 | 
						||
							}
 | 
						||
#else
 | 
						||
 | 
						||
						//从对序列化碰撞Z轴数据进行反序列化,还原成double,再之后还原成FCollisionProcessingWork.DestB
 | 
						||
						for (int32 k = 0; k < NumOfVertex; k++)
 | 
						||
							{
 | 
						||
 | 
						||
								if (RenderAPI == EGeoRenderingAPI::OpenGL)
 | 
						||
								{
 | 
						||
									const int index = k % VerticesPerPatch + (VerticesPerPatch - 1 - (k / VerticesPerPatch)) * VerticesPerPatch;
 | 
						||
 | 
						||
									LocationfVertice_WS = FVector(WorkEl.SourceB->Positions[k].X, WorkEl.SourceB->Positions[k].Y, GetHeightFromGPURead(&ReadData8[4 * index], MaterialIndice));
 | 
						||
								}
 | 
						||
								else
 | 
						||
									LocationfVertice_WS = FVector(WorkEl.SourceB->Positions[k].X, WorkEl.SourceB->Positions[k].Y, GetHeightFromGPURead(&ReadData8[4 * k], MaterialIndice));
 | 
						||
 | 
						||
								WorkEl.DestB->Positions[k] = LocationfVertice_WS;
 | 
						||
								WorkEl.DestB->Positions3f[k] = FVector3f(LocationfVertice_WS);
 | 
						||
								WorkEl.DestB->MaterialIndices[k] = MaterialIndice;
 | 
						||
 | 
						||
								
 | 
						||
								/*
 | 
						||
								 * Height below -7km means terrain hole
 | 
						||
								 */
 | 
						||
								if(WorkEl.DestB->Positions[k].Z < -700000.0)
 | 
						||
								{
 | 
						||
									/*
 | 
						||
									 * Find triangles including this vertex and add them to removed triangles
 | 
						||
									 */
 | 
						||
 | 
						||
									if(WorkEl.SourceB->PositionToTriangle.Contains(k))
 | 
						||
									{
 | 
						||
										TrianglesAffectedByHoles.Append(*WorkEl.SourceB->PositionToTriangle.Find(k));
 | 
						||
									}
 | 
						||
								}
 | 
						||
								else
 | 
						||
								{
 | 
						||
									WorkEl.DestB->Bound += WorkEl.DestB->Positions[k];
 | 
						||
								}
 | 
						||
							}
 | 
						||
 | 
						||
#endif
 | 
						||
						/*
 | 
						||
						 * If the terrain has holes, create a custom index buffer with the related triangles removed
 | 
						||
						 * 地形挖洞,碰撞逻辑处理。
 | 
						||
						 */
 | 
						||
						if(TrianglesAffectedByHoles.Num() > 0)
 | 
						||
						{
 | 
						||
							WorkEl.DestB->FilteredTriangles = MakeShared<FSWShareableIndexBuffer, ESPMode::ThreadSafe>();
 | 
						||
 | 
						||
							for(int32 Triangle = 0; Triangle < WorkEl.TriangleTemplate->Indices.Num()/3; Triangle++)
 | 
						||
							{
 | 
						||
								if(!TrianglesAffectedByHoles.Contains(Triangle))
 | 
						||
								{
 | 
						||
									WorkEl.DestB->FilteredTriangles->Indices.Add(WorkEl.TriangleTemplate->Indices[Triangle * 3]);
 | 
						||
									WorkEl.DestB->FilteredTriangles->Indices.Add(WorkEl.TriangleTemplate->Indices[Triangle * 3 + 1]);
 | 
						||
									WorkEl.DestB->FilteredTriangles->Indices.Add(WorkEl.TriangleTemplate->Indices[Triangle * 3 + 2]);
 | 
						||
 | 
						||
									WorkEl.DestB->FilteredTriangles->Triangles_CollisionOnly.Add(WorkEl.TriangleTemplate->Triangles_CollisionOnly[Triangle]);
 | 
						||
								}
 | 
						||
							}
 | 
						||
						}
 | 
						||
						else
 | 
						||
						{
 | 
						||
							WorkEl.DestB->FilteredTriangles.Reset();
 | 
						||
						}
 | 
						||
 | 
						||
 | 
						||
						//WorkEl.DestB->Bound = FBox(WorkEl.DestB->Positions);
 | 
						||
					}
 | 
						||
				);
 | 
						||
 | 
						||
				//执行完成后将Completion(bProcessingGroundCollision)设置为false代表处理完成。
 | 
						||
				if (Completion.IsValid())
 | 
						||
					Completion->AtomicSet(false);
 | 
						||
 | 
						||
			});
 | 
						||
	}
 | 
						||
 | 
						||
	return true;
 | 
						||
}
 | 
						||
```
 | 
						||
#### CollisionGPU()
 | 
						||
```c++
 | 
						||
 | 
						||
void AShaderWorldActor::CollisionGPU()
 | 
						||
{
 | 
						||
	SCOPED_NAMED_EVENT_TEXT("AShaderWorldActor::CollisionGPU()", FColor::Magenta);
 | 
						||
	SW_FCT_CYCLE()
 | 
						||
 | 
						||
	double CurrentTime = FPlatformTime::Seconds();
 | 
						||
 | 
						||
		if (!(!(*bPreprocessingCollisionUpdate.Get()) && (
 | 
						||
			CollisionShareable->CollisionMeshToUpdate.Num() > 0 ||
 | 
						||
			CollisionShareable->CollisionMeshToRenameMoveUpdate.Num() > 0 ||
 | 
						||
			CollisionShareable->CollisionMeshToCreate.Num() > 0)))
 | 
						||
		{
 | 
						||
			/*
 | 
						||
			 * No collision Preprocessing running, we can safely access the data in the shared pointer without risking race condition
 | 
						||
			 * 确保没有PreprocessingCollision任务在运行
 | 
						||
			 */
 | 
						||
			if(!(*bPreprocessingCollisionUpdate.Get()))
 | 
						||
			{
 | 
						||
				FScopeLock CollisionMeshArrayAccess(CollisionMeshAccessLock.Get());
 | 
						||
 | 
						||
				TArray<FName> NameToRemove;
 | 
						||
				//遍历AvailableCollisionMesh
 | 
						||
				for (const FName& AvailableID : CollisionShareable->AvailableCollisionMesh)
 | 
						||
				{
 | 
						||
					FCollisionMeshElement& Elem = *CollisionMesh.Find(AvailableID);
 | 
						||
					//更新时间戳
 | 
						||
					if (Elem.SleepTime < 0.0)
 | 
						||
						Elem.SleepTime = CurrentTime;
 | 
						||
					else if ((abs(CurrentTime - Elem.SleepTime) >= 1.0) && (CollisionBufferHolder == "" || AvailableID != CollisionBufferHolder) && (!Elem.Mesh || (Elem.Mesh && !Elem.Mesh->HasAsyncWorkPending())))//如果生成超时,则销毁对应变量
 | 
						||
					{
 | 
						||
						if (IsValid(Elem.Mesh))						
 | 
						||
							Elem.Mesh->DestroyComponent();							
 | 
						||
						
 | 
						||
						Elem.Mesh = nullptr;
 | 
						||
 | 
						||
						DecountRTAllocatedMemory(Elem.CollisionRT)
 | 
						||
						DecountRTAllocatedMemory(Elem.CollisionRT_Duplicate)
 | 
						||
 | 
						||
						Elem.CollisionRT = nullptr;
 | 
						||
						Elem.CollisionRT_Duplicate = nullptr;
 | 
						||
						Elem.DynCollisionCompute = nullptr;
 | 
						||
						Elem.LayerComputeForPhysicalMaterial.Empty();
 | 
						||
 | 
						||
						CollisionMesh.Remove(AvailableID);
 | 
						||
 | 
						||
						if (UsedCollisionMesh.Contains(AvailableID))
 | 
						||
						{
 | 
						||
							UsedCollisionMesh.Remove(AvailableID);
 | 
						||
						}
 | 
						||
 | 
						||
						CollisionShareable->CollisionMeshData.Remove(AvailableID);
 | 
						||
						CollisionShareable->UsedCollisionMesh.Remove(AvailableID);
 | 
						||
						NameToRemove.Add(AvailableID);
 | 
						||
					}
 | 
						||
					else
 | 
						||
					{
 | 
						||
					}
 | 
						||
				}
 | 
						||
				for (auto& TR : NameToRemove)
 | 
						||
				{
 | 
						||
					CollisionShareable->AvailableCollisionMesh.Remove(TR);
 | 
						||
				}
 | 
						||
			}
 | 
						||
 | 
						||
			return;
 | 
						||
		}
 | 
						||
			
 | 
						||
 | 
						||
 | 
						||
	UWorld* World = GetWorld();
 | 
						||
 | 
						||
	bool RequireRenderFence = false;
 | 
						||
	int32 CollisionDrawCallCount = 0;
 | 
						||
 | 
						||
 | 
						||
	UsedCollisionMesh = CollisionShareable->UsedCollisionMesh;
 | 
						||
 | 
						||
	/*
 | 
						||
	 * Async task created proxy CollisionMesh and associated IDs to them, create actual GameThread collision using the provided IDs
 | 
						||
	 * 异步任务创建了代理碰撞网格,并将相关 ID 与它们关联起来,使用提供的 ID 在游戏线程中创建实际的碰撞。
 | 
						||
	 */
 | 
						||
	for (FName& ID : CollisionShareable->CollisionMeshToCreate)
 | 
						||
	{
 | 
						||
		//使用需要创建碰撞的ID(Name)来获取碰撞MeshData.
 | 
						||
		const FSWCollisionMeshElemData& Mesh_Shareable = *CollisionShareable->CollisionMeshData.Find(ID);
 | 
						||
 | 
						||
		FCollisionMeshElement& Mesh = GetACollisionMesh(ID);//GetACollisionMesh()取得现有FCollisionMeshElement或者创建新的,代码贼长。
 | 
						||
 | 
						||
		Mesh.Location = Mesh_Shareable.Location;
 | 
						||
		Mesh.MeshLocation = Mesh_Shareable.MeshLocation;
 | 
						||
 | 
						||
		Mesh.Mesh->SetWorldTransform(FTransform(Mesh_Shareable.MeshLocation));
 | 
						||
		Mesh.Mesh->SetCollisionProfileName(UCollisionProfile::BlockAll_ProfileName);
 | 
						||
		Mesh.Mesh->Mobility = EComponentMobility::Static;
 | 
						||
	}
 | 
						||
 | 
						||
	//清空创建列表。
 | 
						||
	CollisionShareable->CollisionMeshToCreate.Empty();
 | 
						||
 | 
						||
	//处理CollisionMeshToUpdate 碰撞数据更新队列
 | 
						||
	for (int i = CollisionShareable->CollisionMeshToUpdate.Num() - 1; i >= 0; i--)
 | 
						||
	{
 | 
						||
		FName& CollisionIDToUpdate = CollisionShareable->CollisionMeshToUpdate[i];
 | 
						||
		FCollisionMeshElement& El = *CollisionMesh.Find(CollisionIDToUpdate);
 | 
						||
		UpdateCollisionMeshData(El);//核心函数
 | 
						||
 | 
						||
		CollisionShareable->CollisionMeshToUpdate.RemoveAt(i);
 | 
						||
 | 
						||
		RequireRenderFence = true;
 | 
						||
		CollisionDrawCallCount++;
 | 
						||
 | 
						||
		if (CollisionDrawCallCount >= CollisionMaxDrawCallPerFrame)
 | 
						||
			break;
 | 
						||
	}
 | 
						||
 | 
						||
	//处理CollisionMeshToRenameMoveUpdate 碰撞数据重命名&移动更新队列
 | 
						||
	for (int i = CollisionShareable->CollisionMeshToRenameMoveUpdate.Num() - 1; i >= 0; i--)
 | 
						||
	{
 | 
						||
		FName& CollisionIDToUpdate = CollisionShareable->CollisionMeshToRenameMoveUpdate[i];
 | 
						||
 | 
						||
		ensure(CollisionMesh.Find(CollisionIDToUpdate) != nullptr);
 | 
						||
		ensure(CollisionShareable->CollisionMeshData.Find(CollisionIDToUpdate) != nullptr);
 | 
						||
 | 
						||
		FCollisionMeshElement& El = *CollisionMesh.Find(CollisionIDToUpdate);
 | 
						||
		const FSWCollisionMeshElemData& El_Shareable = *CollisionShareable->CollisionMeshData.Find(CollisionIDToUpdate);
 | 
						||
 | 
						||
		El.Location = El_Shareable.Location;
 | 
						||
		El.MeshLocation = El_Shareable.MeshLocation;
 | 
						||
 | 
						||
		El.Mesh->SetLocationOnPhysicCookComplete(El.MeshLocation);
 | 
						||
 | 
						||
		UpdateCollisionMeshData(El);
 | 
						||
 | 
						||
		CollisionShareable->CollisionMeshToRenameMoveUpdate.RemoveAt(i);
 | 
						||
 | 
						||
		RequireRenderFence = true;
 | 
						||
		CollisionDrawCallCount++;
 | 
						||
 | 
						||
		if (CollisionDrawCallCount >= CollisionMaxDrawCallPerFrame)
 | 
						||
			break;
 | 
						||
	}
 | 
						||
 | 
						||
	//开启CollisionProcess,需要等到执行完之后,才能执行CollisionPreprocessGPU()
 | 
						||
	if (RequireRenderFence)
 | 
						||
	{
 | 
						||
		CollisionProcess.BeginFence();
 | 
						||
	}
 | 
						||
		
 | 
						||
}
 | 
						||
```
 | 
						||
 | 
						||
```c++
 | 
						||
FCollisionMeshElement& AShaderWorldActor::GetACollisionMesh(FName Name/*, FGuid AlreadyCreatedID*/)
 | 
						||
{
 | 
						||
	UWorld* World = GetWorld();
 | 
						||
 | 
						||
	//存在可用CollisionMesh时,使用ID去寻找,如果找到则直接返回。并将AvailableCollisionMesh中的ID 转移到 UsedCollisionMesh
 | 
						||
	if (CollisionShareable.IsValid() && CollisionShareable->AvailableCollisionMesh.Num() > 0 )
 | 
						||
	{
 | 
						||
		if(FName* FoundName = CollisionShareable->AvailableCollisionMesh.Find(Name))
 | 
						||
		{
 | 
						||
			
 | 
						||
			if (FCollisionMeshElement* Elem = CollisionMesh.Find(Name))
 | 
						||
			{
 | 
						||
				UsedCollisionMesh.Add(Name);
 | 
						||
				CollisionShareable->UsedCollisionMesh.Add(Name);
 | 
						||
				CollisionShareable->AvailableCollisionMesh.Remove(Name);
 | 
						||
				Elem->SleepTime = -1.0;
 | 
						||
				return *Elem;
 | 
						||
			}			
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	FScopeLock CollisionMeshArrayAccess(CollisionMeshAccessLock.Get());
 | 
						||
	//往CollisionMesh Map添加新元素(FCollisionMeshElement)并设置其属性。
 | 
						||
	CollisionMesh.Add(Name, {});
 | 
						||
	FCollisionMeshElement& NewElem = *CollisionMesh.Find(Name);
 | 
						||
	NewElem.ID = Name;
 | 
						||
 | 
						||
 | 
						||
	uint32 SizeT = (uint32)CollisionVerticesPerPatch;
 | 
						||
	NewElem.CollisionVerticesPerPatch = SizeT;
 | 
						||
 | 
						||
 | 
						||
	NewElem.HeightData = MakeShared<FSWColorRead, ESPMode::ThreadSafe>();
 | 
						||
	NewElem.HeightData->ReadData.SetNum(CollisionVerticesPerPatch * CollisionVerticesPerPatch);
 | 
						||
 | 
						||
	NewElem.Mesh = NewObject<UShaderWorldCollisionComponent>(this, Name, RF_Transient | RF_TextExportTransient);
 | 
						||
 | 
						||
	NewElem.Mesh->SetUsedPhysicalMaterial(RootComp->PhysicalMaterials);
 | 
						||
 | 
						||
	NewElem.Mesh->bUseAsyncCooking = true;
 | 
						||
 | 
						||
	NewElem.Mesh->SetSWWorldVersion(Shareable_ID);
 | 
						||
 | 
						||
	if (RootComp)
 | 
						||
	{
 | 
						||
		NewElem.Mesh->ComponentTags = RootComp->ComponentTags;
 | 
						||
		NewElem.Mesh->BodyInstance.CopyBodyInstancePropertiesFrom(&RootComp->BodyInstance);
 | 
						||
 | 
						||
		NewElem.Mesh->ComponentTags = RootComp->ComponentTags;
 | 
						||
	}
 | 
						||
	else
 | 
						||
	{
 | 
						||
		UE_LOG(LogTemp, Warning, TEXT("UShaderWorldCollisionComponent Creation : invalid RootComp"))
 | 
						||
	}
 | 
						||
 | 
						||
	NewElem.Mesh->SetCollisionObjectType(CollisionChannel);
 | 
						||
	NewElem.Mesh->SetGenerateOverlapEvents(true);
 | 
						||
	NewElem.Mesh->SetupAttachment(RootComponent);
 | 
						||
 | 
						||
	//NewElem.Mesh->SetRelativeLocation(FVector(0.f, 0.f, 0.f));
 | 
						||
	NewElem.Mesh->bUseComplexAsSimpleCollision = true;
 | 
						||
	/*
 | 
						||
	NewElem.Mesh->SetUsingAbsoluteLocation(true);
 | 
						||
	NewElem.Mesh->SetUsingAbsoluteRotation(true);
 | 
						||
	*/
 | 
						||
	//NewElem.Mesh->bTraceComplexOnMove = false;
 | 
						||
	NewElem.MeshLocation = NewElem.Mesh->GetComponentLocation();
 | 
						||
 | 
						||
#if WITH_EDITORONLY_DATA
 | 
						||
	NewElem.Mesh->bConsiderForActorPlacementWhenHidden = true;
 | 
						||
#endif
 | 
						||
 | 
						||
	NewElem.Mesh->RegisterComponent();
 | 
						||
 | 
						||
	TSharedPtr<FSWShareableVerticePositionBuffer, ESPMode::ThreadSafe> Vertices = MakeShared<FSWShareableVerticePositionBuffer, ESPMode::ThreadSafe>();
 | 
						||
	TSharedPtr<FSWShareableIndexBuffer, ESPMode::ThreadSafe> Triangles = MakeShared<FSWShareableIndexBuffer, ESPMode::ThreadSafe>();
 | 
						||
 | 
						||
	float Spacing = CollisionResolution;
 | 
						||
 | 
						||
	if(CollisionBufferHolder.IsValid() && (!CollisionMesh.Find(CollisionBufferHolder) || (CollisionMesh.Find(CollisionBufferHolder) && !CollisionMesh.Find(CollisionBufferHolder)->Mesh)))
 | 
						||
		CollisionBufferHolder = "";
 | 
						||
 | 
						||
	//符合条件则重新创建碰撞GPU计算用RT。
 | 
						||
	if ( CollisionBufferHolder == "" || (CollisionBufferHolder != "" && CollisionBufferHolder == Name))
 | 
						||
	{
 | 
						||
		CollisionBufferHolder = Name;
 | 
						||
 | 
						||
		SW_RT(NewElem.CollisionRT, World, Name.ToString() + FString::FromInt(GetUniqueID()), SizeT, TF_Nearest, RTF_RGBA8)
 | 
						||
		RTAllocatedMemory(NewElem.CollisionRT)
 | 
						||
 | 
						||
 | 
						||
		if (bExportPhysicalMaterialID_cached && LayerStoringMaterialID != "")
 | 
						||
		{
 | 
						||
			for (FSWProducers& layer : Producers)
 | 
						||
			{
 | 
						||
				if (layer.LayerName == "Height" || layer.LayerName == "height")
 | 
						||
				{
 | 
						||
					continue;
 | 
						||
				}
 | 
						||
 | 
						||
				if (layer.LayerName != "" && layer.MaterialToGenerateLayer)
 | 
						||
				{
 | 
						||
					NewElem.LayerOrder.Add(layer.LayerName);
 | 
						||
 | 
						||
					UShaderWorldRT2D* LayerRT = nullptr;
 | 
						||
					SW_RT(LayerRT, World, layer.LayerName + "_PhyMat_" + FString::FromInt(GetUniqueID()), SizeT, layer.LayerFiltering, layer.LayerFormat)
 | 
						||
						RTAllocatedMemory(LayerRT)
 | 
						||
 | 
						||
						NewElem.LayerComputeForPhysicalMaterial.Add(layer.LayerName, { LayerRT ,nullptr });
 | 
						||
				}
 | 
						||
 | 
						||
				if (LayerStoringMaterialID == layer.LayerName)
 | 
						||
					break;
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
 | 
						||
		SW_RT(NewElem.CollisionRT_Duplicate, World, "RT_Temp_" + Name.ToString() + FString::FromInt(GetUniqueID()), SizeT, TF_Nearest, RTF_RGBA8)
 | 
						||
		RTAllocatedMemory(NewElem.CollisionRT_Duplicate)
 | 
						||
 | 
						||
		TArray<FVector2f> UV;
 | 
						||
		USWBlueprintFunctionLibrary::CreateGridMeshWelded(CollisionVerticesPerPatch, CollisionVerticesPerPatch, Triangles, Vertices, UV, Spacing);
 | 
						||
 | 
						||
		{
 | 
						||
			const int32 NumTriangles = Triangles->Indices.Num() / 3;
 | 
						||
			Triangles->Triangles_CollisionOnly.Empty();
 | 
						||
			Triangles->Triangles_CollisionOnly.Reserve(NumTriangles);
 | 
						||
			for (int32 TriIdx = 0; TriIdx < NumTriangles; TriIdx++)
 | 
						||
			{
 | 
						||
				FTriIndices& Triangle = Triangles->Triangles_CollisionOnly.AddDefaulted_GetRef();
 | 
						||
				Triangle.v0 = Triangles->Indices[(TriIdx * 3) + 0] /* + VertexBase */;
 | 
						||
				Triangle.v1 = Triangles->Indices[(TriIdx * 3) + 1] /* + VertexBase */;
 | 
						||
				Triangle.v2 = Triangles->Indices[(TriIdx * 3) + 2] /* + VertexBase */;
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		/*
 | 
						||
		 * The data inside the Vertices shared pointer can and will change over time, while VerticesTemplate is immutable,
 | 
						||
		 * therefore we are making a persistent copy instead of using Vertices directly
 | 
						||
		 */
 | 
						||
		NewElem.Mesh->VerticesTemplate = MakeShared<FSWShareableVerticePositionBuffer, ESPMode::ThreadSafe>();
 | 
						||
		NewElem.Mesh->VerticesTemplate->Positions = Vertices->Positions;
 | 
						||
		NewElem.Mesh->VerticesTemplate->Positions3f = Vertices->Positions3f;
 | 
						||
		NewElem.Mesh->VerticesTemplate->MaterialIndices = Vertices->MaterialIndices;
 | 
						||
 | 
						||
		NewElem.Mesh->VerticesTemplate->PositionToTriangle = Vertices->PositionToTriangle;
 | 
						||
 | 
						||
		NewElem.Mesh->TrianglesTemplate = Triangles;
 | 
						||
	}
 | 
						||
	else if(CollisionBufferHolder != "" && CollisionMesh.Find(CollisionBufferHolder) && !CollisionMesh.Find(CollisionBufferHolder)->Mesh->VerticesTemplate.IsValid())
 | 
						||
	{
 | 
						||
		/*
 | 
						||
		 * #TODO Should never occur, remove
 | 
						||
		 */
 | 
						||
#if SWDEBUG
 | 
						||
		SW_LOG("Recreating Collision Vertice template")
 | 
						||
#endif
 | 
						||
		const FCollisionMeshElement& BufferHolder = *CollisionMesh.Find(CollisionBufferHolder);
 | 
						||
 | 
						||
		TArray<FVector2f> UV;
 | 
						||
		USWBlueprintFunctionLibrary::CreateGridMeshWelded(CollisionVerticesPerPatch, CollisionVerticesPerPatch, Triangles, Vertices, UV, Spacing);
 | 
						||
 | 
						||
		{
 | 
						||
			const int32 NumTriangles = Triangles->Indices.Num() / 3;
 | 
						||
			Triangles->Triangles_CollisionOnly.Empty();
 | 
						||
			Triangles->Triangles_CollisionOnly.Reserve(NumTriangles);
 | 
						||
			for (int32 TriIdx = 0; TriIdx < NumTriangles; TriIdx++)
 | 
						||
			{
 | 
						||
				FTriIndices& Triangle = Triangles->Triangles_CollisionOnly.AddDefaulted_GetRef();
 | 
						||
				Triangle.v0 = Triangles->Indices[(TriIdx * 3) + 0] /* + VertexBase */;
 | 
						||
				Triangle.v1 = Triangles->Indices[(TriIdx * 3) + 1] /* + VertexBase */;
 | 
						||
				Triangle.v2 = Triangles->Indices[(TriIdx * 3) + 2] /* + VertexBase */;
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		BufferHolder.Mesh->VerticesTemplate = MakeShared<FSWShareableVerticePositionBuffer, ESPMode::ThreadSafe>();
 | 
						||
		BufferHolder.Mesh->VerticesTemplate->Positions = Vertices->Positions;
 | 
						||
		BufferHolder.Mesh->VerticesTemplate->Positions3f = Vertices->Positions3f;
 | 
						||
		BufferHolder.Mesh->VerticesTemplate->MaterialIndices = Vertices->MaterialIndices;
 | 
						||
 | 
						||
		BufferHolder.Mesh->VerticesTemplate->PositionToTriangle = Vertices->PositionToTriangle;
 | 
						||
 | 
						||
		BufferHolder.Mesh->TrianglesTemplate = Triangles;
 | 
						||
 | 
						||
		Vertices->Positions = BufferHolder.Mesh->VerticesTemplate->Positions;
 | 
						||
		Vertices->Positions3f = BufferHolder.Mesh->VerticesTemplate->Positions3f;
 | 
						||
		Vertices->MaterialIndices = BufferHolder.Mesh->VerticesTemplate->MaterialIndices;
 | 
						||
 | 
						||
		Triangles = BufferHolder.Mesh->TrianglesTemplate;
 | 
						||
 | 
						||
 | 
						||
		SW_RT(NewElem.CollisionRT, World, Name.ToString() + FString::FromInt(GetUniqueID()), SizeT, TF_Nearest, RTF_RGBA8)
 | 
						||
		RTAllocatedMemory(NewElem.CollisionRT)
 | 
						||
 | 
						||
 | 
						||
		if (bExportPhysicalMaterialID_cached && LayerStoringMaterialID != "")
 | 
						||
		{
 | 
						||
			for (FSWProducers& layer : Producers)
 | 
						||
			{
 | 
						||
				if (layer.LayerName == "Height" || layer.LayerName == "height")
 | 
						||
				{
 | 
						||
					continue;
 | 
						||
				}
 | 
						||
 | 
						||
				if (layer.LayerName != "" && layer.MaterialToGenerateLayer)
 | 
						||
				{
 | 
						||
					NewElem.LayerOrder.Add(layer.LayerName);
 | 
						||
 | 
						||
					UShaderWorldRT2D* LayerRT = nullptr;
 | 
						||
					SW_RT(LayerRT, World, layer.LayerName + "_PhyMat_" + FString::FromInt(GetUniqueID()), SizeT, layer.LayerFiltering, layer.LayerFormat)
 | 
						||
						RTAllocatedMemory(LayerRT)
 | 
						||
 | 
						||
						NewElem.LayerComputeForPhysicalMaterial.Add(layer.LayerName, { LayerRT ,nullptr });
 | 
						||
				}
 | 
						||
 | 
						||
				if (LayerStoringMaterialID == layer.LayerName)
 | 
						||
					break;
 | 
						||
			}
 | 
						||
		}
 | 
						||
	}
 | 
						||
	else
 | 
						||
	{
 | 
						||
		check(CollisionMesh.Contains(CollisionBufferHolder))
 | 
						||
 | 
						||
		const FCollisionMeshElement& BufferHolder = *CollisionMesh.Find(CollisionBufferHolder);
 | 
						||
 | 
						||
		check(BufferHolder.Mesh->VerticesTemplate.IsValid())
 | 
						||
		/*
 | 
						||
		 * No point copying irrelevant data that will be overriden once the computation is complete.
 | 
						||
		 */
 | 
						||
		Triangles = BufferHolder.Mesh->TrianglesTemplate;
 | 
						||
 | 
						||
		NewElem.CollisionRT = BufferHolder.CollisionRT;
 | 
						||
		NewElem.LayerOrder = BufferHolder.LayerOrder;
 | 
						||
		NewElem.LayerComputeForPhysicalMaterial = BufferHolder.LayerComputeForPhysicalMaterial;
 | 
						||
	}
 | 
						||
 | 
						||
	NewElem.Mesh->CreateMeshSection(0, Vertices, Triangles, false);
 | 
						||
 | 
						||
	/*
 | 
						||
	 * If not in editor mode, only make collision blue mesh visible if we asked for collisions to be visible
 | 
						||
	 * The renderthread will otherwise completely skip the collision meshes
 | 
						||
	 */
 | 
						||
	NewElem.Mesh->SetMeshSectionVisible(0, CollisionVisible);
 | 
						||
 | 
						||
 | 
						||
	if (!CollisionMat)
 | 
						||
	{
 | 
						||
#if SWDEBUG
 | 
						||
		SW_LOG("Material for Collision mesh not available for Shader World %s", *GetName())
 | 
						||
#endif
 | 
						||
	}
 | 
						||
	else
 | 
						||
	{
 | 
						||
#if WITH_EDITOR
 | 
						||
		if ((CollisionBufferHolder != Name)
 | 
						||
			&& CollisionMesh.Contains(CollisionBufferHolder)
 | 
						||
			&& (*CollisionMesh.Find(CollisionBufferHolder)).Mesh)
 | 
						||
		{
 | 
						||
 | 
						||
			NewElem.Mesh->SetMaterialFromOwner(0, (*CollisionMesh.Find(CollisionBufferHolder)).Mesh->GetMaterial(0));
 | 
						||
		}
 | 
						||
		else
 | 
						||
		{
 | 
						||
			UMaterialInstanceDynamic* DynColMat = SWTakeCareOfMID(CollisionMat.Get(), this);
 | 
						||
			SWorldSubsystem->SetScalarParameterValue(DynColMat, "MakeCollisionVisible", 1.0);
 | 
						||
			SWorldSubsystem->SetScalarParameterValue(DynColMat, "CollisionResolution", CollisionResolution);
 | 
						||
			NewElem.Mesh->SetMaterialFromOwner(0, DynColMat);
 | 
						||
		}
 | 
						||
#endif
 | 
						||
	}
 | 
						||
 | 
						||
	UsedCollisionMesh.Add(Name);
 | 
						||
	CollisionShareable->UsedCollisionMesh.Add(Name);
 | 
						||
 | 
						||
	//if (CollisionShareable->CollisionMeshData.Num() < CollisionMesh.Num())
 | 
						||
	{
 | 
						||
		if(!CollisionShareable->CollisionMeshData.Contains(Name))
 | 
						||
			CollisionShareable->CollisionMeshData.Add(Name, NewElem);
 | 
						||
	}
 | 
						||
 | 
						||
	return *CollisionMesh.Find(Name);
 | 
						||
	//return CollisionMesh[CollisionMesh.Num() - 1];
 | 
						||
}
 | 
						||
```
 | 
						||
 | 
						||
```c++
 | 
						||
void AShaderWorldActor::UpdateCollisionMeshData(FCollisionMeshElement& Mesh)
 | 
						||
{
 | 
						||
	FVector MesgLoc = FVector((Mesh.Location * CollisionResolution * (CollisionVerticesPerPatch - 1) + FIntVector(0.f, 0.f, 1) * HeightOnStart));
 | 
						||
 | 
						||
	if (!Generator)
 | 
						||
	{
 | 
						||
#if SWDEBUG
 | 
						||
		SW_LOG("Generator Material not available for Shader World %s", *GetName())
 | 
						||
#endif
 | 
						||
	}
 | 
						||
	else
 | 
						||
	{
 | 
						||
		UWorld* World = GetWorld();
 | 
						||
 | 
						||
		//OPTION A : Compute collision form GPU readback
 | 
						||
		UMaterialInstanceDynamic* DynCollisionMat = Mesh.DynCollisionCompute;
 | 
						||
		//如果生成高度材质无效,这里将会进行初始化。
 | 
						||
		if (!DynCollisionMat)
 | 
						||
		{	
 | 
						||
			DynCollisionMat = SWTakeCareOfMID(Generator.Get(), this);
 | 
						||
			Mesh.DynCollisionCompute = DynCollisionMat;
 | 
						||
 | 
						||
			SWorldSubsystem->SetScalarParameterValue(DynCollisionMat, "NoMargin", 1.f);
 | 
						||
			SWorldSubsystem->SetScalarParameterValue(DynCollisionMat, "TexelPerSide", CollisionVerticesPerPatch);
 | 
						||
			SWorldSubsystem->SetScalarParameterValue(DynCollisionMat, "PatchFullSize", CollisionResolution * (CollisionVerticesPerPatch - 1));
 | 
						||
			SWorldSubsystem->SetScalarParameterValue(DynCollisionMat, "MeshScale", CollisionResolution * (CollisionVerticesPerPatch <= 1 ? 1 : CollisionVerticesPerPatch));
 | 
						||
 | 
						||
			SWorldSubsystem->SetScalarParameterValue(DynCollisionMat, "N", CollisionVerticesPerPatch);
 | 
						||
			SWorldSubsystem->SetScalarParameterValue(DynCollisionMat, "CacheRes", CollisionVerticesPerPatch);
 | 
						||
			SWorldSubsystem->SetScalarParameterValue(DynCollisionMat, "LocalGridScaling", CollisionResolution);
 | 
						||
 | 
						||
			
 | 
						||
 | 
						||
			if (bExportPhysicalMaterialID_cached && LayerStoringMaterialID != "")
 | 
						||
			{
 | 
						||
				for (FSWProducers& Layer : Producers)
 | 
						||
				{
 | 
						||
					if (Layer.LayerName == "Height" || Layer.LayerName == "height")
 | 
						||
					{
 | 
						||
						continue;
 | 
						||
					}
 | 
						||
 | 
						||
					if (Layer.LayerName != "" && Layer.MaterialToGenerateLayer)
 | 
						||
					{
 | 
						||
						check(Mesh.LayerComputeForPhysicalMaterial.Contains(Layer.LayerName))						
 | 
						||
 | 
						||
						UMaterialInstanceDynamic* LayerPartitionDynMat = SWTakeCareOfMID(Layer.MaterialToGenerateLayer, this);
 | 
						||
 | 
						||
						FSWCollisionLayerData& CLayerData = *Mesh.LayerComputeForPhysicalMaterial.Find(Layer.LayerName);
 | 
						||
						CLayerData.DynMat = LayerPartitionDynMat;
 | 
						||
 | 
						||
						SWorldSubsystem->SetScalarParameterValue(LayerPartitionDynMat, "PatchFullSize", CollisionResolution* (CollisionVerticesPerPatch - 1));
 | 
						||
						SWorldSubsystem->SetScalarParameterValue(LayerPartitionDynMat, "TexelPerSide", CollisionVerticesPerPatch);
 | 
						||
						SWorldSubsystem->SetScalarParameterValue(LayerPartitionDynMat, "NoMargin", 1.f);
 | 
						||
 | 
						||
						SWorldSubsystem->SetScalarParameterValue(LayerPartitionDynMat, "MeshScale", CollisionResolution * (CollisionVerticesPerPatch <= 1 ? 1 : CollisionVerticesPerPatch));
 | 
						||
 | 
						||
					
 | 
						||
						SWorldSubsystem->SetScalarParameterValue(LayerPartitionDynMat, "N", CollisionVerticesPerPatch);
 | 
						||
						SWorldSubsystem->SetScalarParameterValue(LayerPartitionDynMat, "CacheRes", CollisionVerticesPerPatch);
 | 
						||
						SWorldSubsystem->SetScalarParameterValue(LayerPartitionDynMat, "LocalGridScaling", CollisionResolution);
 | 
						||
 | 
						||
						SWorldSubsystem->SetScalarParameterValue(LayerPartitionDynMat, "NormalMapSelect", 1.f);						
 | 
						||
					}
 | 
						||
 | 
						||
					if (LayerStoringMaterialID == Layer.LayerName)
 | 
						||
						break;
 | 
						||
				}
 | 
						||
			}
 | 
						||
 | 
						||
			if (bUseMaterialCollection)
 | 
						||
			{
 | 
						||
				if (UShaderWorld_Material_Collection* MC = GetValid(MaterialCollection))
 | 
						||
				{
 | 
						||
					for (auto& IDMat : MC->MaterialNameToID)
 | 
						||
					{
 | 
						||
						SWorldSubsystem->SetScalarParameterValue(DynCollisionMat, FName(IDMat.Key), IDMat.Value);
 | 
						||
 | 
						||
						for (auto& CollisionLayerC : Mesh.LayerComputeForPhysicalMaterial)
 | 
						||
						{
 | 
						||
							if (CollisionLayerC.Value.DynMat)
 | 
						||
								SWorldSubsystem->SetScalarParameterValue(CollisionLayerC.Value.DynMat, FName(IDMat.Key), IDMat.Value);
 | 
						||
						}
 | 
						||
					}
 | 
						||
				}
 | 
						||
			}
 | 
						||
			else
 | 
						||
			{
 | 
						||
				for (auto& IDMat : MaterialNameToID)
 | 
						||
				{
 | 
						||
					SWorldSubsystem->SetScalarParameterValue(DynCollisionMat, FName(IDMat.Key), IDMat.Value);
 | 
						||
 | 
						||
					for (auto& CollisionLayerC : Mesh.LayerComputeForPhysicalMaterial)
 | 
						||
					{
 | 
						||
						if (CollisionLayerC.Value.DynMat)
 | 
						||
							SWorldSubsystem->SetScalarParameterValue(CollisionLayerC.Value.DynMat, FName(IDMat.Key), IDMat.Value);
 | 
						||
					}
 | 
						||
				}
 | 
						||
			}
 | 
						||
 | 
						||
			TSet<FName> UsedNames;
 | 
						||
			for (FInstancedStruct& Seed : CurrentSeedsArray.SeedsArray)
 | 
						||
			{
 | 
						||
				if (Seed.IsValid())
 | 
						||
				{
 | 
						||
					const UScriptStruct* Type = Seed.GetScriptStruct();
 | 
						||
					CA_ASSUME(Type);
 | 
						||
					if (Type->IsChildOf(FTextureSeed::StaticStruct()))
 | 
						||
					{
 | 
						||
						FTextureSeed& TypedSeed = Seed.GetMutable<FTextureSeed>();
 | 
						||
						if (!UsedNames.Contains(TypedSeed.SeedName))
 | 
						||
						{
 | 
						||
							UsedNames.Add(TypedSeed.SeedName);
 | 
						||
							SWorldSubsystem->SetTextureParameterValue(Mesh.DynCollisionCompute, TypedSeed.SeedName, TypedSeed.Value);
 | 
						||
 | 
						||
							for (auto& LayerEl : Mesh.LayerComputeForPhysicalMaterial)
 | 
						||
							{
 | 
						||
								SWorldSubsystem->SetTextureParameterValue(LayerEl.Value.DynMat, TypedSeed.SeedName, TypedSeed.Value);
 | 
						||
							}
 | 
						||
						}
 | 
						||
					}
 | 
						||
					else if (Type->IsChildOf(FLinearColorSeed::StaticStruct()))
 | 
						||
					{
 | 
						||
						FLinearColorSeed& TypedSeed = Seed.GetMutable<FLinearColorSeed>();
 | 
						||
						if (!UsedNames.Contains(TypedSeed.SeedName))
 | 
						||
						{
 | 
						||
							UsedNames.Add(TypedSeed.SeedName);
 | 
						||
							SWorldSubsystem->SetVectorParameterValue(Mesh.DynCollisionCompute, TypedSeed.SeedName, TypedSeed.Value);
 | 
						||
 | 
						||
							for (auto& LayerEl : Mesh.LayerComputeForPhysicalMaterial)
 | 
						||
							{
 | 
						||
								SWorldSubsystem->SetVectorParameterValue(LayerEl.Value.DynMat, TypedSeed.SeedName, TypedSeed.Value);
 | 
						||
							}
 | 
						||
						}
 | 
						||
					}
 | 
						||
					else if (Type->IsChildOf(FScalarSeed::StaticStruct()))
 | 
						||
					{
 | 
						||
						FScalarSeed& TypedSeed = Seed.GetMutable<FScalarSeed>();
 | 
						||
						if (!UsedNames.Contains(TypedSeed.SeedName))
 | 
						||
						{
 | 
						||
							UsedNames.Add(TypedSeed.SeedName);
 | 
						||
							SWorldSubsystem->SetScalarParameterValue(Mesh.DynCollisionCompute, TypedSeed.SeedName, TypedSeed.Value);
 | 
						||
 | 
						||
							for (auto& LayerEl : Mesh.LayerComputeForPhysicalMaterial)
 | 
						||
							{
 | 
						||
								SWorldSubsystem->SetScalarParameterValue(LayerEl.Value.DynMat, TypedSeed.SeedName, TypedSeed.Value);
 | 
						||
							}
 | 
						||
						}
 | 
						||
					}
 | 
						||
					else
 | 
						||
					{
 | 
						||
#if SWDEBUG
 | 
						||
						SW_LOG("Invalid Seed type found: '%s'", *GetPathNameSafe(Type));
 | 
						||
#endif
 | 
						||
					}
 | 
						||
				}
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		SWorldSubsystem->SetVectorParameterValue(DynCollisionMat,"PatchLocation", MesgLoc);
 | 
						||
 | 
						||
		//UKismetRenderingLibrary::ClearRenderTarget2D(this, Mesh.CollisionRT, FLinearColor::Black);
 | 
						||
 | 
						||
#if SW_COMPUTE_GENERATION
 | 
						||
		//使用CS计算碰撞数据,最后写入FCollisionMeshElement.CollisionRT
 | 
						||
		SWorldSubsystem->DrawMaterialToRenderTarget(
 | 
						||
			{ false,
 | 
						||
				 false,
 | 
						||
				GetWorld()->Scene,
 | 
						||
				(float)GetWorld()->TimeSeconds,
 | 
						||
				false,
 | 
						||
				true,
 | 
						||
				Mesh.CollisionRT->SizeX,
 | 
						||
				(int32)(CollisionVerticesPerPatch - 1) * CollisionResolution,
 | 
						||
				FVector(MesgLoc),
 | 
						||
				FIntPoint(0),
 | 
						||
				FIntPoint(Mesh.CollisionVerticesPerPatch,Mesh.CollisionVerticesPerPatch),
 | 
						||
				false,
 | 
						||
				ReadRequestLocation,
 | 
						||
				DynCollisionMat,
 | 
						||
				 Mesh.CollisionRT
 | 
						||
			});
 | 
						||
#else
 | 
						||
		UKismetRenderingLibrary::DrawMaterialToRenderTarget(this, Mesh.CollisionRT, DynCollisionMat);
 | 
						||
#endif
 | 
						||
 | 
						||
		if (IsValid(BrushManager))
 | 
						||
			BrushManager->ApplyBrushStackToHeightMap(this, 0, Mesh.CollisionRT, Mesh.CollisionVerticesPerPatch, MesgLoc, CollisionResolution, CollisionVerticesPerPatch, true /*Collision heightmap have no border*/);
 | 
						||
 | 
						||
		//输出物理材质ID缓存,bExportPhysicalMaterialID_cached默认为false。
 | 
						||
		if (bExportPhysicalMaterialID_cached && (LayerStoringMaterialID != ""))
 | 
						||
		{
 | 
						||
			{
 | 
						||
				int32 DrawCallCount = 0;
 | 
						||
				TArray<UShaderWorldRT2D*> PreviousLayer;
 | 
						||
				PreviousLayer.Reserve(Mesh.LayerOrder.Num());
 | 
						||
 | 
						||
				int32 LayerIndice = 0;
 | 
						||
 | 
						||
				for (int k = 0; k < Mesh.LayerOrder.Num(); k++)
 | 
						||
				{
 | 
						||
					FString LayerName = Mesh.LayerOrder[k];
 | 
						||
 | 
						||
					check(Mesh.LayerComputeForPhysicalMaterial.Contains(LayerName))
 | 
						||
 | 
						||
					FSWCollisionLayerData& CLayerData = *Mesh.LayerComputeForPhysicalMaterial.Find(LayerName);
 | 
						||
 | 
						||
					if (!CLayerData.DynMat)
 | 
						||
					{
 | 
						||
						UE_LOG(LogTemp, Warning, TEXT("ERROR drawing layers for Physical material: !CLayerData.DynMat[%d]"), k);
 | 
						||
						continue;
 | 
						||
					}
 | 
						||
 | 
						||
					if (UShaderWorldRT2D* Layer_RT_To_Produce = CLayerData.RT)
 | 
						||
					{
 | 
						||
						SWorldSubsystem->SetVectorParameterValue(CLayerData.DynMat, "PatchLocation", MesgLoc);
 | 
						||
						SWorldSubsystem->SetTextureParameterValue(CLayerData.DynMat, "HeightMap", Mesh.CollisionRT);
 | 
						||
						SWorldSubsystem->SetTextureParameterValue(CLayerData.DynMat, "NormalMap", Mesh.CollisionRT);//#TODO Support normal map for collision ?
 | 
						||
						for (int u = 0; u < LayerIndice; u++)
 | 
						||
						{
 | 
						||
							SWorldSubsystem->SetTextureParameterValue(CLayerData.DynMat, FName(*Mesh.LayerOrder[u]), PreviousLayer[u]);
 | 
						||
						}
 | 
						||
#if SW_COMPUTE_GENERATION
 | 
						||
						SWorldSubsystem->DrawMaterialToRenderTarget(
 | 
						||
							{ false,
 | 
						||
								 false,
 | 
						||
								GetWorld()->Scene,
 | 
						||
								(float)GetWorld()->TimeSeconds,
 | 
						||
								false,
 | 
						||
								true,
 | 
						||
								Layer_RT_To_Produce->SizeX,
 | 
						||
								(int32)(CollisionVerticesPerPatch - 1) * CollisionResolution,
 | 
						||
								MesgLoc,
 | 
						||
								FIntPoint(0),
 | 
						||
				FIntPoint(Mesh.CollisionVerticesPerPatch,Mesh.CollisionVerticesPerPatch),
 | 
						||
								false,
 | 
						||
								ReadRequestLocation,
 | 
						||
								 CLayerData.DynMat,
 | 
						||
								Layer_RT_To_Produce,
 | 
						||
								Mesh.CollisionRT,
 | 
						||
								Mesh.CollisionRT//#TODO Support normal map for collision ?
 | 
						||
							});
 | 
						||
#else
 | 
						||
						UKismetRenderingLibrary::DrawMaterialToRenderTarget(this, Layer_RT_To_Produce, Elem.LayerPartitionMatDyn[k]);
 | 
						||
#endif
 | 
						||
						DrawCallCount++;
 | 
						||
						
 | 
						||
 | 
						||
						if (IsValid(BrushManager))
 | 
						||
							DrawCallCount += BrushManager->ApplyBrushStackToLayer(this, 0, Layer_RT_To_Produce, Mesh.CollisionVerticesPerPatch, MesgLoc, CollisionResolution, CollisionVerticesPerPatch, LayerName);
 | 
						||
 | 
						||
						PreviousLayer.Add(Layer_RT_To_Produce);
 | 
						||
						LayerIndice++;
 | 
						||
 | 
						||
						if (LayerStoringMaterialID == LayerName)
 | 
						||
						{
 | 
						||
							float PatchSize = (CollisionVerticesPerPatch - 1) * CollisionResolution;
 | 
						||
 | 
						||
							SWorldSubsystem->CopyAtoB(Mesh.CollisionRT, (*CollisionMesh.Find(CollisionBufferHolder)).CollisionRT_Duplicate);
 | 
						||
 | 
						||
							uint8 channel = (static_cast<uint8>(LayerChannelStoringID)) + 1;
 | 
						||
 | 
						||
							SWorldSubsystem->CopyAtoB(Layer_RT_To_Produce, Mesh.CollisionRT, (*CollisionMesh.Find(CollisionBufferHolder)).CollisionRT_Duplicate, 0, channel, FVector2D(MesgLoc), FVector2D(MesgLoc), PatchSize, PatchSize, SWCopyConfig::AlphaChannelCombine);
 | 
						||
 | 
						||
							break;
 | 
						||
						}
 | 
						||
					}
 | 
						||
				}
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		if (false && bExportPhysicalMaterialID_cached && (LayerStoringMaterialID != "") && GetMeshNum() > 0 && CollisionMesh.Num() > 0 && (*CollisionMesh.Find(CollisionBufferHolder)).CollisionRT_Duplicate)
 | 
						||
		{
 | 
						||
 | 
						||
 | 
						||
			if (USWorldSubsystem* ShaderWorldSubsystem = SWorldSubsystem)
 | 
						||
			{
 | 
						||
 | 
						||
				bool LayerIsRelevant = false;
 | 
						||
				FString LayerSource = LayerStoringMaterialID.ToString();
 | 
						||
 | 
						||
				for (auto& La : Producers)
 | 
						||
				{
 | 
						||
					if (La.LayerName == "Height" || La.LayerName == "height")
 | 
						||
						continue;
 | 
						||
 | 
						||
					if (La.LayerName == LayerSource)
 | 
						||
					{
 | 
						||
						LayerIsRelevant = true;
 | 
						||
						break;
 | 
						||
					}
 | 
						||
				}
 | 
						||
 | 
						||
				if (LayerIsRelevant)
 | 
						||
				{
 | 
						||
					FVector2D Location_Mesh(MesgLoc);
 | 
						||
					FVector2D Extent = CollisionResolution * (CollisionVerticesPerPatch - 1) / 2.f * FVector2D(1.f, 1.f);//Margin
 | 
						||
					FBox2D LocalMeshBox(Location_Mesh - Extent, Location_Mesh + Extent);
 | 
						||
 | 
						||
					int LOD_Candidate = -1;
 | 
						||
					///////////////////////////////
 | 
						||
 | 
						||
					for (int k = 0; k < GetMeshNum(); k++)
 | 
						||
					{
 | 
						||
						FClipMapMeshElement& Elem_Local = GetMesh(k);
 | 
						||
						FIntVector ClipMapLocation = Elem_Local.Location - GetWorld()->OriginLocation;
 | 
						||
 | 
						||
						FVector2D Location_Elem_Local(ClipMapLocation.X, ClipMapLocation.Y);
 | 
						||
						FVector2D Extent_Elem_Local = Elem_Local.GridSpacing * ((Elem_Local.N - 1) / 2) * FVector2D(1.f, 1.f);
 | 
						||
						FBox2D Elem_Local_Footprint(Location_Elem_Local - Extent_Elem_Local, Location_Elem_Local + Extent_Elem_Local);
 | 
						||
 | 
						||
						if (Elem_Local_Footprint.IsInside(LocalMeshBox) && (Elem_Local.IsSectionVisible(0) || Elem_Local.IsSectionVisible(1)))
 | 
						||
						{
 | 
						||
							LOD_Candidate = k;
 | 
						||
						}
 | 
						||
						else
 | 
						||
						{
 | 
						||
							break;
 | 
						||
						}
 | 
						||
					}
 | 
						||
 | 
						||
					if (LOD_Candidate >= 0)
 | 
						||
					{
 | 
						||
						FClipMapMeshElement& Elem_Local = GetMesh(LOD_Candidate);
 | 
						||
						float PatchSize = (Elem_Local.N - 1) * Elem_Local.GridSpacing;
 | 
						||
 | 
						||
						for (int k = 0; k < Elem_Local.LandLayers.Num(); k++)
 | 
						||
						{
 | 
						||
							if (Elem_Local.LandLayers_names[k] == LayerStoringMaterialID)
 | 
						||
							{
 | 
						||
								ShaderWorldSubsystem->CopyAtoB(Mesh.CollisionRT, (*CollisionMesh.Find(CollisionBufferHolder)).CollisionRT_Duplicate);
 | 
						||
 | 
						||
								uint8 channel = (static_cast<uint8>(LayerChannelStoringID)) + 1;
 | 
						||
 | 
						||
								ShaderWorldSubsystem->CopyAtoB(Elem_Local.LandLayers[k], Mesh.CollisionRT, (*CollisionMesh.Find(CollisionBufferHolder)).CollisionRT_Duplicate, 0, channel, FVector2D(FVector(Elem_Local.Location)), FVector2D(MesgLoc), PatchSize, CollisionResolution * (CollisionVerticesPerPatch - 1), SWCopyConfig::AlphaChannelCombine);
 | 
						||
 | 
						||
								break;
 | 
						||
							}
 | 
						||
						}
 | 
						||
 | 
						||
					}
 | 
						||
					else
 | 
						||
					{
 | 
						||
						//UE_LOG(LogTemp, Warning, TEXT("Non Admissible LOD_Candidate %d"), LOD_Candidate);
 | 
						||
					}
 | 
						||
				}
 | 
						||
				else
 | 
						||
				{
 | 
						||
					UE_LOG(LogTemp, Warning, TEXT("No relevant data layer"));
 | 
						||
				}
 | 
						||
			}
 | 
						||
 | 
						||
		}
 | 
						||
 | 
						||
		if (!Mesh.HeightData.IsValid())
 | 
						||
		{
 | 
						||
			Mesh.HeightData = MakeShared<FSWColorRead, ESPMode::ThreadSafe>();
 | 
						||
			Mesh.HeightData->ReadData.SetNum(CollisionVerticesPerPatch * CollisionVerticesPerPatch);
 | 
						||
		}
 | 
						||
 | 
						||
		if (!Mesh.ReadBackCompletion.IsValid())
 | 
						||
		{
 | 
						||
			Mesh.ReadBackCompletion = MakeShared<FThreadSafeBool, ESPMode::ThreadSafe>();
 | 
						||
		}
 | 
						||
 | 
						||
		//设置 碰撞GPU数据回读完成 变量为false
 | 
						||
		Mesh.ReadBackCompletion->AtomicSet(false);
 | 
						||
 | 
						||
 | 
						||
		//ShaderWorld::ReadPixelsFromRT(Mesh.CollisionRT, Mesh);
 | 
						||
		//ShaderWorld::AsyncReadPixelsFromRT(Mesh.CollisionRT, Mesh.HeightData, Mesh.ReadBackCompletion);
 | 
						||
		//从FCollisionMeshElement.CollisionRT读取数据并且写入FCollisionMeshElement.HeightData。
 | 
						||
		SWorldSubsystem->AsyncReadPixelsFromRT(Mesh.CollisionRT, Mesh.HeightData, Mesh.ReadBackCompletion);
 | 
						||
 | 
						||
		//将ID添加到CollisionReadToProcess 碰撞数据回读队列,
 | 
						||
		CollisionReadToProcess.Add(Mesh.ID);
 | 
						||
 | 
						||
		return;
 | 
						||
	}
 | 
						||
 | 
						||
	return;
 | 
						||
}
 | 
						||
 | 
						||
```
 | 
						||
#### CollisionCPU
 | 
						||
```c++
 | 
						||
 | 
						||
void AShaderWorldActor::CollisionCPU()
 | 
						||
{
 | 
						||
	SCOPED_NAMED_EVENT_TEXT("AShaderWorldActor::CollisionCPU()", FColor::Magenta);
 | 
						||
	SW_FCT_CYCLE()
 | 
						||
 | 
						||
		if ((*bPreprocessingCollisionUpdate.Get()) ||
 | 
						||
			CollisionShareable->CollisionMeshToUpdate.Num() > 0 ||
 | 
						||
			CollisionShareable->CollisionMeshToRenameMoveUpdate.Num() > 0 ||
 | 
						||
			CollisionShareable->CollisionMeshToCreate.Num() > 0)
 | 
						||
			return;
 | 
						||
 | 
						||
 | 
						||
	UWorld* World = GetWorld();
 | 
						||
 | 
						||
	//SW Brush相关
 | 
						||
	bool bBrushManagerAskedRedraw = BrushManager && (BrushManagerRedrawScopes_collision.Num() > 0);
 | 
						||
	FVector LocalActorLocation = GetActorLocation();
 | 
						||
	FIntVector LocalOriginLocation = GetWorld()->OriginLocation;
 | 
						||
 | 
						||
	TArray<FBox2D> BrushRedrawScope;
 | 
						||
 | 
						||
	if (bBrushManagerAskedRedraw)
 | 
						||
	{
 | 
						||
		BrushRedrawScope = MoveTemp(BrushManagerRedrawScopes_collision);
 | 
						||
		BrushManagerRedrawScopes_collision.Empty();
 | 
						||
	}
 | 
						||
 | 
						||
 | 
						||
	//通过AShaderWorldActor::UpdateCollisionInRegion()来添加碰撞重建区域
 | 
						||
	bool bExternalCollisionRebuildRequest = !CollisionUpdateRequest.IsEmpty();
 | 
						||
	FBox2D BPCollisionRebuildRequest(ForceInit);
 | 
						||
	//从CollisionUpdateRequest队列中取出碰撞更新范围
 | 
						||
	if (bExternalCollisionRebuildRequest)
 | 
						||
		CollisionUpdateRequest.Dequeue(BPCollisionRebuildRequest);
 | 
						||
 | 
						||
	TArray<ShaderWorld::FVisitor> VisitorLocations;
 | 
						||
 | 
						||
	// Use latest camera we know if current array is empty
 | 
						||
	//在BeginPlay()会将ShaderWorld Actor的Location添加到CameraLocations中。
 | 
						||
	//在UpdateCameraLocation()中会将,这个函数会在Setup()中调用。
 | 
						||
	// USWorldSubsystem::Tick()中会调用UpdateVisitors()获取所有USW_CollisionComponent。
 | 
						||
	if (CameraLocations.Num() <= 0)
 | 
						||
		VisitorLocations.Add(CamLocation);
 | 
						||
 | 
						||
	VisitorLocations.Append(CameraLocations);
 | 
						||
	//将bPreprocessingCollisionUpdate线程安全变量设置为true
 | 
						||
	(*bPreprocessingCollisionUpdate.Get()) = true;
 | 
						||
 | 
						||
	Async(EAsyncExecution::TaskGraph, [Completion = bPreprocessingCollisionUpdate, CollData = CollisionShareable,
 | 
						||
		VisitorLocations, bBrushManagerAskedRedraw, BrushScope = MoveTemp(BrushRedrawScope), bExternalCollisionRebuildRequest,
 | 
						||
		BPRecomputeScope = BPCollisionRebuildRequest, ColRingCount = CollisionGridMaxRingNumber, LocalActorLocation, LocalOriginLocation,
 | 
						||
		Height0 = HeightOnStart,  Bounded = WorldHasBounds_OnRebuild, GBounds = WorldBoundsOnRebuild, Lock = CollisionMeshAccessLock]
 | 
						||
		{
 | 
						||
			//CollisionShareable无效则返回
 | 
						||
			if (!CollData.IsValid())
 | 
						||
			{
 | 
						||
				if (Completion.IsValid())
 | 
						||
					(*Completion.Get()) = false;
 | 
						||
				return;
 | 
						||
			}
 | 
						||
 | 
						||
			FScopeLock CollisionMeshArrayAccess(Lock.Get());
 | 
						||
			//Brush相关
 | 
						||
			double CollisionWidth = CollData->CollisionResolution * (CollData->VerticePerPatch - 1);
 | 
						||
			TMap<FIntVector, FVector> BrushRedraws;
 | 
						||
			for (const FBox2d& B : BrushScope)
 | 
						||
			{
 | 
						||
				const int32 MinBoxX_local = FMath::FloorToInt(((double)(B.Min.X + LocalOriginLocation.X)) / CollisionWidth + 0.45);
 | 
						||
				const int32 MinBoxY_local = FMath::FloorToInt(((double)(B.Min.Y + LocalOriginLocation.Y)) / CollisionWidth + 0.45);
 | 
						||
 | 
						||
				const int32 MaxBoxX_local = FMath::FloorToInt(((double)(B.Max.X + LocalOriginLocation.X)) / CollisionWidth + 0.55);
 | 
						||
				const int32 MaxBoxY_local = FMath::FloorToInt(((double)(B.Max.Y + LocalOriginLocation.Y)) / CollisionWidth + 0.55);
 | 
						||
 | 
						||
				for (int32 X_iter = MinBoxX_local; X_iter <= MaxBoxX_local; X_iter++)
 | 
						||
				{
 | 
						||
					for (int32 Y_iter = MinBoxY_local; Y_iter <= MaxBoxY_local; Y_iter++)
 | 
						||
					{
 | 
						||
						BrushRedraws.Add(FIntVector(X_iter, Y_iter, 0));
 | 
						||
					}
 | 
						||
				}
 | 
						||
			}
 | 
						||
 | 
						||
			/*
 | 
						||
			 * Get World Bounds in centimeters
 | 
						||
			 */
 | 
						||
			FBox SWorldBounds = GBounds;
 | 
						||
			SWorldBounds.Min *= 100.0;
 | 
						||
			SWorldBounds.Max *= 100.0;
 | 
						||
 | 
						||
			TSet<FIntVector> ExternalCollisionsBounds;
 | 
						||
			for (const ShaderWorld::FVisitor& SingleCamLoc : VisitorLocations)
 | 
						||
			{
 | 
						||
				if (SingleCamLoc.Bounds.IsValid)
 | 
						||
				{
 | 
						||
					ExternalCollisionsBounds.Append(USWBlueprintFunctionLibrary::GetCollisionMeshesLayout(CollisionWidth, SingleCamLoc.Bounds, SWorldBounds));
 | 
						||
				}
 | 
						||
			}
 | 
						||
 | 
						||
			CollData->MultipleCamera.Empty();
 | 
						||
			CollData->LocRefs.Empty();
 | 
						||
 | 
						||
			//遍历所有Visitor,之后往CollisionShareable中的LocRefs、MultipleCamera添加计算结果。
 | 
						||
			for (const ShaderWorld::FVisitor& SingleCamLoc : VisitorLocations)
 | 
						||
			{			
 | 
						||
				if (SingleCamLoc.Bounds.IsValid)
 | 
						||
					continue;
 | 
						||
 | 
						||
				double Cam_X_local = SingleCamLoc.Location.X + LocalOriginLocation.X;
 | 
						||
				double Cam_Y_local = SingleCamLoc.Location.Y + LocalOriginLocation.Y;
 | 
						||
 | 
						||
				if (Bounded)
 | 
						||
				{
 | 
						||
					Cam_X_local = FMath::Max(FMath::Min(Cam_X_local, GBounds.Max.X * 100.0), GBounds.Min.X * 100.0);
 | 
						||
					Cam_Y_local = FMath::Max(FMath::Min(Cam_Y_local, GBounds.Max.Y * 100.0), GBounds.Min.Y * 100.0);					
 | 
						||
				}
 | 
						||
 | 
						||
				const int CamX_local = FMath::RoundToInt(Cam_X_local / CollisionWidth);
 | 
						||
				const int CamY_local = FMath::RoundToInt(Cam_Y_local / CollisionWidth);
 | 
						||
 | 
						||
				FIntVector LocRef_local = FIntVector(CamX_local, CamY_local, 0.f) * CollisionWidth + FIntVector(0.f, 0.f, 1) * Height0 - LocalOriginLocation;
 | 
						||
 | 
						||
 | 
						||
				int32 LocalRingCount = FMath::Clamp(FMath::Floor(FMath::Abs(SingleCamLoc.Range * 100.0) / CollisionWidth), 1, ColRingCount);
 | 
						||
 | 
						||
				CollData->LocRefs.Add(LocRef_local, LocalRingCount);
 | 
						||
				CollData->MultipleCamera.Add(FIntVector(CamX_local, CamY_local, 0), LocalRingCount);
 | 
						||
			}
 | 
						||
 | 
						||
			TArray<FName> NameToRemove;
 | 
						||
			for (const FName& CollID : CollData->UsedCollisionMesh)
 | 
						||
			{
 | 
						||
				FSWCollisionMeshElemData& El = *CollData->CollisionMeshData.Find(CollID);
 | 
						||
 | 
						||
				bool BeyondCriteria_local = !ExternalCollisionsBounds.Contains(El.Location);
 | 
						||
 | 
						||
 | 
						||
				for (auto& Elem : CollData->LocRefs)
 | 
						||
				{
 | 
						||
					FIntVector& SingleLocRef = Elem.Key;
 | 
						||
					int32& LocalRingCount = Elem.Value;
 | 
						||
 | 
						||
					const FVector ToCompLocal = FVector(FIntVector(El.MeshLocation) - SingleLocRef) / CollisionWidth;
 | 
						||
					BeyondCriteria_local = BeyondCriteria_local && (FMath::Abs(ToCompLocal.X) > LocalRingCount + .1f || FMath::Abs(ToCompLocal.Y) > LocalRingCount + .1f);
 | 
						||
					if (!BeyondCriteria_local)
 | 
						||
						break;
 | 
						||
				}
 | 
						||
 | 
						||
 | 
						||
				if (BeyondCriteria_local)
 | 
						||
				{					
 | 
						||
					CollData->AvailableCollisionMesh.Add(CollID);
 | 
						||
					NameToRemove.Add(CollID);
 | 
						||
 | 
						||
					for (auto It = CollData->GroundCollisionLayout.CreateConstIterator(); It; ++It)
 | 
						||
					{
 | 
						||
						if(It->Value == CollID)
 | 
						||
						{
 | 
						||
							CollData->GroundCollisionLayout.Remove(It->Key);
 | 
						||
							break;
 | 
						||
						}
 | 
						||
					}
 | 
						||
				}
 | 
						||
				else
 | 
						||
				{
 | 
						||
 | 
						||
					if (bBrushManagerAskedRedraw || bExternalCollisionRebuildRequest)
 | 
						||
					{
 | 
						||
 | 
						||
						bool UpdateRequested = false;
 | 
						||
						if (bBrushManagerAskedRedraw)
 | 
						||
						{
 | 
						||
							if (BrushRedraws.Contains(El.Location))
 | 
						||
							{
 | 
						||
								CollData->CollisionMeshToUpdate.Add(CollID);
 | 
						||
								UpdateRequested = true;
 | 
						||
							}
 | 
						||
						}
 | 
						||
 | 
						||
						if (!UpdateRequested && bExternalCollisionRebuildRequest)
 | 
						||
						{
 | 
						||
							const FVector Location_LocalOrigin = FVector(El.Location * CollisionWidth + FIntVector(0, 0, 1) * LocalActorLocation.Z - LocalOriginLocation);
 | 
						||
 | 
						||
							FVector2D Location_Mesh(Location_LocalOrigin.X, Location_LocalOrigin.Y);
 | 
						||
							FVector2D Extent = CollisionWidth / 2.f * FVector2D(1.f, 1.f);
 | 
						||
							FBox2D LocalCollisionMeshBox(Location_Mesh - Extent, Location_Mesh + Extent);
 | 
						||
 | 
						||
							if (BPRecomputeScope.Intersect(LocalCollisionMeshBox))
 | 
						||
							{
 | 
						||
								CollData->CollisionMeshToUpdate.Add(CollID);
 | 
						||
							}
 | 
						||
						}
 | 
						||
					}
 | 
						||
 | 
						||
				}
 | 
						||
 | 
						||
			}
 | 
						||
 | 
						||
			for (const FName& TR : NameToRemove)
 | 
						||
			{
 | 
						||
				CollData->UsedCollisionMesh.Remove(TR);
 | 
						||
			}
 | 
						||
 | 
						||
			BrushRedraws.Empty();
 | 
						||
 | 
						||
			FBox WorldBounds = GBounds;
 | 
						||
			WorldBounds.Min *= 100.0;
 | 
						||
			WorldBounds.Max *= 100.0;
 | 
						||
 | 
						||
			//根据ShaderWorld边界,来维护UsedCollisionMesh、CollisionMeshData、CollisionMeshToCreate、CollisionMeshToRenameMoveUpdate、GroundCollisionLayout
 | 
						||
			for (auto& ExternalBounds : ExternalCollisionsBounds)
 | 
						||
			{
 | 
						||
				FIntVector LocMeshInt = ExternalBounds;
 | 
						||
				FIntVector MeshLoc = LocMeshInt * CollisionWidth + FIntVector(0.f, 0.f, 1) * Height0 - LocalOriginLocation;
 | 
						||
 | 
						||
				if (Bounded && !WorldBounds.IntersectXY(FBox((FVector(LocMeshInt) - FVector(0.5, 0.5, 0.0)) * CollisionWidth, (FVector(LocMeshInt) + FVector(0.5, 0.5, 0.0)) * CollisionWidth)))
 | 
						||
					continue;
 | 
						||
 | 
						||
				if (!CollData->GroundCollisionLayout.Contains(LocMeshInt))
 | 
						||
				{
 | 
						||
					const FString CollisionMeshString = "SW_Collision_X_" + FString::FromInt(LocMeshInt.X) + "_Y_" + FString::FromInt(LocMeshInt.Y) + "_Z_" + FString::FromInt(LocMeshInt.Z);
 | 
						||
 | 
						||
					const FName CollisionMeshName = FName(*CollisionMeshString);
 | 
						||
 | 
						||
					if (CollData->AvailableCollisionMesh.Num() > 0 && CollData->AvailableCollisionMesh.Contains(CollisionMeshName))
 | 
						||
					{
 | 
						||
						FSWCollisionMeshElemData& ElemData = *CollData->CollisionMeshData.Find(CollisionMeshName);
 | 
						||
						CollData->UsedCollisionMesh.Add(CollisionMeshName);
 | 
						||
						CollData->AvailableCollisionMesh.Remove(CollisionMeshName);
 | 
						||
 | 
						||
						ElemData.Location = LocMeshInt;
 | 
						||
						ElemData.MeshLocation = FVector(MeshLoc);
 | 
						||
 | 
						||
						CollData->CollisionMeshToRenameMoveUpdate.AddUnique(CollisionMeshName);
 | 
						||
 | 
						||
						CollData->GroundCollisionLayout.Add(LocMeshInt, CollisionMeshName);
 | 
						||
					}
 | 
						||
					else
 | 
						||
					{
 | 
						||
						FCollisionMeshElement NewElem;
 | 
						||
						NewElem.ID = CollisionMeshName;
 | 
						||
 | 
						||
						NewElem.Location = LocMeshInt;
 | 
						||
						NewElem.MeshLocation = FVector(MeshLoc);
 | 
						||
 | 
						||
						CollData->UsedCollisionMesh.Add(CollisionMeshName);
 | 
						||
						CollData->CollisionMeshData.Add(CollisionMeshName, NewElem);
 | 
						||
 | 
						||
						CollData->CollisionMeshToCreate.AddUnique(CollisionMeshName);
 | 
						||
						CollData->CollisionMeshToRenameMoveUpdate.AddUnique(CollisionMeshName);
 | 
						||
 | 
						||
						CollData->GroundCollisionLayout.Add(LocMeshInt, CollisionMeshName);
 | 
						||
					}
 | 
						||
				}
 | 
						||
			}
 | 
						||
 | 
						||
			//根据ShaderWorld MultipleCamera,来维护UsedCollisionMesh、CollisionMeshData、CollisionMeshToCreate、CollisionMeshToRenameMoveUpdate、GroundCollisionLayout
 | 
						||
			for (auto& Elem : CollData->MultipleCamera)
 | 
						||
			{
 | 
						||
				FIntVector& SingleCam = Elem.Key;
 | 
						||
				int32& LocalRingCount = Elem.Value;
 | 
						||
 | 
						||
				for (int r = LocalRingCount; r >= 0; r--)
 | 
						||
				{
 | 
						||
					for (int i = -r; i <= r; i++)
 | 
						||
					{
 | 
						||
						for (int j = -r; j <= r; j++)
 | 
						||
						{
 | 
						||
							if (abs(j) != r && abs(i) != r)
 | 
						||
								continue;
 | 
						||
 | 
						||
							FIntVector LocMeshInt = FIntVector(SingleCam.X + i, SingleCam.Y + j, 0);
 | 
						||
							FIntVector MeshLoc = LocMeshInt * CollisionWidth + FIntVector(0.f, 0.f, 1) * Height0 - LocalOriginLocation;
 | 
						||
 | 
						||
							if (Bounded && !WorldBounds.IntersectXY(FBox((FVector(LocMeshInt) - FVector(0.5, 0.5, 0.0)) * CollisionWidth, (FVector(LocMeshInt) + FVector(0.5, 0.5, 0.0)) * CollisionWidth)))
 | 
						||
								continue;
 | 
						||
 | 
						||
							if (!CollData->GroundCollisionLayout.Contains(LocMeshInt))
 | 
						||
							{							
 | 
						||
								const FString CollisionMeshString = "SW_Collision_X_" + FString::FromInt(LocMeshInt.X) + "_Y_" + FString::FromInt(LocMeshInt.Y) + "_Z_" + FString::FromInt(LocMeshInt.Z);
 | 
						||
 | 
						||
								const FName CollisionMeshName = FName(*CollisionMeshString);
 | 
						||
 | 
						||
								if(CollData->AvailableCollisionMesh.Num() > 0 && CollData->AvailableCollisionMesh.Contains(CollisionMeshName))
 | 
						||
								{
 | 
						||
									FSWCollisionMeshElemData& ElemData = *CollData->CollisionMeshData.Find(CollisionMeshName);
 | 
						||
									CollData->UsedCollisionMesh.Add(CollisionMeshName);
 | 
						||
									CollData->AvailableCollisionMesh.Remove(CollisionMeshName);
 | 
						||
 | 
						||
									ElemData.Location = LocMeshInt;
 | 
						||
									ElemData.MeshLocation = FVector(MeshLoc);
 | 
						||
 | 
						||
									CollData->CollisionMeshToRenameMoveUpdate.AddUnique(CollisionMeshName);
 | 
						||
 | 
						||
									CollData->GroundCollisionLayout.Add(LocMeshInt, CollisionMeshName);
 | 
						||
								}
 | 
						||
								else
 | 
						||
								{
 | 
						||
									FCollisionMeshElement NewElem;
 | 
						||
									NewElem.ID = CollisionMeshName;
 | 
						||
 | 
						||
									NewElem.Location = LocMeshInt;
 | 
						||
									NewElem.MeshLocation = FVector(MeshLoc);
 | 
						||
 | 
						||
									CollData->UsedCollisionMesh.Add(CollisionMeshName);
 | 
						||
									CollData->CollisionMeshData.Add(CollisionMeshName, NewElem);
 | 
						||
 | 
						||
									CollData->CollisionMeshToCreate.AddUnique(CollisionMeshName);
 | 
						||
									CollData->CollisionMeshToRenameMoveUpdate.AddUnique(CollisionMeshName);
 | 
						||
 | 
						||
									CollData->GroundCollisionLayout.Add(LocMeshInt, CollisionMeshName);
 | 
						||
								}								
 | 
						||
							}
 | 
						||
						}
 | 
						||
					}
 | 
						||
				}
 | 
						||
			}
 | 
						||
 | 
						||
			//开始下一轮循环,bPreprocessingCollisionUpdate 设置为false
 | 
						||
			if (Completion.IsValid())
 | 
						||
				(*Completion.Get()) = false;
 | 
						||
		}
 | 
						||
	);
 | 
						||
}
 | 
						||
```
 | 
						||
 | 
						||
### SpawnablesManagement()
 | 
						||
 | 
						||
### TerrainAndSpawnablesManagement()
 | 
						||
 | 
						||
## PreEditChange() / PostEditChangeProperty()
 | 
						||
**PreEditChange()** 主要针对以下两个变量的设置:
 | 
						||
- PreventReRegistration:防止重新注册。
 | 
						||
- RuntimePropertyEditing:实时属性修改。
 | 
						||
 | 
						||
**PostEditChangeProperty()** 
 | 
						||
里面比较关键的逻辑有:
 | 
						||
- EditRebuildVegetation.AtomicSet(true);
 | 
						||
	- 在Setup()中清空Bioms数组。
 | 
						||
- EditRebuild.AtomicSet(true);
 | 
						||
	- 在SetupCollisions()设置rebuild = true。
 | 
						||
 | 
						||
如果RuntimePropertyEditing为true,在最后会将RuntimePropertyEditing设置为false。PreventReRegistration也会设置为false。
 | 
						||
 | 
						||
## Rebuild变量
 | 
						||
主要出现在:
 | 
						||
- Setup():调用RebuildCleanup()清空所有数据。
 | 
						||
- SetupCollision(): RedbuildCollisionContext = true。
 | 
						||
- ProcessSpawnablePending():如果处于重建状态就直接返回。
 | 
						||
- InitiateClipMapMeshes():如果处于重建状态就直接返回。
 | 
						||
- FinalizeAsyncWork():如果处于重建状态就直接返回。
 | 
						||
 | 
						||
## DrawMaterialToRenderTarget
 | 
						||
USWorldSubsystem::DrawMaterialToRenderTarget
 | 
						||
=>
 | 
						||
SWShaderToolBox::DrawMaterial
 | 
						||
=>
 | 
						||
DrawMaterial_CS_RT
 | 
						||
 | 
						||
调用路径:
 | 
						||
- AShaderWorldActor::[[#RetrieveHeightAt]](好像没有引用):检索高度
 | 
						||
- AShaderWorldActor::ComputeHeight_Segmented_MapForClipMap
 | 
						||
	- AShaderWorldActor::ProcessSegmentedComputation() <- AShaderWorldActor::TerrainAndSpawnablesManagement() <- AShaderWorldActor::Tick()
 | 
						||
- AShaderWorldActor::ComputeHeightMapForClipMap
 | 
						||
	- AShaderWorldActor::UpdateClipMap() <- AShaderWorldActor::TerrainAndSpawnablesManagement() <- AShaderWorldActor::Tick()
 | 
						||
- AShaderWorldActor::ComputeDataLayersForClipMap
 | 
						||
	- AShaderWorldActor::UpdateClipMap() <- AShaderWorldActor::TerrainAndSpawnablesManagement() <- AShaderWorldActor::Tick()
 | 
						||
- AShaderWorldActor::UpdateCollisionMeshData:更新碰撞模型数据。
 | 
						||
	- AShaderWorldActor::CollisionGPU() <- AShaderWorldActor::CollisionManagement() <- AShaderWorldActor::Tick()
 | 
						||
- FSpawnableMesh::UpdateSpawnableData
 | 
						||
	- AShaderWorldActor::ProcessSegmentedComputation() <- AShaderWorldActor::TerrainAndSpawnablesManagement() <- AShaderWorldActor::Tick()
 | 
						||
 | 
						||
 | 
						||
## Cache机制
 | 
						||
AShaderWorldActor::ProcessSegmentedComputation() <- AShaderWorldActor::TerrainAndSpawnablesManagement() <- AShaderWorldActor::Tick()
 | 
						||
 | 
						||
 | 
						||
# 其他Bug
 | 
						||
 | 
						||
 | 
						||
## SetTextureParameterValue相关逻辑排查
 | 
						||
- AShaderWorldActor中的SetTextureParameterValue
 | 
						||
	- ~~ExportCacheInBounds~~
 | 
						||
	- ~~AssignHeightMapToDynamicMaterial~~
 | 
						||
	- UpdateStaticDataFor
 | 
						||
	- ComputeHeight_Segmented_MapForClipMap:似乎会设置
 | 
						||
	- ~~UpdateCollisionMeshData~~
 | 
						||
	- [x] [[#InitializeReadBackDependencies]]
 | 
						||
	- [x] InitiateMaterials
 | 
						||
 | 
						||
### UpdateStaticDataFor
 | 
						||
 | 
						||
 | 
						||
### ComputeHeight_Segmented_MapForClipMap
 | 
						||
- 作用:
 | 
						||
- 调用顺序:AShaderWorldActor::Tick() -> AShaderWorldActor::TerrainAndSpawnablesManagement() -> AShaderWorldActor::ProcessSegmentedComputation() -> ComputeHeight_Segmented_MapForClipMap
 | 
						||
 | 
						||
 | 
						||
>// 1) Intersect clipmap with grid quad  
 | 
						||
  // 2) Gather non computed quads   
 | 
						||
  // 3) Allocated Compute element to missing Quad   
 | 
						||
  // 4) Update the indirection data to the new elements  
 | 
						||
  // 5) Update the Clipmap Heightmap with the grid data
 | 
						||
 
 | 
						||
 | 
						||
### UpdateCollisionMeshData
 | 
						||
 - 作用:
 | 
						||
	 1. 判断DynCollisionMat是否有效,无效就使用`Generator`(高度数据生成材质)来创建。
 | 
						||
	 2. 设置材质参数NoMargin、TexelPerSide、PatchFullSize、MeshScale。
 | 
						||
	 3. 设置随机种子相关的材质参数。
 | 
						||
	 4. 设置材质参数PatchLocation。
 | 
						||
	 5. 生成碰撞数据到`CollisionRT`。
 | 
						||
	 6. 笔刷功能逻辑:ApplyBrushStackToHeightMap()。
 | 
						||
	 7. ExportPhysicalMaterialID逻辑。
 | 
						||
	 8. GPU碰撞数据回读:ShaderWorld::AsyncReadPixelsFromRT()。
 | 
						||
		 1. ShaderWorld::GSWReadbackManager.AddPendingReadBack(),将回读Task增加`TArray<FReadBackTask> PendingReads;`。
 | 
						||
		 2. 之后会在USWorldSubsystem::Tick()中调用ShaderWorld::GSWReadbackManager.TickReadBack(),不断检查是否可回读,并进行最终回读。
 | 
						||
 - 调用顺序:Tick() -> CollisionManagement() -> CollisionGPU() -> UpdateCollisionMeshData()
 | 
						||
 | 
						||
```c++
 | 
						||
namespace ShaderWorld
 | 
						||
{
 | 
						||
	FORCEINLINE void AsyncReadPixelsFromRT(UShaderWorldRT2D* InRT, TSharedPtr<FSWColorRead, ESPMode::ThreadSafe> Destination, TSharedPtr < FThreadSafeBool, ESPMode::ThreadSafe> Completion)
 | 
						||
	{
 | 
						||
 | 
						||
		ENQUEUE_RENDER_COMMAND(ReadGeoClipMapRTCmd)(
 | 
						||
			[InRT, HeightData = Destination, Completion = Completion](FRHICommandListImmediate& RHICmdList)
 | 
						||
			{
 | 
						||
				check(IsInRenderingThread());
 | 
						||
 | 
						||
				if (HeightData.IsValid() && InRT->GetResource())
 | 
						||
				{
 | 
						||
					FRDGBuilder GraphBuilder(RHICmdList);
 | 
						||
					TSharedPtr<FRHIGPUTextureReadback> ReadBackStaging = MakeShared<FRHIGPUTextureReadback>(TEXT("SWGPUTextureReadback"));
 | 
						||
					FRDGTextureRef RDGSourceTexture = RegisterExternalTexture(GraphBuilder, InRT->GetResource()->TextureRHI, TEXT("SWSourceTextureToReadbackTexture"));
 | 
						||
					AddEnqueueCopyPass(GraphBuilder, ReadBackStaging.Get(), RDGSourceTexture);
 | 
						||
					GraphBuilder.Execute();
 | 
						||
 | 
						||
					ShaderWorld::GSWReadbackManager.AddPendingReadBack(RHICmdList, GPixelFormats[RDGSourceTexture->Desc.Format].BlockBytes, RDGSourceTexture->Desc.Extent.X, RDGSourceTexture->Desc.Extent.Y, ReadBackStaging, const_cast<TSharedPtr<FSWColorRead, ESPMode::ThreadSafe>&>(HeightData), const_cast<TSharedPtr < FThreadSafeBool, ESPMode::ThreadSafe>&>(Completion));
 | 
						||
				}
 | 
						||
 | 
						||
			});	
 | 
						||
	}
 | 
						||
```
 | 
						||
 | 
						||
 | 
						||
### InitializeReadBackDependencies
 | 
						||
- 作用:初始化几个GPU数据回读用的RT。
 | 
						||
- 调用顺序:BeginPlay() -> InitiateWorld() -> InitializeReadBackDependencies()
 | 
						||
 | 
						||
1. 初始化3个RT:ReadRequestLocation、ReadRequestLocationHeightmap、GeneratorDynamicForReadBack。
 | 
						||
2. 会设置`TObjectPtr < UMaterialInstanceDynamic> GeneratorDynamicForReadBack`各种变量
 | 
						||
```c++
 | 
						||
GeneratorDynamicForReadBack->SetScalarParameterValue("HeightReadBack", 1.f);  
 | 
						||
GeneratorDynamicForReadBack->SetTextureParameterValue("SpecificLocationsRT", ReadRequestLocation);  
 | 
						||
GeneratorDynamicForReadBack->SetScalarParameterValue("NoMargin", 0.f);  
 | 
						||
GeneratorDynamicForReadBack->SetScalarParameterValue("N", N);  
 | 
						||
GeneratorDynamicForReadBack->SetScalarParameterValue("NormalMapSelect", 0.f);  
 | 
						||
GeneratorDynamicForReadBack->SetScalarParameterValue("HeightMapToggle", 1.f);
 | 
						||
```
 | 
						||
3. 设置随机种子相关Shader Parameter。
 | 
						||
### InitiateMaterials
 | 
						||
作用:初始化`TArray<FClipMapMeshElement> Meshes;`的Material、`Producers`
 | 
						||
调用顺序:BeginPlay() -> InitiateWorld() -> InitiateMaterials()
 | 
						||
 | 
						||
经过断点调试,会设置WorldSettings里的Material(地形Material)的HeightMap与NormalMap。
 | 
						||
 | 
						||
 | 
						||
# 
 | 
						||
SWorldSubsystem->DrawMaterialToRenderTarget
 | 
						||
 | 
						||
 | 
						||
# Rebuild逻辑
 | 
						||
## 重要函数
 | 
						||
- AShaderWorldActor::BeginPlay()
 | 
						||
- AShaderWorldActor::Setup()(<- TerrainAndSpawnablesManagement(float& DeltaT) <- Tick())
 | 
						||
 | 
						||
## Rebuild逻辑顺序
 | 
						||
1. AShaderWorldActor::BeginPlay()
 | 
						||
	1. 
 | 
						||
 | 
						||
 | 
						||
# Debug
 | 
						||
1. AShaderWorldActor::ComputeHeight_Segmented_MapForClipMap 十多次
 | 
						||
2. UpdateCollisionMeshData
 | 
						||
3. AShaderWorldActor::ComputeHeight_Segmented_MapForClipMap  十多次
 | 
						||
4. [[#RetrieveHeightAt]]
 | 
						||
5. UpdateCollisionMeshData 3次
 | 
						||
6. AShaderWorldActor::ComputeHeight_Segmented_MapForClipMap  十多次
 | 
						||
 | 
						||
# RetrieveHeightAt
 | 
						||
可能存在bug待排查的:
 | 
						||
- ShaderWorldSubsystem->LoadSampleLocationsInRT()
 | 
						||
- ShaderWorldSubsystem->DrawMaterialToRenderTarget()
 | 
						||
 | 
						||
## 相关变量
 | 
						||
- FThreadSafeBool
 | 
						||
	- bProcessingHeightRetrieval
 | 
						||
	- bProcessingHeightRetrievalRT
 | 
						||
- MID
 | 
						||
	- GeneratorDynamicForReadBack
 | 
						||
- UShaderWorldRT2D(`UTextureRenderTarget2D`)
 | 
						||
	- ReadRequestLocation:RTF_RG32f,初始化于`InitializeReadBackDependencies() <- InitiateWorld()`
 | 
						||
	- ReadRequestLocationHeightmap:RTF_RGBA8,初始化于`InitializeReadBackDependencies() <- InitiateWorld()`
 | 
						||
 | 
						||
 | 
						||
 | 
						||
## 代码
 | 
						||
```c++
 | 
						||
bool AShaderWorldActor::RetrieveHeightAt(const TArray<FVector>& Origin, const FSWHeightRetrievalDelegate& Callback)
 | 
						||
{	
 | 
						||
	if (!GeneratorDynamicForReadBack || !SWorldSubsystem)
 | 
						||
		return false;
 | 
						||
 | 
						||
	if (!bProcessingHeightRetrieval.IsValid())
 | 
						||
	{
 | 
						||
		bProcessingHeightRetrieval = MakeShared<FThreadSafeBool, ESPMode::ThreadSafe>();
 | 
						||
		bProcessingHeightRetrieval->AtomicSet(false);
 | 
						||
	}
 | 
						||
	if (!bProcessingHeightRetrievalRT.IsValid())
 | 
						||
	{
 | 
						||
		bProcessingHeightRetrievalRT = MakeShared<FThreadSafeBool, ESPMode::ThreadSafe>();
 | 
						||
		bProcessingHeightRetrievalRT->AtomicSet(false);
 | 
						||
	}
 | 
						||
 | 
						||
 | 
						||
	if (!(*bProcessingHeightRetrieval.Get()) && ReadRequestLocation && ReadRequestLocationHeightmap && GeneratorDynamicForReadBack)
 | 
						||
	{
 | 
						||
		bProcessingHeightRetrieval->AtomicSet(true);
 | 
						||
		bProcessingHeightRetrievalRT->AtomicSet(false);
 | 
						||
		HeightRetrieveDelegate = Callback;
 | 
						||
 | 
						||
		//初始化采样点数组结构体FSWShareableSamplePoints
 | 
						||
		PointsPendingReadBacks = MakeShared<FSWShareableSamplePoints, ESPMode::ThreadSafe>();
 | 
						||
		TSharedPtr<FSWShareableSamplePoints>& Samples = PointsPendingReadBacks;
 | 
						||
 | 
						||
		FBox BoundingBoxRead(Origin);
 | 
						||
 | 
						||
		Samples->PositionsXY.SetNum(25 * 2);
 | 
						||
		for (int i = 0; i < 25; i++)
 | 
						||
		{
 | 
						||
			if (i < Origin.Num())
 | 
						||
			{
 | 
						||
				Samples->PositionsXY[i * 2] = Origin[i].X;
 | 
						||
				Samples->PositionsXY[i * 2 + 1] = Origin[i].Y;
 | 
						||
			}
 | 
						||
			else
 | 
						||
			{
 | 
						||
				Samples->PositionsXY[i * 2] = 0.f;
 | 
						||
				Samples->PositionsXY[i * 2 + 1] = 0.f;
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		if (USWorldSubsystem* ShaderWorldSubsystem = SWorldSubsystem)
 | 
						||
		{
 | 
						||
			//从渲染线程
 | 
						||
			ShaderWorldSubsystem->LoadSampleLocationsInRT(ReadRequestLocation, Samples);
 | 
						||
 | 
						||
#if SW_COMPUTE_GENERATION
 | 
						||
			ShaderWorldSubsystem->DrawMaterialToRenderTarget(
 | 
						||
			{   false,
 | 
						||
				 false,
 | 
						||
				GetWorld()->Scene,
 | 
						||
				(float)GetWorld()->TimeSeconds,
 | 
						||
				false,
 | 
						||
				true,
 | 
						||
				ReadRequestLocationHeightmap->SizeX,
 | 
						||
				10,
 | 
						||
				FVector(0.f),
 | 
						||
				true,
 | 
						||
				ReadRequestLocation,
 | 
						||
				GeneratorDynamicForReadBack,
 | 
						||
				ReadRequestLocationHeightmap
 | 
						||
			});
 | 
						||
#else
 | 
						||
			UKismetRenderingLibrary::DrawMaterialToRenderTarget(this, ReadRequestLocationHeightmap, GeneratorDynamicForReadBack);
 | 
						||
#endif
 | 
						||
 | 
						||
			int32 Size_RT_Readback = ReadRequestLocationHeightmap.Get()->SizeX;
 | 
						||
 | 
						||
			FVector Barycentre = BoundingBoxRead.GetCenter();
 | 
						||
			FVector Extent = BoundingBoxRead.GetExtent();
 | 
						||
			float gridspacing = Extent.X * 2.0 / (Size_RT_Readback - 1);
 | 
						||
 | 
						||
			if (IsValid(BrushManager))
 | 
						||
				BrushManager->ApplyBrushStackToHeightMap(this, 0, ReadRequestLocationHeightmap.Get(), Barycentre, gridspacing, Size_RT_Readback, true, true, ReadRequestLocation.Get());
 | 
						||
 | 
						||
 | 
						||
			ReadBackHeightData = MakeShared<FSWColorRead, ESPMode::ThreadSafe>();
 | 
						||
			ReadBackHeightData->ReadData.SetNum(25);
 | 
						||
 | 
						||
			ENQUEUE_RENDER_COMMAND(ReadGeoClipMapRTCmd)(
 | 
						||
				[InRT = ReadRequestLocationHeightmap, HeightData = ReadBackHeightData, Completion = bProcessingHeightRetrievalRT](FRHICommandListImmediate& RHICmdList)
 | 
						||
				{
 | 
						||
					check(IsInRenderingThread());
 | 
						||
 | 
						||
					if (HeightData.IsValid() && InRT->GetResource())
 | 
						||
					{
 | 
						||
						FRDGBuilder GraphBuilder(RHICmdList);
 | 
						||
 | 
						||
						TSharedPtr<FRHIGPUTextureReadback> ReadBackStaging = MakeShared<FRHIGPUTextureReadback>(TEXT("SWGPUTextureReadback"));
 | 
						||
 | 
						||
						FRDGTextureRef RDGSourceTexture = RegisterExternalTexture(GraphBuilder, InRT->GetResource()->TextureRHI, TEXT("SWSourceTextureToReadbackTexture"));
 | 
						||
 | 
						||
						AddEnqueueCopyPass(GraphBuilder, ReadBackStaging.Get(), RDGSourceTexture);
 | 
						||
 | 
						||
						GraphBuilder.Execute();
 | 
						||
 | 
						||
						ShaderWorld::GSWReadbackManager.AddPendingReadBack(RHICmdList, GPixelFormats[RDGSourceTexture->Desc.Format].BlockBytes, RDGSourceTexture->Desc.Extent.X, RDGSourceTexture->Desc.Extent.Y, ReadBackStaging, const_cast<TSharedPtr<FSWColorRead, ESPMode::ThreadSafe>&>(HeightData), const_cast<TSharedPtr < FThreadSafeBool, ESPMode::ThreadSafe>&>(Completion));
 | 
						||
 | 
						||
					}
 | 
						||
 | 
						||
				});
 | 
						||
 | 
						||
			HeightReadBackFence.BeginFence(true);
 | 
						||
		}
 | 
						||
 | 
						||
		return true;
 | 
						||
	}
 | 
						||
 | 
						||
	return false;
 | 
						||
}
 | 
						||
```
 | 
						||
 | 
						||
 | 
						||
### RequestReadBackLoad
 | 
						||
```c++
 | 
						||
bool USWorldSubsystem::LoadSampleLocationsInRT(UShaderWorldRT2D* LocationsRequestedRT,
 | 
						||
                                               TSharedPtr<FSWShareableSamplePoints>& Samples)
 | 
						||
{
 | 
						||
	if (!RenderThreadResponded)
 | 
						||
		return false;
 | 
						||
 | 
						||
	const SWSampleRequestComputeData ReadBackData(LocationsRequestedRT, Samples);
 | 
						||
	SWToolBox->RequestReadBackLoad(ReadBackData);
 | 
						||
	return true;
 | 
						||
}
 | 
						||
```
 | 
						||
# SWShaderToolBox
 | 
						||
## RequestReadBackLoad
 | 
						||
```c++
 | 
						||
void SWShaderToolBox::RequestReadBackLoad(const SWSampleRequestComputeData& Data) const
 | 
						||
{
 | 
						||
	if (Data.CPU)
 | 
						||
		return CPUTools.RequestReadBackLoad(Data);
 | 
						||
 | 
						||
	ENQUEUE_RENDER_COMMAND(ShaderTools_copy_rt)
 | 
						||
		([this, Data](FRHICommandListImmediate& RHICmdList)
 | 
						||
			{
 | 
						||
				if (Data.SamplesXY && Data.SamplesXY->GetResource())
 | 
						||
				RequestReadBackLoad_RT(RHICmdList,Data);
 | 
						||
			}
 | 
						||
	);
 | 
						||
 | 
						||
}
 | 
						||
 | 
						||
void SWShaderToolBox::RequestReadBackLoad_RT(FRHICommandListImmediate& RHICmdList, const SWSampleRequestComputeData& Data) const
 | 
						||
{
 | 
						||
	if (!(Data.SamplesXY && Data.SamplesXY->GetResource()))
 | 
						||
		return;
 | 
						||
 | 
						||
	FRDGBuilder GraphBuilder(RHICmdList);
 | 
						||
 | 
						||
		{
 | 
						||
			RDG_EVENT_SCOPE(GraphBuilder, "ShaderWorld  PositionReadBack");
 | 
						||
			RDG_GPU_STAT_SCOPE(GraphBuilder, ShaderWorldReadBack);
 | 
						||
 | 
						||
			FIntVector GroupCount;
 | 
						||
			GroupCount.X = FMath::DivideAndRoundUp((float)Data.SamplesXY->GetResource()->GetSizeX(), (float)SW_LoadReadBackLocations_GroupSizeX);
 | 
						||
			GroupCount.Y = FMath::DivideAndRoundUp((float)Data.SamplesXY->GetResource()->GetSizeY(), (float)SW_LoadReadBackLocations_GroupSizeY);
 | 
						||
			GroupCount.Z = 1;
 | 
						||
 | 
						||
			const FUnorderedAccessViewRHIRef RT_UAV = GraphBuilder.RHICmdList.CreateUnorderedAccessView(Data.SamplesXY->GetResource()->TextureRHI);
 | 
						||
 | 
						||
			const FRDGBufferRef LocationRequest = CreateUploadBuffer(
 | 
						||
				GraphBuilder,
 | 
						||
				TEXT("SWLoadSampleLocations"),
 | 
						||
				sizeof(float),
 | 
						||
				Data.SamplesSource->PositionsXY.Num(),
 | 
						||
				Data.SamplesSource->PositionsXY.GetData(),
 | 
						||
				Data.SamplesSource->PositionsXY.Num() * Data.SamplesSource->PositionsXY.GetTypeSize()
 | 
						||
			);
 | 
						||
 | 
						||
			const FRDGBufferSRVRef LocationRequestSRV = GraphBuilder.CreateSRV(FRDGBufferSRVDesc(LocationRequest, PF_R32_FLOAT));;
 | 
						||
 | 
						||
			FLoadReadBackLocations_CS::FPermutationDomain PermutationVector;
 | 
						||
			TShaderMapRef<FLoadReadBackLocations_CS> ComputeShader(GetGlobalShaderMap(GMaxRHIFeatureLevel), PermutationVector);
 | 
						||
	
 | 
						||
			FLoadReadBackLocations_CS::FParameters* PassParameters = GraphBuilder.AllocParameters<FLoadReadBackLocations_CS::FParameters>();
 | 
						||
			PassParameters->SampleDim = Data.SamplesXY->GetResource()->GetSizeX();
 | 
						||
			PassParameters->DestLocationsTex = RT_UAV;
 | 
						||
			PassParameters->SourceLocationBuffer = LocationRequestSRV;
 | 
						||
 | 
						||
 | 
						||
			GraphBuilder.AddPass(
 | 
						||
				RDG_EVENT_NAME("ShaderWorld LoadReadBacklocations_CS"),
 | 
						||
				PassParameters,
 | 
						||
				ERDGPassFlags::Compute |
 | 
						||
				ERDGPassFlags::NeverCull,
 | 
						||
				[PassParameters, ComputeShader, GroupCount](FRHICommandList& RHICmdList)
 | 
						||
				{
 | 
						||
					FComputeShaderUtils::Dispatch(RHICmdList, ComputeShader, *PassParameters, GroupCount);
 | 
						||
				});
 | 
						||
 | 
						||
		}
 | 
						||
 | 
						||
	GraphBuilder.Execute();
 | 
						||
}
 | 
						||
```
 | 
						||
 | 
						||
FLoadReadBackLocations_CS
 | 
						||
```c++
 | 
						||
uint SampleDim;
 | 
						||
RWTexture2D<float2> DestLocationsTex;
 | 
						||
Buffer<float> SourceLocationBuffer;
 | 
						||
 | 
						||
[numthreads(THREADGROUP_SIZEX, THREADGROUP_SIZEY, THREADGROUP_SIZEZ)]
 | 
						||
void SampleLocationLoaderCS(uint3 ThreadId : SV_DispatchThreadID)
 | 
						||
{
 | 
						||
    if (any(ThreadId.xy >= SampleDim.xx))
 | 
						||
        return;
 | 
						||
 | 
						||
	uint IndexPixel = (ThreadId.x + ThreadId.y * SampleDim) * 2;
 | 
						||
	
 | 
						||
	DestLocationsTex[ThreadId.xy] = float2(SourceLocationBuffer[IndexPixel],SourceLocationBuffer[IndexPixel + 1]);
 | 
						||
}
 | 
						||
```
 | 
						||
 | 
						||
 | 
						||
# FSWDrawMaterial_SL_CS
 | 
						||
IMPLEMENT_MATERIAL_SHADER_TYPE(template<>, FSWDrawMaterial_SL_CS, TEXT("/ShaderWorld/ShaderWorldUtilities.usf"), TEXT("DrawMaterialCS"), SF_Compute);
 | 
						||
 | 
						||
FMeshMaterialShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);  
 | 
						||
OutEnvironment.SetDefine(TEXT("SW_DRAW_WITH_HEIGHTNORMAL"), 0);  
 | 
						||
OutEnvironment.SetDefine(TEXT("SW_DRAWMATERIAL"), 1);  
 | 
						||
OutEnvironment.SetDefine(TEXT("SW_SPECIFIC_LOCATION_DRAW"), 1);  
 | 
						||
  
 | 
						||
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEX"), bIsMobileRenderer ? SW_MobileLowSharedMemory_GroupSizeX : FComputeShaderUtils::kGolden2DGroupSize);  
 | 
						||
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEY"), bIsMobileRenderer ? SW_MobileLowSharedMemory_GroupSizeY : FComputeShaderUtils::kGolden2DGroupSize);  
 | 
						||
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEZ"), 1);
 | 
						||
 | 
						||
# 
 | 
						||
- M_Blank_HeightLayer
 | 
						||
	- MFC_WorldPositionNormal_Layer
 | 
						||
		- M_Blank_HeightLayer(未连接)
 | 
						||
	- MFC_Position_ForCache
 | 
						||
- 其他
 | 
						||
	- MF_CacheRead_Reference_Tessellation
 | 
						||
	- MFC_CacheRead:没有其他引用
 | 
						||
	- MFC_CacheRead_Tessellationt
 | 
						||
	- MFC_CacheReadNoVertexManipulation
 | 
						||
	- MFC_ExternalCacheRead
 | 
						||
 | 
						||
 | 
						||
 | 
						||
# 植被生成逻辑
 | 
						||
Tick() => TerrainAndSpawnablesManagement() => UpdateSpawnables() => UpdateSpawnable()
 | 
						||
 | 
						||
 | 
						||
# 碰撞Python逻辑
 | 
						||
 | 
						||
```python
 | 
						||
def TickCheckWorldReady(self, dt: float) -> bool:  
 | 
						||
    is_shader_world_ready = self.ShaderWorldActor.WorldAndCollisionGeneratedReady()  
 | 
						||
    if not is_shader_world_ready:  
 | 
						||
        return True  
 | 
						||
    is_ready = self.ShaderWorldActor.CheckCollisionGeneratedReady(self.AvatarBornPosition)  
 | 
						||
    print("LBK --------  TickCheckWorldReady", self.FailedCheckCnt, is_ready)  
 | 
						||
    if is_ready:  
 | 
						||
        self.OnWorldMeshReady()  
 | 
						||
        ue.RemoveTicker(self.worldTickHander)  
 | 
						||
        # 等待地形物理加载完毕  
 | 
						||
        if self.OtherLoadFinshed:  
 | 
						||
            GlobalData.TimerSystem.AddTimer(1, self.ReallyStartDriving)  
 | 
						||
        else:  
 | 
						||
            self.OtherLoadFinshed = True  
 | 
						||
        print('LBK -------------- DrivingModeSystem: TickCheckWorldReady', GlobalData.p.pos, self.AvatarBornPosition)  
 | 
						||
        return False  
 | 
						||
    self.FailedCheckCnt += 1  
 | 
						||
    if self.FailedCheckCnt > self.FailedAttemptCnt:  #self.FailedAttemptCnt为40
 | 
						||
        self.ShaderWorldActor.RebuildWorld()  
 | 
						||
        self.FailedCheckCnt = 0  
 | 
						||
    return True
 | 
						||
 | 
						||
# 通过LineTraceSingleForObjects检测碰撞高度是否超过5,来判断碰撞是否生成。
 | 
						||
def CheckCollisionGeneratedReady(self, checkPosition: ue.Vector) -> bool:  
 | 
						||
    HitStartLocation = checkPosition + ue.Vector(0, 0, 50000.0)  
 | 
						||
    HitEndLocation = checkPosition + ue.Vector(0, 0, -50000.0)  
 | 
						||
    hitState, hitResult = ue.KismetSystemLibrary.LineTraceSingleForObjects(  
 | 
						||
       self.GetWorld(),  
 | 
						||
       HitStartLocation,  
 | 
						||
       HitEndLocation,  
 | 
						||
       [ue.EObjectTypeQuery.ObjectTypeQuery1, ue.EObjectTypeQuery.ObjectTypeQuery13],  # Ground  
 | 
						||
       False,  # 是否追踪复杂碰撞  
 | 
						||
       [],  
 | 
						||
       ue.EDrawDebugTrace.ForDuration if GlobalData.IsDebugMode else ue.EDrawDebugTrace.NONE,  # 调试可视化  
 | 
						||
       True,  # 忽略自身  
 | 
						||
       ue.LinearColor(1.0, 0.0, 0.0, 1.0),  # 红色  
 | 
						||
       ue.LinearColor(0.0, 1.0, 0.0, 1.0),  # 绿色  
 | 
						||
       20.0  # 调试线条持续时间  
 | 
						||
    )  
 | 
						||
    if not hitState:  
 | 
						||
       return False  
 | 
						||
    # TODO LBK 这个射线检测的方式不太准确,那个物理生成,会从平面开始  
 | 
						||
    return hitResult.Location.Z > 5.0
 | 
						||
```
 | 
						||
 | 
						||
```c++
 | 
						||
bool AShaderWorldActor::WorldAndCollisionGeneratedReady()
 | 
						||
{
 | 
						||
	if (!WorldGeneratedAndReady())
 | 
						||
		return false;
 | 
						||
	if (!CollisionShareable.IsValid())
 | 
						||
		return false;
 | 
						||
 | 
						||
	FScopeLock CollisionMeshArrayAccess(CollisionMeshAccessLock.Get());
 | 
						||
 | 
						||
	// 1. 必须有碰撞布局
 | 
						||
	if (CollisionShareable->GroundCollisionLayout.Num() == 0)
 | 
						||
		return false;
 | 
						||
 | 
						||
	// 2. 检查所有Patch都ready
 | 
						||
	for (const auto& Pair : CollisionShareable->GroundCollisionLayout)
 | 
						||
	{
 | 
						||
		const FIntVector& Loc = Pair.Key;
 | 
						||
		const FName& CollisionName = Pair.Value;
 | 
						||
 | 
						||
		// Patch必须存在
 | 
						||
		if (!CollisionMesh.Contains(CollisionName))
 | 
						||
			return false;
 | 
						||
 | 
						||
		// Patch必须在已用集合
 | 
						||
		if (!CollisionShareable->UsedCollisionMesh.Contains(CollisionName))
 | 
						||
			return false;
 | 
						||
 | 
						||
		// Patch必须ReadBack完成
 | 
						||
		const FCollisionMeshElement& CollisionElement = *CollisionMesh.Find(CollisionName);
 | 
						||
		if (!CollisionElement.ReadBackCompletion.IsValid() || !(*CollisionElement.ReadBackCompletion.Get()))
 | 
						||
			return false;
 | 
						||
 | 
						||
		// Patch不能在待处理队列
 | 
						||
		if (CollisionReadToProcess.Contains(CollisionName))
 | 
						||
			return false;
 | 
						||
		if (!UsedCollisionMesh.Contains(CollisionName))
 | 
						||
			return false;
 | 
						||
 | 
						||
		// Patch不能在工作队列
 | 
						||
		for (const auto& WQ : CollisionWorkQueue)
 | 
						||
			if (WQ.MeshID == CollisionName)
 | 
						||
				return false;
 | 
						||
	}
 | 
						||
 | 
						||
	return true;
 | 
						||
}
 | 
						||
``` |