Files
UnrealEngine/Engine/Source/Editor/UnrealEd/Private/Lightmass/LightmassLandscapeRender.cpp
2025-05-18 13:04:45 +08:00

250 lines
13 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Lightmass/LightmassLandscapeRender.h"
#include "RHI.h"
#include "RenderingThread.h"
#include "RenderResource.h"
#include "VertexFactory.h"
#include "PackedNormal.h"
#include "LandscapeLight.h"
#include "ShowFlags.h"
#include "HitProxies.h"
#include "RHIStaticStates.h"
#include "SceneView.h"
#include "PrimitiveUniformShaderParameters.h"
#include "LocalVertexFactory.h"
#include "CanvasRender.h"
#include "MeshBatch.h"
#include "MeshBuilderOneFrameResources.h"
#include "LandscapeProxy.h"
#include "LandscapeInfo.h"
#include "RendererInterface.h"
#include "LandscapeComponent.h"
#include "EngineModule.h"
#include "LandscapeEdit.h"
#include "DynamicMeshBuilder.h"
#include "MeshPassProcessor.h"
#include "UnrealClient.h"
void RenderLandscapeMaterialForLightmass(const FLandscapeStaticLightingMesh* LandscapeMesh, FMaterialRenderProxy* MaterialProxy, const FRenderTarget* RenderTarget)
{
const ULandscapeComponent* LandscapeComponent = CastChecked<ULandscapeComponent>(LandscapeMesh->Component);
const int32 SubsectionSizeQuads = LandscapeComponent->SubsectionSizeQuads;
const int32 NumSubsections = LandscapeComponent->NumSubsections;
const int32 ComponentSizeQuads = LandscapeComponent->ComponentSizeQuads;
int32 PatchExpandCountX = 0;
int32 PatchExpandCountY = 0;
int32 DesiredSize = 1;
const float LightMapOffset = 0.0f;
const float LightMapRes = LandscapeComponent->StaticLightingResolution > 0.f ? LandscapeComponent->StaticLightingResolution : LandscapeComponent->GetLandscapeProxy()->StaticLightingResolution;
const int32 LightingLOD = LandscapeComponent->GetLandscapeProxy()->StaticLightingLOD;
const float LightMapRatio = ::GetTerrainExpandPatchCount(LightMapRes, PatchExpandCountX, PatchExpandCountY, ComponentSizeQuads, (NumSubsections * (SubsectionSizeQuads + 1)), DesiredSize, LightingLOD);
const FVector2D PatchExpandOffset = FVector2D((float)PatchExpandCountX / (ComponentSizeQuads + 2 * PatchExpandCountX), (float)PatchExpandCountY / (ComponentSizeQuads + 2 * PatchExpandCountY)) * FVector2D(RenderTarget->GetSizeXY());
const FVector2D PatchExpandScale = FVector2D((float)ComponentSizeQuads / (ComponentSizeQuads + 2 * PatchExpandCountX), (float)ComponentSizeQuads / (ComponentSizeQuads + 2 * PatchExpandCountY));
TArray<FDynamicMeshVertex> Vertices;
TArray<uint32> Indices;
Vertices.Reserve(FMath::Square(NumSubsections) * 4);
Indices.Reserve(FMath::Square(NumSubsections) * 6);
const float fraction = 1.0f / NumSubsections;
const FVector2D PositionScale = FVector2D(RenderTarget->GetSizeXY()) * fraction * PatchExpandScale;
const FVector::FReal LayerScale = SubsectionSizeQuads;
const float WeightmapSubsection = LandscapeComponent->WeightmapSubsectionOffset;
const FVector2D WeightmapBias = FVector2D(LandscapeComponent->WeightmapScaleBias.Z, LandscapeComponent->WeightmapScaleBias.W);
const FVector2D WeightmapScale = FVector2D(LandscapeComponent->WeightmapScaleBias.X, LandscapeComponent->WeightmapScaleBias.Y) * SubsectionSizeQuads;
const int32 SubsectionX_Start = PatchExpandCountX > 0 ? -1 : 0;
const int32 SubsectionX_End = NumSubsections + (PatchExpandCountX > 0 ? 1 : 0);
const int32 SubsectionY_Start = PatchExpandCountY > 0 ? -1 : 0;
const int32 SubsectionY_End = NumSubsections + (PatchExpandCountY > 0 ? 1 : 0);
for (int32 SubsectionY = SubsectionY_Start; SubsectionY < SubsectionY_End; ++SubsectionY)
{
for (int32 SubsectionX = SubsectionX_Start; SubsectionX < SubsectionX_End; ++SubsectionX)
{
const FIntPoint UVSubsection = FIntPoint((SubsectionX >= 0 ? SubsectionX : 0),
(SubsectionY >= 0 ? SubsectionY : 0));
const FVector2D UVScale = FVector2D((SubsectionX >= 0 && SubsectionX < NumSubsections ? 1 : 0),
(SubsectionY >= 0 && SubsectionY < NumSubsections ? 1 : 0));
const FVector2D BasePosition = PatchExpandOffset + FVector2D(SubsectionX, SubsectionY) * PositionScale;
const FVector2D BaseLayerCoords = FVector2D(LandscapeComponent->SectionBaseX, LandscapeComponent->SectionBaseY) + FVector2D(UVSubsection) * LayerScale;
const FVector2f BaseWeightmapCoords = FVector2f(WeightmapBias) + FVector2f(UVSubsection) * WeightmapSubsection; // LWC_TODO: Precision loss
int32 Index = Vertices.Add(FDynamicMeshVertex(FVector3f(FVector2f(BasePosition) /*FVector2D(0, 0) * PositionScale*/, 0), FVector3f(FVector2f(BaseLayerCoords) /*FVector2D(0, 0) * UVScale * LayerScale*/, 0), BaseWeightmapCoords /*FVector2D(0, 0) * UVScale * WeightmapScale*/));
verifySlow( Vertices.Add(FDynamicMeshVertex(FVector3f(FVector2f(BasePosition + FVector2D(1, 0) * PositionScale), 0), FVector3f(FVector2f(BaseLayerCoords + FVector2D(1, 0) * UVScale * LayerScale), 0), BaseWeightmapCoords + FVector2f(1, 0) * FVector2f(UVScale * WeightmapScale))) == Index + 1); // LWC_TODO: Precision loss
verifySlow( Vertices.Add(FDynamicMeshVertex(FVector3f(FVector2f(BasePosition + FVector2D(0, 1) * PositionScale), 0), FVector3f(FVector2f(BaseLayerCoords + FVector2D(0, 1) * UVScale * LayerScale), 0), BaseWeightmapCoords + FVector2f(0, 1) * FVector2f(UVScale * WeightmapScale))) == Index + 2); // LWC_TODO: Precision loss
verifySlow( Vertices.Add(FDynamicMeshVertex(FVector3f(FVector2f(BasePosition + FVector2D(1, 1) * PositionScale), 0), FVector3f(FVector2f(BaseLayerCoords + FVector2D(1, 1) * UVScale * LayerScale), 0), BaseWeightmapCoords + FVector2f(1, 1) * FVector2f(UVScale * WeightmapScale))) == Index + 3); // LWC_TODO: Precision loss
checkSlow(Index + 3 <= MAX_uint16);
Indices.Add(Index);
Indices.Add(Index + 3);
Indices.Add(Index + 1);
Indices.Add(Index);
Indices.Add(Index + 2);
Indices.Add(Index + 3);
}
}
FSceneViewFamily ViewFamily(FSceneViewFamily::ConstructionValues(
RenderTarget,
NULL,
FEngineShowFlags(ESFIM_Game))
.SetTime(FGameTime()));
FDynamicMeshBuilder DynamicMeshBuilder(ViewFamily.GetFeatureLevel(), 4, 0, true);
DynamicMeshBuilder.AddVertices(Vertices);
DynamicMeshBuilder.AddTriangles(Indices);
const FIntRect ViewRect(FIntPoint(0, 0), RenderTarget->GetSizeXY());
// make a temporary view
FSceneViewInitOptions ViewInitOptions;
ViewInitOptions.ViewFamily = &ViewFamily;
ViewInitOptions.SetViewRectangle(ViewRect);
ViewInitOptions.ViewOrigin = FVector::ZeroVector;
ViewInitOptions.ViewRotationMatrix = FMatrix::Identity;
ViewInitOptions.ProjectionMatrix = FCanvas::CalcBaseTransform2D(RenderTarget->GetSizeXY().X, RenderTarget->GetSizeXY().Y);
ViewInitOptions.BackgroundColor = FLinearColor::Black;
ViewInitOptions.OverlayColor = FLinearColor::White;
ENQUEUE_RENDER_COMMAND(CanvasFlushSetupCommand)(
[RenderTarget, &DynamicMeshBuilder, ViewInitOptions, MaterialProxy](FRHICommandListImmediate& RHICmdList)
{
FMeshBatch Mesh;
FMeshBuilderOneFrameResources OneFrameResource;
DynamicMeshBuilder.GetMeshElement(FMatrix::Identity, MaterialProxy, SDPG_Foreground, true, false, 0, OneFrameResource, Mesh);
//SCOPED_DRAW_EVENT(RHICmdList, CanvasFlush);
if (OneFrameResource.IsValidForRendering())
{
const FIntRect RTViewRect = FIntRect(0, 0, RenderTarget->GetRenderTargetTexture()->GetSizeX(), RenderTarget->GetRenderTargetTexture()->GetSizeY());
FRDGBuilder GraphBuilder(RHICmdList, RDG_EVENT_NAME("LightmassLandscapeMaterial"));
FSceneView View(ViewInitOptions);
FMeshPassProcessorRenderState DrawRenderState;
// disable depth test & writes
DrawRenderState.SetDepthStencilState(TStaticDepthStencilState<false, CF_Always>::GetRHI());
//SCOPED_DRAW_EVENT(RHICmdList, RenderLandscapeMaterialToTexture);
FCanvasRenderContext RenderContext(GraphBuilder, RenderTarget, RTViewRect, FIntRect(0, 0, 0, 0), false);
GetRendererModule().DrawTileMesh(RenderContext, DrawRenderState, View, Mesh, false, FHitProxyId());
GraphBuilder.Execute();
}
});
FlushRenderingCommands();
}
void GetLandscapeOpacityData(const FLandscapeStaticLightingMesh* LandscapeMesh, int32& InOutSizeX, int32& InOutSizeY, TArray<FFloat16Color>& OutMaterialSamples)
{
const ULandscapeComponent* LandscapeComponent = CastChecked<ULandscapeComponent>(LandscapeMesh->Component);
const int32 SubsectionSizeQuads = LandscapeComponent->SubsectionSizeQuads;
const int32 NumSubsections = LandscapeComponent->NumSubsections;
const int32 ComponentSizeQuads = LandscapeComponent->ComponentSizeQuads;
int32 PatchExpandCountX = 0;
int32 PatchExpandCountY = 0;
int32 DesiredSize = 1;
const float LightMapOffset = 0.0f;
const float LightMapRes = LandscapeComponent->StaticLightingResolution > 0.f ? LandscapeComponent->StaticLightingResolution : LandscapeComponent->GetLandscapeProxy()->StaticLightingResolution;
const int32 LightingLOD = LandscapeComponent->GetLandscapeProxy()->StaticLightingLOD;
const float LightMapRatio = ::GetTerrainExpandPatchCount(LightMapRes, PatchExpandCountX, PatchExpandCountY, ComponentSizeQuads, (NumSubsections * (SubsectionSizeQuads + 1)), DesiredSize, LightingLOD);
ULandscapeInfo* const LandscapeInfo = LandscapeComponent->GetLandscapeInfo();
check(LandscapeInfo);
FLandscapeEditDataInterface DataInterface = FLandscapeEditDataInterface(LandscapeInfo);
const int32 X1 = LandscapeComponent->SectionBaseX - PatchExpandCountX;
const int32 X2 = LandscapeComponent->SectionBaseX + ComponentSizeQuads + PatchExpandCountX + 1;
const int32 Y1 = LandscapeComponent->SectionBaseY - PatchExpandCountY;
const int32 Y2 = LandscapeComponent->SectionBaseY + ComponentSizeQuads + PatchExpandCountY + 1;
const int32 XSize = X2 - X1 + 1;
const int32 YSize = Y2 - Y1 + 1;
TArray<uint8> Data;
Data.AddUninitialized(YSize * XSize);
FMemory::Memset(Data.GetData(), 255, Data.Num());
DataInterface.GetWeightDataFast(ALandscapeProxy::VisibilityLayer, X1, Y1, X2, Y2, Data.GetData(), 0);
const int32 BaseComponentX = LandscapeComponent->SectionBaseX / ComponentSizeQuads;
const int32 BaseComponentY = LandscapeComponent->SectionBaseY / ComponentSizeQuads;
for (int32 ComponentY = BaseComponentY - 1; ComponentY <= BaseComponentY + 1; ++ComponentY)
{
for (int32 ComponentX = BaseComponentX - 1; ComponentX <= BaseComponentX + 1; ++ComponentX)
{
if (ComponentX == 0 && ComponentY == 0)
{
continue;
}
ULandscapeComponent* Component = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentX, ComponentY));
if (Component)
{
const TArray<FWeightmapLayerAllocationInfo>& ComponentWeightmapLayerAllocations = Component->GetWeightmapLayerAllocations();
if (ComponentWeightmapLayerAllocations.ContainsByPredicate([](const FWeightmapLayerAllocationInfo& Allocation) { return Allocation.LayerInfo == ALandscapeProxy::VisibilityLayer; }))
{
// filled by GetWeightDataFast()
}
else
{
// fill with 0 (fully opaque)
// we don't worry about the border between components, we assume the value is identical on both components (it should be anyway)
const int32 X_Start = FMath::Clamp(ComponentX * ComponentSizeQuads + PatchExpandCountX , 0, XSize - 1);
const int32 X_End = FMath::Clamp(ComponentX * ComponentSizeQuads + PatchExpandCountX + ComponentSizeQuads + 1, 0, XSize - 1);
const int32 Y_Start = FMath::Clamp(ComponentY * ComponentSizeQuads + PatchExpandCountX , 0, YSize - 1);
const int32 Y_End = FMath::Clamp(ComponentY * ComponentSizeQuads + PatchExpandCountX + ComponentSizeQuads + 1, 0, YSize - 1);
for (int32 Y = Y_Start; Y < Y_End; ++Y)
{
uint8* DataPtr = &Data[Y * XSize + X_Start];
FMemory::Memset(DataPtr, 0, X_End - X_Start);
}
}
}
else
{
// filled with 255 (hole) by the Memset above.
// not done here due to the complexity of handling the shared border between existing / non-existing components
// it would be better if we could remove the triangles for non-existent components from the expanded landscape lighting mesh, but we don't yet do that
}
}
}
// Scale up the hole map to compensate for lightmass using point-sampling
static const int32 ScaleFactor = 3;
InOutSizeX = (XSize - 1) * ScaleFactor;
InOutSizeY = (YSize - 1) * ScaleFactor;
OutMaterialSamples.Empty(InOutSizeX * InOutSizeY);
for (int32 Y = 0; Y < InOutSizeY; ++Y)
{
for (int32 X = 0; X < InOutSizeX; ++X)
{
// Adding a half-pixel offset to compensate for lightmass using point-sampling
const FVector2D SampleUV = FVector2D(X + 0.5f, Y + 0.5f) / ScaleFactor;
const int32 X0 = FMath::FloorToInt(SampleUV.X);
const float Xf = SampleUV.X - X0;
const int32 Y0 = FMath::FloorToInt(SampleUV.Y);
const float Yf = SampleUV.Y - Y0;
const uint8 Value = FMath::BiLerp(
Data[FMath::Clamp(Y0 , 0, YSize - 1) * XSize + FMath::Clamp(X0 , 0, XSize - 1)],
Data[FMath::Clamp(Y0 , 0, YSize - 1) * XSize + FMath::Clamp(X0 + 1, 0, XSize - 1)],
Data[FMath::Clamp(Y0 + 1, 0, YSize - 1) * XSize + FMath::Clamp(X0 , 0, XSize - 1)],
Data[FMath::Clamp(Y0 + 1, 0, YSize - 1) * XSize + FMath::Clamp(X0 + 1, 0, XSize - 1)],
Xf, Yf);
const float Opacity = (255 - Value) / 255.0f;
OutMaterialSamples.Add(FLinearColor(Opacity, Opacity, Opacity));
}
}
}