2336 lines
79 KiB
Markdown
Raw Normal View History

2025-06-03 10:19:29 +08:00
---
title: Untitled
date: 2025-06-03 10:19:25
excerpt:
tags:
rating: ⭐
---
2025-06-03 10:50:23 +08:00
# 前言
1. ShaderWorldPCGInterop
2. ShaderWorld
2025-06-03 18:24:36 +08:00
3. ShaderWorldCore
# ShaderWorld
- Class
- Actor
- ShaderWorldActor.h:[[#AShaderWorldActor]]
- SWorld.h:[[#ASWorld]]
2025-06-25 16:20:36 +08:00
# USWorldSubsystem
主要管理:
- TArray<USWContextBase*> SW_Contexts
- 渲染相关变量RT_Ready、RenderThreadPoked、RenderThreadResponded处理。
# ASWorld
2025-06-03 18:24:36 +08:00
2025-06-25 16:20:36 +08:00
# AShaderWorldActor
2025-06-25 20:44:30 +08:00
## 大致启动流程
2025-06-25 16:20:36 +08:00
大致启动流程:
2025-06-25 17:48:04 +08:00
- BeginPlay()
1. 设置SWCamera Location。
2. 清空所有数据。
3. InitiateWorld()
2025-06-25 20:44:30 +08:00
1. 更新相关变量GenerateCollision_last、VerticalRangeMeters_last、WorldHasBounds_OnRebuild。**Segmented_Initialized** 、**bExportPhysicalMaterialID_cached**
2025-06-25 18:25:35 +08:00
2. 设置高度生成用材质变量 Generator;bool bHadGeneratorAtRebuildTime = IsValid(Generator);
3. LOD_Num与WorldDimensionMeters计算。
2025-06-25 19:05:47 +08:00
4. 重新生成若干数组:
1. LODs_DimensionsMeters
2. ClipMapToUpdateAndMove
3. ClipMapToUpdate
4. NeedSegmentedUpdate
2025-06-25 20:44:30 +08:00
5. AdaptiveTopology
2025-06-25 19:05:47 +08:00
5. 相关线程安全变量:
1. Shareable_ID_FarLOD
2. UpdateHOverTime
2025-06-25 20:44:30 +08:00
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
2025-06-25 17:48:04 +08:00
- Tick()
2025-06-25 20:44:30 +08:00
- 初始化
1. 取得USWorldSubsystem指针并且进行初始化。
- ReadbacksManagement()
- CollisionManagement()
- SpawnablesManagement()
- TerrainAndSpawnablesManagement()
### ReadbacksManagement()
2025-06-26 23:33:30 +08:00
主要用于回读RetrieveHeightAt()调用后GPU计算出来的Position信息。在**用于计算HeightReadBack的渲染线程、RHI、GPU都执行完成**后从ReadBackHeightData的像素点最多25个中读取Z轴信息之后和PointsPendingReadBacks中的X、Y轴信息合并成一个Vector3 Position。
2025-06-25 23:32:48 +08:00
- ThreadSafe
- bool
- **bProcessingHeightRetrieval**
- **bProcessingHeightRetrievalRT**
- FSWShareableSamplePoints
- PointsPendingReadBacks
- FSWColorRead
- ReadBackHeightData
- FRenderCommandFence
- HeightReadBackFence
2025-06-25 20:44:30 +08:00
2025-06-25 23:32:48 +08:00
```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()
2025-06-26 18:45:36 +08:00
- ThreadSafe
- bool
2025-07-14 17:51:44 +08:00
- ***bProcessingGroundCollision***:处理地面碰撞。
2025-06-26 18:45:36 +08:00
- bPreprocessingCollisionUpdate
- EditRebuild传递Bool值给 rebuild。
- FSWCollisionManagementShareableData
- CollisionShareable
- FRenderCommandFence
- 1
- bool
- RedbuildCollisionContext
- Array
2025-06-27 18:34:16 +08:00
- CollisionReadToProcess
- CollisionWorkQueue类型为FCollisionProcessingWork碰撞处理任务队列将回读的
- FCollisionMeshElement
- ReadBackCompletion碰撞数据GPU回读是否完成。
2025-06-26 18:45:36 +08:00
2025-07-15 19:06:28 +08:00
相关条件
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())
2025-06-25 23:40:46 +08:00
```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
2025-07-15 19:06:28 +08:00
// 碰撞生成与Update计时
2025-06-25 23:40:46 +08:00
{
CollisionUpdateTimeAcu += DeltaT;
2025-07-15 19:06:28 +08:00
//CollisionUpdateTimeAcu如果超过0.1s或者CollisionWorkQueue的任务都完成了。CollisionProcess已经完成。
2025-06-25 23:40:46 +08:00
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();
}
```
2025-06-26 17:36:24 +08:00
2025-06-25 23:40:46 +08:00
#### SetupCollisions()
2025-06-27 18:34:16 +08:00
设置相关变量。
2025-06-26 18:45:36 +08:00
```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;
2025-07-15 19:06:28 +08:00
//创建全线程碰撞生成相关数据结构体CollisionShareable
2025-06-26 18:45:36 +08:00
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;
}
```
2025-06-25 23:32:48 +08:00
2025-07-14 17:51:44 +08:00
#### CollisionFinalizeWork() 笔记为3.8.6版本非最新
结束碰撞生成任务true为完成false为任务超时未完成。**主要的逻辑是将之前的数据设置为脏并且更新FShaderWProceduralMeshSceneProxy,5.4 Finaly版本里这里已经移除了碰撞相关逻辑**
2025-06-27 11:02:45 +08:00
- TMap<FName,FCollisionMeshElement> CollisionMesh碰撞Name => 碰撞Mesh Map。
- FCollisionMeshElement管理单个碰撞数据的结构体。
2025-06-27 11:49:20 +08:00
- UBodySetup虚幻引擎中一个重要的物理相关类主要用于定义和存储物体的物理属性和碰撞设置
2025-06-27 11:02:45 +08:00
2025-06-26 23:34:25 +08:00
```c++
bool AShaderWorldActor::CollisionFinalizeWork()
{
SCOPED_NAMED_EVENT_TEXT("AShaderWorldActor::CollisionFinalizeWork()", FColor::Magenta);
SW_FCT_CYCLE()
2025-06-27 11:02:45 +08:00
2025-06-26 23:34:25 +08:00
if (!CollisionReadToProcess.IsEmpty())
return true;
const double GameThreadBudget_ms = SWGameThreadBudgetCollision_ms.GetValueOnGameThread();
double TimeStart = FPlatformTime::Seconds();
2025-06-27 11:02:45 +08:00
//判断处理队列是否为空
2025-06-26 23:34:25 +08:00
if(CollisionWorkQueue.Num()<=0)
return true;
{
FScopeLock CollisionMeshArrayAccess(&CollisionMeshAccessLock);
for (int i = CollisionWorkQueue.Num() - 1; i >= 0; i--)
{
2025-06-27 11:02:45 +08:00
//判断生成时间是否是否超过预设数值如果超过就直接返回。默认为1 ms。
2025-06-26 23:34:25 +08:00
if ((FPlatformTime::Seconds() - TimeStart) * 1000.0 > GameThreadBudget_ms)
return false;
2025-06-27 11:02:45 +08:00
//将生成的碰撞数据赋予给对应的碰撞Mesh最后移除任务队列中的任务。
2025-06-26 23:34:25 +08:00
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;
2025-06-27 11:02:45 +08:00
}
void UShaderWorldCollisionComponent::UpdateSectionTriMesh(TSharedPtr<FSWShareableVerticePositionBuffer, ESPMode::ThreadSafe>& Positions)
{
2025-06-27 18:34:16 +08:00
//当UBodySetup更新队列还有任务时执行改函数会将FSWShareableVerticePositionBuffer加入到UpdatesReceivedDuringCompute数组中并且退出。
2025-06-27 11:02:45 +08:00
if (AsyncBodySetupQueue.Num() > 0)
{
#if SWDEBUG
SW_LOG("Collision Update received during alreayd occuring computation %s",*GetName())
#endif
UpdatesReceivedDuringCompute.Add(Positions);
return;
}
UpdatesReceivedDuringCompute.Empty();
2025-06-27 18:34:16 +08:00
//使用FSWShareableVerticePositionBuffer数据来更新当前UShaderWorldCollisionComponent
2025-06-27 11:02:45 +08:00
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;
2025-06-26 23:34:25 +08:00
2025-06-27 11:02:45 +08:00
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()))
{
2025-06-27 18:34:16 +08:00
//塞入Normal
2025-06-27 11:02:45 +08:00
if(IsValid(Comp))
Comp->ReceiveComputedNormals(PBuffer, Normals);
}
}
});
});
}
//Materials
// Pass new positions to trimesh
2025-06-27 18:34:16 +08:00
UpdateCollision();//1. 异步Cook UBodySetup 2. 删除原本的碰撞 3. UseBodySetup->CreatePhysicsMeshesAsync()使用UBodySetup异步创建新的碰撞网格。
UpdateLocalBounds();//更新UShaderWorldCollisionComponent的LocalBoundingBox
2025-06-27 11:02:45 +08:00
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;
2025-06-27 18:34:16 +08:00
//更新SceneProxy FShaderWProceduralMeshSceneProxy的NewPositionBuffer也就是UpdateSection
2025-06-27 11:02:45 +08:00
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();
}
2025-06-26 23:34:25 +08:00
}
```
2025-06-27 18:34:16 +08:00
#### CollisionPreprocessGPU()
GPU生成碰撞 的预处理阶段。
2025-07-14 17:51:44 +08:00
将从GPU回读的碰撞顶点Z轴数据***uint8* ReadData8*** 反序列化成double之后再还原成***FCollisionProcessingWork.DestB***的Positions、Normals、Positions3f、MaterialIndices、Bound。
2025-06-27 18:34:16 +08:00
```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);
}
}
2025-07-14 17:51:44 +08:00
//回读队列处理完毕后之后执行后续逻辑
2025-06-27 18:34:16 +08:00
if (!CollisionReadToProcess.IsEmpty())
{
return false;
}
2025-07-14 17:51:44 +08:00
//开始执行碰撞处理队列逻辑
2025-06-27 18:34:16 +08:00
CollisionReadToProcess.Empty();
if (CollisionWorkQueue.Num() > 0)
{
(*bProcessingGroundCollision.Get()) = true;
AShaderWorldActor* SWContext = this;
2025-07-14 17:51:44 +08:00
//异步并行 反序列化Ground碰撞数据
2025-06-27 18:34:16 +08:00
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;
2025-07-14 17:51:44 +08:00
//取得从GPU回读的序列化数据
2025-06-27 18:34:16 +08:00
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
2025-07-14 17:51:44 +08:00
//从对序列化碰撞Z轴数据进行反序列化还原成double再之后还原成FCollisionProcessingWork.DestB
2025-06-27 18:34:16 +08:00
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
2025-07-14 17:51:44 +08:00
* 地形挖洞,碰撞逻辑处理。
2025-06-27 18:34:16 +08:00
*/
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);
}
);
2025-07-14 17:51:44 +08:00
//执行完成后将CompletionbProcessingGroundCollision设置为false代表处理完成。
2025-06-27 18:34:16 +08:00
if (Completion.IsValid())
Completion->AtomicSet(false);
});
}
return true;
}
```
2025-07-14 22:20:22 +08:00
#### 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;
2025-07-15 19:06:28 +08:00
else if ((abs(CurrentTime - Elem.SleepTime) >= 1.0) && (CollisionBufferHolder == "" || AvailableID != CollisionBufferHolder) && (!Elem.Mesh || (Elem.Mesh && !Elem.Mesh->HasAsyncWorkPending())))//如果生成超时,则销毁对应变量
2025-07-14 22:20:22 +08:00
{
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
2025-07-15 19:06:28 +08:00
* 异步任务创建了代理碰撞网格,并将相关 ID 与它们关联起来,使用提供的 ID 在游戏线程中创建实际的碰撞。
2025-07-14 22:20:22 +08:00
*/
for (FName& ID : CollisionShareable->CollisionMeshToCreate)
{
2025-07-15 19:06:28 +08:00
//使用需要创建碰撞的IDName来获取碰撞MeshData.
2025-07-14 22:20:22 +08:00
const FSWCollisionMeshElemData& Mesh_Shareable = *CollisionShareable->CollisionMeshData.Find(ID);
2025-07-15 19:06:28 +08:00
FCollisionMeshElement& Mesh = GetACollisionMesh(ID);//GetACollisionMesh()取得现有FCollisionMeshElement或者创建新的代码贼长。
2025-07-14 22:20:22 +08:00
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;
}
2025-07-15 19:06:28 +08:00
//清空创建列表。
2025-07-14 22:20:22 +08:00
CollisionShareable->CollisionMeshToCreate.Empty();
2025-07-15 19:06:28 +08:00
//处理CollisionMeshToUpdate 碰撞数据更新队列
2025-07-14 22:20:22 +08:00
for (int i = CollisionShareable->CollisionMeshToUpdate.Num() - 1; i >= 0; i--)
{
FName& CollisionIDToUpdate = CollisionShareable->CollisionMeshToUpdate[i];
FCollisionMeshElement& El = *CollisionMesh.Find(CollisionIDToUpdate);
2025-07-15 19:06:28 +08:00
UpdateCollisionMeshData(El);//核心函数
2025-07-14 22:20:22 +08:00
CollisionShareable->CollisionMeshToUpdate.RemoveAt(i);
RequireRenderFence = true;
CollisionDrawCallCount++;
if (CollisionDrawCallCount >= CollisionMaxDrawCallPerFrame)
break;
}
2025-07-15 19:06:28 +08:00
//处理CollisionMeshToRenameMoveUpdate 碰撞数据重命名&移动更新队列
2025-07-14 22:20:22 +08:00
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;
}
2025-07-15 19:06:28 +08:00
//开启CollisionProcess需要等到执行完之后才能执行CollisionPreprocessGPU()
2025-07-14 22:20:22 +08:00
if (RequireRenderFence)
{
CollisionProcess.BeginFence();
}
}
```
2025-07-14 17:51:44 +08:00
```c++
2025-07-15 19:06:28 +08:00
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++
2025-07-14 17:51:44 +08:00
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;
2025-07-15 19:06:28 +08:00
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();
}
bool bExternalCollisionRebuildRequest = !CollisionUpdateRequest.IsEmpty();
FBox2D BPCollisionRebuildRequest(ForceInit);
if (bExternalCollisionRebuildRequest)
CollisionUpdateRequest.Dequeue(BPCollisionRebuildRequest);
TArray<ShaderWorld::FVisitor> VisitorLocations;
// Use latest camera we know if current array is empty
if (CameraLocations.Num() <= 0)
VisitorLocations.Add(CamLocation);
VisitorLocations.Append(CameraLocations);
(*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]
{
if (!CollData.IsValid())
{
if (Completion.IsValid())
(*Completion.Get()) = false;
return;
}
FScopeLock CollisionMeshArrayAccess(Lock.Get());
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();
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;
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);
}
}
}
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);
}
}
}
}
}
}
if (Completion.IsValid())
(*Completion.Get()) = false;
}
);
}
2025-07-14 17:51:44 +08:00
```
2025-06-25 23:32:48 +08:00
### SpawnablesManagement()
### TerrainAndSpawnablesManagement()
2025-06-25 16:20:36 +08:00
## PreEditChange() / PostEditChangeProperty()
2025-06-25 14:59:46 +08:00
**PreEditChange()** 主要针对以下两个变量的设置:
- PreventReRegistration防止重新注册。
- RuntimePropertyEditing实时属性修改。
**PostEditChangeProperty()**
里面比较关键的逻辑有:
- EditRebuildVegetation.AtomicSet(true);
- 在Setup()中清空Bioms数组。
- EditRebuild.AtomicSet(true);
- 在SetupCollisions()设置rebuild = true。
如果RuntimePropertyEditing为true在最后会将RuntimePropertyEditing设置为false。PreventReRegistration也会设置为false。
2025-06-25 16:20:36 +08:00
## Rebuild变量
2025-06-25 14:59:46 +08:00
主要出现在:
- Setup():调用RebuildCleanup()清空所有数据。
- SetupCollision(): RedbuildCollisionContext = true。
- ProcessSpawnablePending():如果处于重建状态就直接返回。
- InitiateClipMapMeshes():如果处于重建状态就直接返回。
- FinalizeAsyncWork():如果处于重建状态就直接返回。
2025-06-09 16:42:14 +08:00
## DrawMaterialToRenderTarget
USWorldSubsystem::DrawMaterialToRenderTarget
=>
SWShaderToolBox::DrawMaterial
=>
DrawMaterial_CS_RT
调用路径:
2025-06-13 11:57:17 +08:00
- AShaderWorldActor::[[#RetrieveHeightAt]](好像没有引用):检索高度
2025-06-09 16:42:14 +08:00
- 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
2025-06-09 17:32:09 +08:00
- AShaderWorldActor::ProcessSegmentedComputation() <- AShaderWorldActor::TerrainAndSpawnablesManagement() <- AShaderWorldActor::Tick()
## Cache机制
AShaderWorldActor::ProcessSegmentedComputation() <- AShaderWorldActor::TerrainAndSpawnablesManagement() <- AShaderWorldActor::Tick()
2025-06-10 14:59:07 +08:00
# 其他Bug
2025-06-10 18:47:39 +08:00
2025-06-10 14:59:07 +08:00
## SetTextureParameterValue相关逻辑排查
- AShaderWorldActor中的SetTextureParameterValue
- ~~ExportCacheInBounds~~
- ~~AssignHeightMapToDynamicMaterial~~
- UpdateStaticDataFor
- ComputeHeight_Segmented_MapForClipMap似乎会设置
2025-06-10 18:47:39 +08:00
- ~~UpdateCollisionMeshData~~
2025-06-10 17:02:58 +08:00
- [x] [[#InitializeReadBackDependencies]]
- [x] InitiateMaterials
2025-06-10 14:59:07 +08:00
2025-06-10 18:47:39 +08:00
### UpdateStaticDataFor
2025-06-10 14:59:07 +08:00
2025-06-10 17:02:58 +08:00
2025-06-10 14:59:07 +08:00
### ComputeHeight_Segmented_MapForClipMap
2025-06-10 20:14:47 +08:00
- 作用:
- 调用顺序AShaderWorldActor::Tick() -> AShaderWorldActor::TerrainAndSpawnablesManagement() -> AShaderWorldActor::ProcessSegmentedComputation() -> ComputeHeight_Segmented_MapForClipMap
2025-06-10 14:59:07 +08:00
2025-06-10 20:14:47 +08:00
>// 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
2025-06-10 18:47:39 +08:00
2025-06-10 14:59:07 +08:00
2025-06-10 18:47:39 +08:00
### 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));
}
});
}
```
2025-06-10 14:59:07 +08:00
2025-06-10 17:02:58 +08:00
### InitializeReadBackDependencies
2025-06-10 18:47:39 +08:00
- 作用初始化几个GPU数据回读用的RT。
- 调用顺序BeginPlay() -> InitiateWorld() -> InitializeReadBackDependencies()
2025-06-10 17:02:58 +08:00
1. 初始化3个RTReadRequestLocation、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。
2025-06-10 14:59:07 +08:00
### InitiateMaterials
2025-06-10 17:02:58 +08:00
作用:初始化`TArray<FClipMapMeshElement> Meshes;`的Material、`Producers`
调用顺序BeginPlay() -> InitiateWorld() -> InitiateMaterials()
2025-06-10 21:54:10 +08:00
经过断点调试会设置WorldSettings里的Material地形Material的HeightMap与NormalMap。
#
SWorldSubsystem->DrawMaterialToRenderTarget
2025-06-11 12:17:42 +08:00
# Rebuild逻辑
2025-06-11 12:49:33 +08:00
## 重要函数
- AShaderWorldActor::BeginPlay()
- AShaderWorldActor::Setup()<- TerrainAndSpawnablesManagement(float& DeltaT) <- Tick()
2025-06-11 13:25:51 +08:00
## Rebuild逻辑顺序
1. AShaderWorldActor::BeginPlay()
1.
2025-06-12 12:22:31 +08:00
# Debug
2025-06-12 13:45:32 +08:00
1. AShaderWorldActor::ComputeHeight_Segmented_MapForClipMap 十多次
2. UpdateCollisionMeshData
3. AShaderWorldActor::ComputeHeight_Segmented_MapForClipMap 十多次
2025-06-13 11:57:17 +08:00
4. [[#RetrieveHeightAt]]
2025-06-12 13:45:32 +08:00
5. UpdateCollisionMeshData 3次
2025-06-13 11:57:17 +08:00
6. AShaderWorldActor::ComputeHeight_Segmented_MapForClipMap 十多次
# RetrieveHeightAt
可能存在bug待排查的
- ShaderWorldSubsystem->LoadSampleLocationsInRT()
- ShaderWorldSubsystem->DrawMaterialToRenderTarget()
## 相关变量
- FThreadSafeBool
- bProcessingHeightRetrieval
- bProcessingHeightRetrievalRT
- MID
- GeneratorDynamicForReadBack
- UShaderWorldRT2D`UTextureRenderTarget2D`
- ReadRequestLocationRTF_RG32f初始化于`InitializeReadBackDependencies() <- InitiateWorld()`
- ReadRequestLocationHeightmapRTF_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]);
}
2025-06-13 17:47:07 +08:00
```
2025-06-13 18:51:25 +08:00
# 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);
2025-06-13 17:47:07 +08:00
#
- M_Blank_HeightLayer
- MFC_WorldPositionNormal_Layer
- M_Blank_HeightLayer未连接
- MFC_Position_ForCache
- 其他
- MF_CacheRead_Reference_Tessellation
- MFC_CacheRead没有其他引用
- MFC_CacheRead_Tessellationt
- MFC_CacheReadNoVertexManipulation
2025-06-13 18:51:25 +08:00
- MFC_ExternalCacheRead
2025-06-17 17:42:27 +08:00
# 植被生成逻辑
2025-06-25 12:29:10 +08:00
Tick() => TerrainAndSpawnablesManagement() => UpdateSpawnables() => UpdateSpawnable()
2025-07-14 15:06:59 +08:00
# 碰撞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;
}
```