EM_Task/UnrealEd/Private/Lightmass/ImportVolumetricLightmap.cpp
Boshuang Zhao 5144a49c9b add
2026-02-13 16:18:33 +08:00

2221 lines
118 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
ImportVolumetricLightmap.h
=============================================================================*/
#include "Lightmass/Lightmass.h"
#include "EngineDefines.h"
#include "Engine/World.h"
#include "EngineUtils.h"
#include "PrecomputedVolumetricLightmap.h"
#include "Engine/MapBuildDataRegistry.h"
#include "ImportExport.h"
#include "UnrealEngine.h"
#include "Logging/TokenizedMessage.h"
#include "Logging/MessageLog.h"
#include "RenderUtils.h"
#define LOCTEXT_NAMESPACE "ImportVolumetricLightmap"
DEFINE_LOG_CATEGORY_STATIC(LogVolumetricLightmapImport, Log, All);
extern ENGINE_API bool GLightingScenraioSeperateData;
FVector GDebugVoxelPosition = FVector::ZeroVector;
float NormalizeFloat(float X)
{
if (X < 10)
{
return X / 10 * 0.8;
}
else
{
return (X - 10) / X * 0.2 + 0.8;
}
}
void CopyBrickToAtlasVolumeTexture(int32 FormatSize, FIntVector AtlasSize, FIntVector BrickMin, FIntVector BrickSize, const uint8* RESTRICT SourceData, uint8* RESTRICT DestData)
{
const int32 SourcePitch = BrickSize.X * FormatSize;
const int32 Pitch = AtlasSize.X * FormatSize;
const int32 DepthPitch = AtlasSize.X * AtlasSize.Y * FormatSize;
// Copy each row into the correct position in the global volume texture
for (int32 ZIndex = 0; ZIndex < BrickSize.Z; ZIndex++)
{
const int32 DestZIndex = (BrickMin.Z + ZIndex) * DepthPitch + BrickMin.X * FormatSize;
const int32 SourceZIndex = ZIndex * BrickSize.Y * SourcePitch;
for (int32 YIndex = 0; YIndex < BrickSize.Y; YIndex++)
{
const int32 DestIndex = DestZIndex + (BrickMin.Y + YIndex) * Pitch;
const int32 SourceIndex = SourceZIndex + YIndex * SourcePitch;
FMemory::Memcpy((uint8*)&DestData[DestIndex], (const uint8*)&SourceData[SourceIndex], SourcePitch);
}
}
}
void CopyBetweenAtlasVolumeTextures(
int32 FormatSize,
FIntVector BrickSize,
FIntVector SourceAtlasSize,
FIntVector SourceBrickMin,
const TArray<uint8>& SourceData,
FIntVector DestAtlasSize,
FIntVector DestBrickMin,
TArray<uint8>& DestData)
{
const int32 DestPitch = DestAtlasSize.X * FormatSize;
const int32 DestDepthPitch = DestAtlasSize.X * DestAtlasSize.Y * FormatSize;
const int32 SourcePitch = SourceAtlasSize.X * FormatSize;
const int32 SourceDepthPitch = SourceAtlasSize.X * SourceAtlasSize.Y * FormatSize;
// Copy each row into the correct position in the global volume texture
for (int32 ZIndex = 0; ZIndex < BrickSize.Z; ZIndex++)
{
const int32 DestZIndex = (DestBrickMin.Z + ZIndex) * DestDepthPitch + DestBrickMin.X * FormatSize;
const int32 SourceZIndex = (SourceBrickMin.Z + ZIndex) * SourceDepthPitch + SourceBrickMin.X * FormatSize;
for (int32 YIndex = 0; YIndex < BrickSize.Y; YIndex++)
{
const int32 DestIndex = DestZIndex + (DestBrickMin.Y + YIndex) * DestPitch;
const int32 SourceIndex = SourceZIndex + (SourceBrickMin.Y + YIndex) * SourcePitch;
for (int32 XIndex = 0; XIndex < BrickSize.X * FormatSize; XIndex++)
{
FMemory::Memcpy((uint8*)&DestData[DestIndex], (const uint8*)&SourceData[SourceIndex], BrickSize.X * FormatSize);
}
}
}
}
int32 ComputeLinearVoxelIndex(FIntVector VoxelCoordinate, FIntVector VolumeDimensions)
{
return (VoxelCoordinate.Z * VolumeDimensions.Y + VoxelCoordinate.Y) * VolumeDimensions.X + VoxelCoordinate.X;
}
struct FImportedVolumetricLightmapBrick
{
FGuid IntersectingLevelGuid;
FIntVector IndirectionTexturePosition;
int32 TreeDepth;
float AverageClosestGeometryDistance;
TArray<FFloat3Packed> AmbientVector;
TArray<FFloat3Packed> SkyBouncedAmbientVector;
TArray<FColor> AmbientVectorMobile;
TArray<FColor> SHCoefficients[6];
TArray<FColor> SkyVisibilitySHCoefficients[3];
TArray<FColor> SkyBouncedSHCoefficients[6];
TArray<FFloat3Packed> LQLightColor;
TArray<FColor> LQLightDirection;
TArray<FColor> SHCoefficientsMobile[2];
TArray<FColor> SkyBentNormal;
TArray<uint8> DirectionalLightShadowing;
TArray<Lightmass::FIrradianceVoxelImportProcessingData> TaskVoxelImportProcessingData;
};
struct FImportedVolumetricLightmapTaskData
{
TArray<FImportedVolumetricLightmapBrick> Bricks;
};
inline FIntVector ComputeBrickLayoutPosition(int32 BrickLayoutAllocation, FIntVector BrickLayoutDimensions)
{
const FIntVector BrickPosition(
BrickLayoutAllocation % BrickLayoutDimensions.X,
(BrickLayoutAllocation / BrickLayoutDimensions.X) % BrickLayoutDimensions.Y,
BrickLayoutAllocation / (BrickLayoutDimensions.X * BrickLayoutDimensions.Y));
return BrickPosition;
}
bool CopyFromBrickmapTexel(
FVector IndirectionDataSourceCoordinate,
FIntVector LocalCellDestCoordinate,
int32 MinDestinationNumBottomLevelBricks,
int32 BrickSize,
FIntVector BrickLayoutPosition,
const FPrecomputedVolumetricLightmapData& CurrentLevelData,
FVolumetricLightmapBrickData& BrickData)
{
const FVector IndirectionCoordMax(FVector(CurrentLevelData.IndirectionTextureDimensions) * (1 - GPointFilteringThreshold));
if (IndirectionDataSourceCoordinate.X < 0 || IndirectionDataSourceCoordinate.Y < 0 || IndirectionDataSourceCoordinate.Z < 0 ||
IndirectionDataSourceCoordinate.X > IndirectionCoordMax.X || IndirectionDataSourceCoordinate.Y > IndirectionCoordMax.Y || IndirectionDataSourceCoordinate.Z > IndirectionCoordMax.Z)
{
return false;
}
FIntVector IndirectionBrickOffset;
int32 IndirectionBrickSize;
checkSlow(GPixelFormats[CurrentLevelData.IndirectionTexture.Format].BlockBytes == sizeof(uint8) * 4);
SampleIndirectionTexture(IndirectionDataSourceCoordinate, CurrentLevelData.IndirectionTextureDimensions, CurrentLevelData.IndirectionTexture.Data.GetData(), IndirectionBrickOffset, IndirectionBrickSize);
if (IndirectionBrickSize > MinDestinationNumBottomLevelBricks)
{
const FVector BrickTextureCoordinate = ComputeBrickTextureCoordinate(IndirectionDataSourceCoordinate, IndirectionBrickOffset, IndirectionBrickSize, BrickSize);
const FIntVector DestCellPosition = BrickLayoutPosition + LocalCellDestCoordinate;
const int32 LinearDestCellIndex = ComputeLinearVoxelIndex(DestCellPosition, CurrentLevelData.BrickDataDimensions);
*(FFloat3Packed*)&BrickData.AmbientVector.Data[LinearDestCellIndex * sizeof(FFloat3Packed)] = FilteredVolumeLookupReconverted<FFloat3Packed>(BrickTextureCoordinate, CurrentLevelData.BrickDataDimensions, (const FFloat3Packed*)BrickData.AmbientVector.Data.GetData());
*(FFloat3Packed*)&BrickData.SkyBouncedAmbientVector.Data[LinearDestCellIndex * sizeof(FFloat3Packed)] = FilteredVolumeLookupReconverted<FFloat3Packed>(BrickTextureCoordinate, CurrentLevelData.BrickDataDimensions, (const FFloat3Packed*)BrickData.SkyBouncedAmbientVector.Data.GetData());
*(FColor*)&BrickData.AmbientVectorMobile.Data[LinearDestCellIndex * sizeof(FColor)] = FilteredVolumeLookupReconverted<FColor>(BrickTextureCoordinate, CurrentLevelData.BrickDataDimensions, (const FColor*)BrickData.AmbientVectorMobile.Data.GetData());
for (int32 i = 0; i < UE_ARRAY_COUNT(BrickData.SHCoefficients); i++)
{
*(FColor*)&BrickData.SHCoefficients[i].Data[LinearDestCellIndex * sizeof(FColor)] = FilteredVolumeLookupReconverted<FColor>(BrickTextureCoordinate, CurrentLevelData.BrickDataDimensions, (const FColor*)BrickData.SHCoefficients[i].Data.GetData());
}
for (int32 i = 0; i < UE_ARRAY_COUNT(BrickData.SHCoefficientsMobile); i++)
{
*(FColor*)&BrickData.SHCoefficientsMobile[i].Data[LinearDestCellIndex * sizeof(FColor)] = FilteredVolumeLookupReconverted<FColor>(BrickTextureCoordinate, CurrentLevelData.BrickDataDimensions, (const FColor*)BrickData.SHCoefficientsMobile[i].Data.GetData());
}
for (int32 i = 0; i < UE_ARRAY_COUNT(BrickData.SkyVisibilitySHCoefficients); i++)
{
*(FColor*)&BrickData.SkyVisibilitySHCoefficients[i].Data[LinearDestCellIndex * sizeof(FColor)] = FilteredVolumeLookupReconverted<FColor>(BrickTextureCoordinate, CurrentLevelData.BrickDataDimensions, (const FColor*)BrickData.SkyVisibilitySHCoefficients[i].Data.GetData());
}
for (int32 i = 0; i < UE_ARRAY_COUNT(BrickData.SkyBouncedSHCoefficients); i++)
{
*(FColor*)&BrickData.SkyBouncedSHCoefficients[i].Data[LinearDestCellIndex * sizeof(FColor)] = FilteredVolumeLookupReconverted<FColor>(BrickTextureCoordinate, CurrentLevelData.BrickDataDimensions, (const FColor*)BrickData.SkyBouncedSHCoefficients[i].Data.GetData());
}
*(FFloat3Packed*)&BrickData.LQLightColor.Data[LinearDestCellIndex * sizeof(FFloat3Packed)] = FilteredVolumeLookupReconverted<FFloat3Packed>(BrickTextureCoordinate, CurrentLevelData.BrickDataDimensions, (const FFloat3Packed*)BrickData.LQLightColor.Data.GetData());
*(FColor*)&BrickData.LQLightDirection.Data[LinearDestCellIndex * sizeof(FColor)] = FilteredVolumeLookupReconverted<FColor>(BrickTextureCoordinate, CurrentLevelData.BrickDataDimensions, (const FColor*)BrickData.LQLightDirection.Data.GetData());
if (BrickData.SkyBentNormal.Data.Num() > 0)
{
*(FColor*)&BrickData.SkyBentNormal.Data[LinearDestCellIndex * sizeof(FColor)] = FilteredVolumeLookupReconverted<FColor>(BrickTextureCoordinate, CurrentLevelData.BrickDataDimensions, (const FColor*)BrickData.SkyBentNormal.Data.GetData());
}
*(uint8*)&BrickData.DirectionalLightShadowing.Data[LinearDestCellIndex * sizeof(uint8)] = FilteredVolumeLookupReconverted<uint8>(BrickTextureCoordinate, CurrentLevelData.BrickDataDimensions, (const uint8*)BrickData.DirectionalLightShadowing.Data.GetData());
return true;
}
return false;
}
static NSwarm::TChannelFlags LM_VOLUMETRICLIGHTMAP_CHANNEL_FLAGS = NSwarm::SWARM_JOB_CHANNEL_READ;
void FLightmassProcessor::ImportIrradianceTasks(bool& bGenerateSkyShadowing, TArray<FImportedVolumetricLightmapTaskData>& TaskDataArray)
{
TList<FGuid>* Element = CompletedVolumetricLightmapTasks.ExtractAll();
while (Element)
{
// If this task has not already been imported, import it now
TList<FGuid>* NextElement = Element->Next;
const FString ChannelName = Lightmass::CreateChannelName(Element->Element, Lightmass::LM_VOLUMETRICLIGHTMAP_VERSION, Lightmass::LM_VOLUMETRICLIGHTMAP_EXTENSION);
const int32 Channel = Swarm.OpenChannel(*ChannelName, LM_VOLUMETRICLIGHTMAP_CHANNEL_FLAGS);
if (Channel >= 0)
{
TaskDataArray.AddDefaulted();
FImportedVolumetricLightmapTaskData& NewTaskData = TaskDataArray.Last();
int32 NumBricks;
Swarm.ReadChannel(Channel, &NumBricks, sizeof(NumBricks));
NewTaskData.Bricks.Empty(NumBricks);
for (int32 BrickIndex = 0; BrickIndex < NumBricks; BrickIndex++)
{
NewTaskData.Bricks.AddDefaulted();
FImportedVolumetricLightmapBrick& NewBrick = NewTaskData.Bricks.Last();
Swarm.ReadChannel(Channel, &NewBrick.IntersectingLevelGuid, sizeof(NewBrick.IntersectingLevelGuid));
Swarm.ReadChannel(Channel, &NewBrick.IndirectionTexturePosition, sizeof(NewBrick.IndirectionTexturePosition));
Swarm.ReadChannel(Channel, &NewBrick.TreeDepth, sizeof(NewBrick.TreeDepth));
Swarm.ReadChannel(Channel, &NewBrick.AverageClosestGeometryDistance, sizeof(NewBrick.AverageClosestGeometryDistance));
ReadArray(Channel, NewBrick.AmbientVector);
ReadArray(Channel, NewBrick.SkyBouncedAmbientVector);
for (int32 i = 0; i < UE_ARRAY_COUNT(NewBrick.SHCoefficients); i++)
{
ReadArray(Channel, NewBrick.SHCoefficients[i]);
}
check(NewBrick.AmbientVector.Num() == NewBrick.SHCoefficients[0].Num());
for (int32 i = 0; i < UE_ARRAY_COUNT(NewBrick.SHCoefficientsMobile); i++)
{
NewBrick.SHCoefficientsMobile[i].SetNumUninitialized(NewBrick.SHCoefficients[0].Num());
}
NewBrick.AmbientVectorMobile.SetNumUninitialized(NewBrick.AmbientVector.Num());
for (int32 i = 0; i < NewBrick.SHCoefficients[0].Num(); i++)
{
FLinearColor AmbientColor = NewBrick.AmbientVector[i].ToLinearColor();
NewBrick.AmbientVectorMobile[i] = FColor(NormalizeFloat(AmbientColor.R) * 255, NormalizeFloat(AmbientColor.G) * 255, NormalizeFloat(AmbientColor.B) * 255, NewBrick.SHCoefficients[4][i].B);
NewBrick.SHCoefficientsMobile[0][i] = FColor(NewBrick.SHCoefficients[0][i].R, NewBrick.SHCoefficients[0][i].G, NewBrick.SHCoefficients[0][i].B, NewBrick.SHCoefficients[2][i].R);
NewBrick.SHCoefficientsMobile[1][i] = FColor(NewBrick.SHCoefficients[2][i].G, NewBrick.SHCoefficients[2][i].B, NewBrick.SHCoefficients[4][i].R, NewBrick.SHCoefficients[4][i].G);
}
for (int32 i = 0; i < UE_ARRAY_COUNT(NewBrick.SkyVisibilitySHCoefficients); i++)
{
ReadArray(Channel, NewBrick.SkyVisibilitySHCoefficients[i]);
}
for (int32 i = 0; i < UE_ARRAY_COUNT(NewBrick.SkyBouncedSHCoefficients); i++)
{
ReadArray(Channel, NewBrick.SkyBouncedSHCoefficients[i]);
}
ReadArray(Channel, NewBrick.LQLightColor);
ReadArray(Channel, NewBrick.LQLightDirection);
ReadArray(Channel, NewBrick.SkyBentNormal);
ReadArray(Channel, NewBrick.DirectionalLightShadowing);
bGenerateSkyShadowing = bGenerateSkyShadowing || NewBrick.SkyBentNormal.Num() > 0;
ReadArray(Channel, NewBrick.TaskVoxelImportProcessingData);
}
ImportDebugOutputStruct(Channel);
Swarm.CloseChannel(Channel);
}
else
{
UE_LOG(LogVolumetricLightmapImport, Fatal, TEXT("Error, failed to import volumetric lightmap %s with error code %d"), *ChannelName, Channel);
}
delete Element;
Element = NextElement;
}
}
// One pass needed to cover the trilinear filtering footprint, another pass needed to cover exterior voxels which see backfaces due to the large ray start bias.
int32 NumDilateOverEmbeddedVoxelsPasses = 2;
struct FFilteredBrickData
{
FFloat3Packed AmbientVector;
FFloat3Packed SkyBouncedAmbientVector;
FColor SHCoefficients[UE_ARRAY_COUNT(FImportedVolumetricLightmapBrick::SHCoefficients)];
FColor SkyVisibilitySHCoefficients[UE_ARRAY_COUNT(FImportedVolumetricLightmapBrick::SkyVisibilitySHCoefficients)];
FColor SkyBouncedSHCoefficients[UE_ARRAY_COUNT(FImportedVolumetricLightmapBrick::SkyBouncedSHCoefficients)];
uint8 DirectionalLightShadowing;
};
void FilterWithNeighbors(
const TArray<const FImportedVolumetricLightmapBrick*>& BricksAtCurrentDepth,
int32 BrickStartAllocation,
int32 CurrentDepth,
FIntVector BrickLayoutDimensions,
const Lightmass::FVolumetricLightmapSettings& VolumetricLightmapSettings,
FPrecomputedVolumetricLightmapData& CurrentLevelData,
const TArray<Lightmass::FIrradianceVoxelImportProcessingData>& VoxelImportProcessingData,
TArray<Lightmass::FIrradianceVoxelImportProcessingData>& NewVoxelImportProcessingData)
{
int32 BrickSize = VolumetricLightmapSettings.BrickSize;
int32 PaddedBrickSize = BrickSize + 1;
const int32 BrickSizeLog2 = FMath::FloorLog2(BrickSize);
const float InvBrickSize = 1.0f / BrickSize;
const FBox VolumeBox(VolumetricLightmapSettings.VolumeMin, VolumetricLightmapSettings.VolumeMin + VolumetricLightmapSettings.VolumeSize);
const FVector DebugPositionIndirectionCoordinate = ComputeIndirectionCoordinate(GDebugVoxelPosition, VolumeBox, CurrentLevelData.IndirectionTextureDimensions);
TArray<FFilteredBrickData> FilteredBrickData;
TArray<bool> FilteredBrickDataValid;
FilteredBrickData.Empty(BrickSize * BrickSize * BrickSize);
FilteredBrickData.AddZeroed(BrickSize * BrickSize * BrickSize);
FilteredBrickDataValid.Empty(BrickSize * BrickSize * BrickSize);
FilteredBrickDataValid.AddZeroed(BrickSize * BrickSize * BrickSize);
// Fill in voxels which are inside geometry with their valid neighbors
for (int32 BrickIndex = 0; BrickIndex < BricksAtCurrentDepth.Num(); BrickIndex++)
{
const FImportedVolumetricLightmapBrick& Brick = *BricksAtCurrentDepth[BrickIndex];
// Initialize temporary brick data to invalid
FPlatformMemory::Memzero(FilteredBrickDataValid.GetData(), FilteredBrickDataValid.Num() * FilteredBrickDataValid.GetTypeSize());
checkSlow(Brick.TreeDepth == CurrentDepth);
const int32 DetailCellsPerCurrentLevelBrick = 1 << ((VolumetricLightmapSettings.MaxRefinementLevels - Brick.TreeDepth) * BrickSizeLog2);
const int32 NumBottomLevelBricks = DetailCellsPerCurrentLevelBrick / BrickSize;
const FVector IndirectionTexturePosition = FVector(Brick.IndirectionTexturePosition);
const FIntVector BrickLayoutPosition = ComputeBrickLayoutPosition(BrickStartAllocation + BrickIndex, BrickLayoutDimensions) * PaddedBrickSize;
for (int32 Z = 0; Z < BrickSize; Z++)
{
for (int32 Y = 0; Y < BrickSize; Y++)
{
for (int32 X = 0; X < BrickSize; X++)
{
FIntVector VoxelCoordinate(X, Y, Z);
const int32 LinearDestCellIndex = ComputeLinearVoxelIndex(VoxelCoordinate + BrickLayoutPosition, CurrentLevelData.BrickDataDimensions);
Lightmass::FIrradianceVoxelImportProcessingData VoxelImportData = VoxelImportProcessingData[LinearDestCellIndex];
if (GDebugVoxelPosition != FVector::ZeroVector)
{
const FVector CellIndirectionTexturePosition = IndirectionTexturePosition + FVector(X, Y, Z) * InvBrickSize * NumBottomLevelBricks;
const FBox CellIndirectionTextureBounds(CellIndirectionTexturePosition, CellIndirectionTexturePosition + InvBrickSize * NumBottomLevelBricks);
if (CellIndirectionTextureBounds.IsInside(DebugPositionIndirectionCoordinate))
{
int32 DebugBreakpoint = 0;
}
}
if (VoxelImportData.bInsideGeometry
// Don't modify border voxels
&& !VoxelImportData.bBorderVoxel)
{
//@todo - filter SkyBentNormal from neighbors too
FLinearColor AmbientVector = FLinearColor(0, 0, 0, 0);
FLinearColor SkyBouncedAmbientVector = FLinearColor(0, 0, 0, 0);
FLinearColor SHCoefficients[UE_ARRAY_COUNT(Brick.SHCoefficients)];
float SkyVisibilityAmbientWeight = 0;
float SkyBouncedAmbientWeight = 0;
FLinearColor SkyVisibilitySHCoefficients[UE_ARRAY_COUNT(Brick.SkyVisibilitySHCoefficients)];
FLinearColor SkyBouncedSHCoefficients[UE_ARRAY_COUNT(Brick.SkyBouncedSHCoefficients)];
FLinearColor DirectionalLightShadowing = FLinearColor(0, 0, 0, 0);
for (int32 i = 0; i < UE_ARRAY_COUNT(SHCoefficients); i++)
{
SHCoefficients[i] = FLinearColor(0, 0, 0, 0);
}
for (int32 i = 0; i < UE_ARRAY_COUNT(SkyVisibilitySHCoefficients); i++)
{
SkyVisibilitySHCoefficients[i] = FLinearColor(0, 0, 0, 0);
}
for (int32 i = 0; i < UE_ARRAY_COUNT(SkyBouncedSHCoefficients); i++)
{
SkyBouncedSHCoefficients[i] = FLinearColor(0, 0, 0, 0);
}
float TotalWeight = 0.0f;
for (int32 NeighborZ = -1; NeighborZ <= 1; NeighborZ++)
{
for (int32 NeighborY = -1; NeighborY <= 1; NeighborY++)
{
for (int32 NeighborX = -1; NeighborX <= 1; NeighborX++)
{
const FVector NeighborIndirectionDataSourceCoordinate = IndirectionTexturePosition + FVector(X + NeighborX, Y + NeighborY, Z + NeighborZ) * InvBrickSize * NumBottomLevelBricks;
const FIntVector NeighborVoxelCoordinate(X + NeighborX, Y + NeighborY, Z + NeighborZ);
if (NeighborVoxelCoordinate != VoxelCoordinate && NeighborIndirectionDataSourceCoordinate.X >= 0.0f && NeighborIndirectionDataSourceCoordinate.Y >= 0.0f && NeighborIndirectionDataSourceCoordinate.Z >= 0.0f && NeighborIndirectionDataSourceCoordinate.X < CurrentLevelData.IndirectionTextureDimensions.X && NeighborIndirectionDataSourceCoordinate.Y < CurrentLevelData.IndirectionTextureDimensions.Y && NeighborIndirectionDataSourceCoordinate.Z < CurrentLevelData.IndirectionTextureDimensions.Z)
{
FIntVector IndirectionBrickOffset;
int32 IndirectionBrickSize;
checkSlow(GPixelFormats[CurrentLevelData.IndirectionTexture.Format].BlockBytes == sizeof(uint8) * 4);
SampleIndirectionTexture(NeighborIndirectionDataSourceCoordinate, CurrentLevelData.IndirectionTextureDimensions, CurrentLevelData.IndirectionTexture.Data.GetData(), IndirectionBrickOffset, IndirectionBrickSize);
// Only filter from bricks with equal density, to avoid reading from uninitialized padding
// This causes seams but they fall at density transitions so not noticeable
if (IndirectionBrickSize == NumBottomLevelBricks)
{
const FVector BrickTextureCoordinate = ComputeBrickTextureCoordinate(NeighborIndirectionDataSourceCoordinate, IndirectionBrickOffset, IndirectionBrickSize, BrickSize);
Lightmass::FIrradianceVoxelImportProcessingData NeighborVoxelImportData = NearestVolumeLookup<Lightmass::FIrradianceVoxelImportProcessingData>(BrickTextureCoordinate, CurrentLevelData.BrickDataDimensions, VoxelImportProcessingData.GetData());
if (!NeighborVoxelImportData.bInsideGeometry && !NeighborVoxelImportData.bBorderVoxel)
{
const float Weight = 1.0f / FMath::Max<float>(FMath::Abs(NeighborX) + FMath::Abs(NeighborY) + FMath::Abs(NeighborZ), .5f);
FLinearColor NeighborAmbientVector = FilteredVolumeLookup<FFloat3Packed>(BrickTextureCoordinate, CurrentLevelData.BrickDataDimensions, (const FFloat3Packed*)CurrentLevelData.BrickData.AmbientVector.Data.GetData());
AmbientVector += NeighborAmbientVector * Weight;
for (int32 i = 0; i < UE_ARRAY_COUNT(SHCoefficients); i++)
{
static_assert(UE_ARRAY_COUNT(SHCoefficients) == 6, "Assuming 2 SHCoefficient vectors per color channel");
// Weight by ambient before filtering, normalized SH coefficients don't filter properly
float AmbientCoefficient = NeighborAmbientVector.Component(i / 2);
SHCoefficients[i] += AmbientCoefficient * Weight * FilteredVolumeLookup<FColor>(BrickTextureCoordinate, CurrentLevelData.BrickDataDimensions, (const FColor*)CurrentLevelData.BrickData.SHCoefficients[i].Data.GetData());
}
FLinearColor NeighborAmbient = FilteredVolumeLookup<FColor>(BrickTextureCoordinate, CurrentLevelData.BrickDataDimensions, (const FColor*)CurrentLevelData.BrickData.SkyVisibilitySHCoefficients[0].Data.GetData());
float SkyVisibilityAmbient = NeighborAmbient.R + NeighborAmbient.G * 10 + NeighborAmbient.B * 100 + NeighborAmbient.A * 1000;
SkyVisibilityAmbientWeight += SkyVisibilityAmbient * Weight;
for (int32 i = 1; i < UE_ARRAY_COUNT(SkyVisibilitySHCoefficients); i++)
{
static_assert(UE_ARRAY_COUNT(SkyVisibilitySHCoefficients) == 3, "Assuming 3 SkyVisibilitySHCoefficient vectors per color channel");
// Weight by ambient before filtering, normalized SH coefficients don't filter properly
SkyVisibilitySHCoefficients[i] += SkyVisibilityAmbient * Weight * FilteredVolumeLookup<FColor>(BrickTextureCoordinate, CurrentLevelData.BrickDataDimensions, (const FColor*)CurrentLevelData.BrickData.SkyVisibilitySHCoefficients[i].Data.GetData());
}
FLinearColor SkyBouncedNeighborAmbientVector = FilteredVolumeLookup<FFloat3Packed>(BrickTextureCoordinate, CurrentLevelData.BrickDataDimensions, (const FFloat3Packed*)CurrentLevelData.BrickData.SkyBouncedAmbientVector.Data.GetData());
SkyBouncedAmbientVector += SkyBouncedNeighborAmbientVector * Weight;
for (int32 i = 0; i < UE_ARRAY_COUNT(SkyBouncedSHCoefficients); i++)
{
static_assert(UE_ARRAY_COUNT(SkyBouncedSHCoefficients) == 6, "Assuming 2 SkyBouncedSHCoefficients vectors per color channel");
// Weight by ambient before filtering, normalized SH coefficients don't filter properly
float AmbientCoefficient = SkyBouncedNeighborAmbientVector.Component(i / 2);
SkyBouncedSHCoefficients[i] += AmbientCoefficient * Weight * FilteredVolumeLookup<FColor>(BrickTextureCoordinate, CurrentLevelData.BrickDataDimensions, (const FColor*)CurrentLevelData.BrickData.SkyBouncedSHCoefficients[i].Data.GetData());
}
FLinearColor NeighborDirectionalLightShadowing = FilteredVolumeLookup<uint8>(BrickTextureCoordinate, CurrentLevelData.BrickDataDimensions, (const uint8*)CurrentLevelData.BrickData.DirectionalLightShadowing.Data.GetData());
DirectionalLightShadowing += NeighborDirectionalLightShadowing * Weight;
TotalWeight += Weight;
}
}
}
}
}
}
if (TotalWeight > 0.0f)
{
const int32 LinearVoxelIndex = ComputeLinearVoxelIndex(VoxelCoordinate, FIntVector(BrickSize, BrickSize, BrickSize));
// Store filtered output to temporary brick data to avoid order dependent results between voxels
// This still produces order dependent filtering between neighboring bricks
FilteredBrickDataValid[LinearVoxelIndex] = true;
const float InvTotalWeight = 1.0f / TotalWeight;
const FLinearColor FilteredAmbientColor = AmbientVector * InvTotalWeight;
FilteredBrickData[LinearVoxelIndex].AmbientVector = ConvertFromLinearColor<FFloat3Packed>(FilteredAmbientColor);
for (int32 i = 0; i < UE_ARRAY_COUNT(SHCoefficients); i++)
{
static_assert(UE_ARRAY_COUNT(SHCoefficients) == 6, "Assuming 2 SHCoefficient vectors per color channel");
float AmbientCoefficient = FMath::Max(FilteredAmbientColor.Component(i / 2), KINDA_SMALL_NUMBER);
FilteredBrickData[LinearVoxelIndex].SHCoefficients[i] = ConvertFromLinearColor<FColor>(SHCoefficients[i] * InvTotalWeight / AmbientCoefficient);
}
const float FilteredSkyVisibilityAmbient = SkyVisibilityAmbientWeight * InvTotalWeight;
FilteredBrickData[LinearVoxelIndex].SkyVisibilitySHCoefficients[0] = ConvertFromLinearColor<FColor>(FLinearColor(FMath::Fractional(FilteredSkyVisibilityAmbient),
(int(FilteredSkyVisibilityAmbient) % 10) / 10.0f,
((int(FilteredSkyVisibilityAmbient) / 10) % 10) / 10.0f,
((int(FilteredSkyVisibilityAmbient) / 100) % 10) / 10.0f));
for (int32 i = 1; i < UE_ARRAY_COUNT(SkyVisibilitySHCoefficients); i++)
{
static_assert(UE_ARRAY_COUNT(SkyVisibilitySHCoefficients) == 3, "Assuming 3 SkyVisibilitySHCoefficient vectors per color channel");
float AmbientCoefficient = FMath::Max(FilteredSkyVisibilityAmbient, KINDA_SMALL_NUMBER);
FilteredBrickData[LinearVoxelIndex].SkyVisibilitySHCoefficients[i] = ConvertFromLinearColor<FColor>(SkyVisibilitySHCoefficients[i] * InvTotalWeight / AmbientCoefficient);
}
const FLinearColor FilteredSkyBouncedAmbientColor = SkyBouncedAmbientVector * InvTotalWeight;
FilteredBrickData[LinearVoxelIndex].SkyBouncedAmbientVector = ConvertFromLinearColor<FFloat3Packed>(FilteredSkyBouncedAmbientColor);
for (int32 i = 0; i < UE_ARRAY_COUNT(SkyBouncedSHCoefficients); i++)
{
static_assert(UE_ARRAY_COUNT(SkyBouncedSHCoefficients) == 6, "Assuming 2 SHCoefficient vectors per color channel");
float AmbientCoefficient = FMath::Max(FilteredSkyBouncedAmbientColor.Component(i / 2), KINDA_SMALL_NUMBER);
FilteredBrickData[LinearVoxelIndex].SkyBouncedSHCoefficients[i] = ConvertFromLinearColor<FColor>(SkyBouncedSHCoefficients[i] * InvTotalWeight / AmbientCoefficient);
}
FilteredBrickData[LinearVoxelIndex].DirectionalLightShadowing = ConvertFromLinearColor<uint8>(DirectionalLightShadowing * InvTotalWeight);
}
}
}
}
}
for (int32 Z = 0; Z < BrickSize; Z++)
{
for (int32 Y = 0; Y < BrickSize; Y++)
{
for (int32 X = 0; X < BrickSize; X++)
{
FIntVector VoxelCoordinate(X, Y, Z);
const int32 LinearVoxelIndex = ComputeLinearVoxelIndex(VoxelCoordinate, FIntVector(BrickSize, BrickSize, BrickSize));
// Write filtered voxel data back to the original
if (FilteredBrickDataValid[LinearVoxelIndex])
{
const int32 LinearBrickLayoutCellIndex = ComputeLinearVoxelIndex(VoxelCoordinate + BrickLayoutPosition, CurrentLevelData.BrickDataDimensions);
// Mark as valid for future passes now that we've overwritten with filtered neighbors
NewVoxelImportProcessingData[LinearBrickLayoutCellIndex].bInsideGeometry = false;
FFloat3Packed* DestAmbientVector = (FFloat3Packed*)&CurrentLevelData.BrickData.AmbientVector.Data[LinearBrickLayoutCellIndex * sizeof(FFloat3Packed)];
*DestAmbientVector = FilteredBrickData[LinearVoxelIndex].AmbientVector;
FFloat3Packed* DestSkyBouncedAmbientVector = (FFloat3Packed*)&CurrentLevelData.BrickData.SkyBouncedAmbientVector.Data[LinearBrickLayoutCellIndex * sizeof(FFloat3Packed)];
*DestSkyBouncedAmbientVector = FilteredBrickData[LinearVoxelIndex].SkyBouncedAmbientVector;
for (int32 i = 0; i < UE_ARRAY_COUNT(FilteredBrickData[LinearVoxelIndex].SHCoefficients); i++)
{
FColor* DestCoefficients = (FColor*)&CurrentLevelData.BrickData.SHCoefficients[i].Data[LinearBrickLayoutCellIndex * sizeof(FColor)];
*DestCoefficients = FilteredBrickData[LinearVoxelIndex].SHCoefficients[i];
}
for (int32 i = 0; i < UE_ARRAY_COUNT(FilteredBrickData[LinearVoxelIndex].SkyVisibilitySHCoefficients); i++)
{
FColor* DestCoefficients = (FColor*)&CurrentLevelData.BrickData.SkyVisibilitySHCoefficients[i].Data[LinearBrickLayoutCellIndex * sizeof(FColor)];
*DestCoefficients = FilteredBrickData[LinearVoxelIndex].SkyVisibilitySHCoefficients[i];
}
for (int32 i = 0; i < UE_ARRAY_COUNT(FilteredBrickData[LinearVoxelIndex].SkyBouncedSHCoefficients); i++)
{
FColor* DestCoefficients = (FColor*)&CurrentLevelData.BrickData.SkyBouncedSHCoefficients[i].Data[LinearBrickLayoutCellIndex * sizeof(FColor)];
*DestCoefficients = FilteredBrickData[LinearVoxelIndex].SkyBouncedSHCoefficients[i];
}
uint8* DestDirectionalLightShadowing = (uint8*)&CurrentLevelData.BrickData.DirectionalLightShadowing.Data[LinearBrickLayoutCellIndex * sizeof(uint8)];
*DestDirectionalLightShadowing = FilteredBrickData[LinearVoxelIndex].DirectionalLightShadowing;
}
}
}
}
}
}
void StitchDetailBricksWithLowDensityNeighbors(
const TArray<const FImportedVolumetricLightmapBrick*>& BricksAtCurrentDepth,
int32 BrickStartAllocation,
int32 CurrentDepth,
FIntVector BrickLayoutDimensions,
const Lightmass::FVolumetricLightmapSettings& VolumetricLightmapSettings,
FPrecomputedVolumetricLightmapData& CurrentLevelData)
{
int32 BrickSize = VolumetricLightmapSettings.BrickSize;
int32 PaddedBrickSize = BrickSize + 1;
const int32 BrickSizeLog2 = FMath::FloorLog2(BrickSize);
const float InvBrickSize = 1.0f / BrickSize;
// Stitch all higher density bricks with neighboring lower density bricks
for (int32 BrickIndex = 0; BrickIndex < BricksAtCurrentDepth.Num(); BrickIndex++)
{
const FImportedVolumetricLightmapBrick& Brick = *BricksAtCurrentDepth[BrickIndex];
const FIntVector BrickLayoutPosition = ComputeBrickLayoutPosition(BrickStartAllocation + BrickIndex, BrickLayoutDimensions) * PaddedBrickSize;
const int32 DetailCellsPerCurrentLevelBrick = 1 << ((VolumetricLightmapSettings.MaxRefinementLevels - Brick.TreeDepth) * BrickSizeLog2);
const int32 NumBottomLevelBricks = DetailCellsPerCurrentLevelBrick / BrickSize;
const FVector IndirectionTexturePosition = FVector(Brick.IndirectionTexturePosition);
int32 X, Y, Z = 0;
// Iterate over unique data on the edge of the brick which needs to match padding on lower resolution bricks
for (X = 0, Z = 0; Z < BrickSize; Z++)
{
for (Y = 0; Y < BrickSize; Y++)
{
FVector IndirectionDataSourceCoordinate = IndirectionTexturePosition + FVector(X, Y, Z) * InvBrickSize * NumBottomLevelBricks;
for (int32 StitchDirection = 1; StitchDirection < 8; StitchDirection++)
{
FVector StitchSourceCoordinate = IndirectionDataSourceCoordinate;
if ((StitchDirection & 1) && X == 0)
{
StitchSourceCoordinate.X -= GPointFilteringThreshold * 2;
}
if ((StitchDirection & 2) && Y == 0)
{
StitchSourceCoordinate.Y -= GPointFilteringThreshold * 2;
}
if ((StitchDirection & 4) && Z == 0)
{
StitchSourceCoordinate.Z -= GPointFilteringThreshold * 2;
}
if (StitchSourceCoordinate != IndirectionDataSourceCoordinate)
{
bool bStitched = CopyFromBrickmapTexel(
StitchSourceCoordinate,
FIntVector(X, Y, Z),
// Restrict copies to only read from bricks that are lower effective resolution (higher NumBottomLevelBricks)
NumBottomLevelBricks,
BrickSize,
BrickLayoutPosition,
CurrentLevelData,
CurrentLevelData.BrickData);
if (bStitched)
{
break;
}
}
}
}
}
for (Z = 0, Y = 0; Y < BrickSize; Y++)
{
for (X = 1; X < BrickSize; X++)
{
FVector IndirectionDataSourceCoordinate = IndirectionTexturePosition + FVector(X, Y, Z) * InvBrickSize * NumBottomLevelBricks;
for (int32 StitchDirection = 1; StitchDirection < 8; StitchDirection++)
{
FVector StitchSourceCoordinate = IndirectionDataSourceCoordinate;
if ((StitchDirection & 1) && X == 0)
{
StitchSourceCoordinate.X -= GPointFilteringThreshold * 2;
}
if ((StitchDirection & 2) && Y == 0)
{
StitchSourceCoordinate.Y -= GPointFilteringThreshold * 2;
}
if ((StitchDirection & 4) && Z == 0)
{
StitchSourceCoordinate.Z -= GPointFilteringThreshold * 2;
}
if (StitchSourceCoordinate != IndirectionDataSourceCoordinate)
{
bool bStitched = CopyFromBrickmapTexel(
StitchSourceCoordinate,
FIntVector(X, Y, Z),
// Restrict copies to only read from bricks that are lower effective resolution (higher NumBottomLevelBricks)
NumBottomLevelBricks,
BrickSize,
BrickLayoutPosition,
CurrentLevelData,
CurrentLevelData.BrickData);
if (bStitched)
{
break;
}
}
}
}
}
for (Y = 0, Z = 1; Z < BrickSize; Z++)
{
for (X = 1; X < BrickSize; X++)
{
FVector IndirectionDataSourceCoordinate = IndirectionTexturePosition + FVector(X, Y, Z) * InvBrickSize * NumBottomLevelBricks;
for (int32 StitchDirection = 1; StitchDirection < 8; StitchDirection++)
{
FVector StitchSourceCoordinate = IndirectionDataSourceCoordinate;
if ((StitchDirection & 1) && X == 0)
{
StitchSourceCoordinate.X -= GPointFilteringThreshold * 2;
}
if ((StitchDirection & 2) && Y == 0)
{
StitchSourceCoordinate.Y -= GPointFilteringThreshold * 2;
}
if ((StitchDirection & 4) && Z == 0)
{
StitchSourceCoordinate.Z -= GPointFilteringThreshold * 2;
}
if (StitchSourceCoordinate != IndirectionDataSourceCoordinate)
{
bool bStitched = CopyFromBrickmapTexel(
StitchSourceCoordinate,
FIntVector(X, Y, Z),
// Restrict copies to only read from bricks that are lower effective resolution (higher NumBottomLevelBricks)
NumBottomLevelBricks,
BrickSize,
BrickLayoutPosition,
CurrentLevelData,
CurrentLevelData.BrickData);
if (bStitched)
{
break;
}
}
}
}
}
}
}
void CopyPaddingFromUniqueData(
const TArray<const FImportedVolumetricLightmapBrick*>& BricksAtCurrentDepth,
int32 BrickStartAllocation,
int32 CurrentDepth,
FIntVector BrickLayoutDimensions,
const Lightmass::FVolumetricLightmapSettings& VolumetricLightmapSettings,
const FPrecomputedVolumetricLightmapData& CurrentLevelData,
FVolumetricLightmapBrickData& BrickData)
{
int32 BrickSize = VolumetricLightmapSettings.BrickSize;
int32 PaddedBrickSize = BrickSize + 1;
const int32 BrickSizeLog2 = FMath::FloorLog2(BrickSize);
const float InvBrickSize = 1.0f / BrickSize;
for (int32 BrickIndex = 0; BrickIndex < BricksAtCurrentDepth.Num(); BrickIndex++)
{
const FImportedVolumetricLightmapBrick& Brick = *BricksAtCurrentDepth[BrickIndex];
const FIntVector BrickLayoutPosition = ComputeBrickLayoutPosition(BrickStartAllocation + BrickIndex, BrickLayoutDimensions) * PaddedBrickSize;
const int32 DetailCellsPerCurrentLevelBrick = 1 << ((VolumetricLightmapSettings.MaxRefinementLevels - Brick.TreeDepth) * BrickSizeLog2);
const int32 NumBottomLevelBricks = DetailCellsPerCurrentLevelBrick / BrickSize;
const FVector IndirectionTexturePosition = FVector(Brick.IndirectionTexturePosition);
int32 X, Y, Z = 0;
// Iterate over padding voxels
for (X = PaddedBrickSize - 1, Z = 0; Z < PaddedBrickSize; Z++)
{
for (Y = 0; Y < PaddedBrickSize; Y++)
{
const FVector IndirectionDataSourceCoordinate = IndirectionTexturePosition + FVector(X, Y, Z) * InvBrickSize * NumBottomLevelBricks;
// Overwrite padding with unique data from this same coordinate in the indirection texture
CopyFromBrickmapTexel(
IndirectionDataSourceCoordinate,
FIntVector(X, Y, Z),
0,
BrickSize,
BrickLayoutPosition,
CurrentLevelData,
BrickData);
}
}
for (Z = PaddedBrickSize - 1, Y = 0; Y < PaddedBrickSize; Y++)
{
for (X = 0; X < PaddedBrickSize; X++)
{
const FVector IndirectionDataSourceCoordinate = IndirectionTexturePosition + FVector(X, Y, Z) * InvBrickSize * NumBottomLevelBricks;
CopyFromBrickmapTexel(
IndirectionDataSourceCoordinate,
FIntVector(X, Y, Z),
0,
BrickSize,
BrickLayoutPosition,
CurrentLevelData,
BrickData);
}
}
for (Y = PaddedBrickSize - 1, Z = 0; Z < PaddedBrickSize; Z++)
{
for (X = 0; X < PaddedBrickSize; X++)
{
const FVector IndirectionDataSourceCoordinate = IndirectionTexturePosition + FVector(X, Y, Z) * InvBrickSize * NumBottomLevelBricks;
CopyFromBrickmapTexel(
IndirectionDataSourceCoordinate,
FIntVector(X, Y, Z),
0,
BrickSize,
BrickLayoutPosition,
CurrentLevelData,
BrickData);
}
}
}
}
FVector GetLookupPositionAwayFromBorder(int32 PaddedBrickSize, FIntVector LocalCellCoordinate)
{
FVector LookupCoordinate(LocalCellCoordinate);
if (LocalCellCoordinate.X == PaddedBrickSize - 1)
{
LookupCoordinate.X -= 1;
}
if (LocalCellCoordinate.Y == PaddedBrickSize - 1)
{
LookupCoordinate.Y -= 1;
}
if (LocalCellCoordinate.Z == PaddedBrickSize - 1)
{
LookupCoordinate.Z -= 1;
}
return LookupCoordinate;
}
void CopyVolumeBorderFromInterior(
const TArray<const FImportedVolumetricLightmapBrick*>& BricksAtCurrentDepth,
int32 BrickStartAllocation,
int32 CurrentDepth,
FIntVector BrickLayoutDimensions,
const Lightmass::FVolumetricLightmapSettings& VolumetricLightmapSettings,
FPrecomputedVolumetricLightmapData& CurrentLevelData,
FVolumetricLightmapBrickData& BrickData)
{
int32 BrickSize = VolumetricLightmapSettings.BrickSize;
int32 PaddedBrickSize = BrickSize + 1;
const int32 BrickSizeLog2 = FMath::FloorLog2(BrickSize);
const float InvBrickSize = 1.0f / BrickSize;
for (int32 BrickIndex = 0; BrickIndex < BricksAtCurrentDepth.Num(); BrickIndex++)
{
const FImportedVolumetricLightmapBrick& Brick = *BricksAtCurrentDepth[BrickIndex];
const FIntVector BrickLayoutPosition = ComputeBrickLayoutPosition(BrickStartAllocation + BrickIndex, BrickLayoutDimensions) * PaddedBrickSize;
const int32 DetailCellsPerCurrentLevelBrick = 1 << ((VolumetricLightmapSettings.MaxRefinementLevels - Brick.TreeDepth) * BrickSizeLog2);
const int32 NumBottomLevelBricks = DetailCellsPerCurrentLevelBrick / BrickSize;
const FVector IndirectionTexturePosition = FVector(Brick.IndirectionTexturePosition);
// Operate on bricks on the edge of the volume covered by the indirection texture
if (Brick.IndirectionTexturePosition.X + NumBottomLevelBricks == CurrentLevelData.IndirectionTextureDimensions.X)
{
int32 X, Y, Z = 0;
// Iterate over padding voxels
for (X = PaddedBrickSize - 1, Z = 0; Z < PaddedBrickSize; Z++)
{
for (Y = 0; Y < PaddedBrickSize; Y++)
{
const FVector LookupPosition = GetLookupPositionAwayFromBorder(PaddedBrickSize, FIntVector(X, Y, Z));
const FVector IndirectionDataSourceCoordinate = IndirectionTexturePosition + LookupPosition * InvBrickSize * NumBottomLevelBricks;
// Overwrite padding on the edge of the volume with neighboring data inside the volume
CopyFromBrickmapTexel(
IndirectionDataSourceCoordinate,
FIntVector(X, Y, Z),
0,
BrickSize,
BrickLayoutPosition,
CurrentLevelData,
BrickData);
}
}
}
if (Brick.IndirectionTexturePosition.Y + NumBottomLevelBricks == CurrentLevelData.IndirectionTextureDimensions.Y)
{
int32 X, Y, Z = 0;
// Iterate over padding voxels
for (Y = PaddedBrickSize - 1, Z = 0; Z < PaddedBrickSize; Z++)
{
for (X = 0; X < PaddedBrickSize; X++)
{
const FVector LookupPosition = GetLookupPositionAwayFromBorder(PaddedBrickSize, FIntVector(X, Y, Z));
const FVector IndirectionDataSourceCoordinate = IndirectionTexturePosition + LookupPosition * InvBrickSize * NumBottomLevelBricks;
CopyFromBrickmapTexel(
IndirectionDataSourceCoordinate,
FIntVector(X, Y, Z),
0,
BrickSize,
BrickLayoutPosition,
CurrentLevelData,
BrickData);
}
}
}
if (Brick.IndirectionTexturePosition.Z + NumBottomLevelBricks == CurrentLevelData.IndirectionTextureDimensions.Z)
{
int32 X, Y, Z = 0;
// Iterate over padding voxels
for (Z = PaddedBrickSize - 1, Y = 0; Y < PaddedBrickSize; Y++)
{
for (X = 0; X < PaddedBrickSize; X++)
{
const FVector LookupPosition = GetLookupPositionAwayFromBorder(PaddedBrickSize, FIntVector(X, Y, Z));
const FVector IndirectionDataSourceCoordinate = IndirectionTexturePosition + LookupPosition * InvBrickSize * NumBottomLevelBricks;
CopyFromBrickmapTexel(
IndirectionDataSourceCoordinate,
FIntVector(X, Y, Z),
0,
BrickSize,
BrickLayoutPosition,
CurrentLevelData,
BrickData);
}
}
}
}
}
int32 TrimBricksByInterpolationError(
TArray<TArray<const FImportedVolumetricLightmapBrick*>>& BricksByDepth,
const Lightmass::FVolumetricLightmapSettings& VolumetricLightmapSettings,
bool onlyBakeSkyVisibility)
{
int32 NumBricksRemoved = 0;
if (onlyBakeSkyVisibility)
return NumBricksRemoved;
if (VolumetricLightmapSettings.MaxRefinementLevels > 1)
{
TArray<const FImportedVolumetricLightmapBrick*>& HighestDensityBricks = BricksByDepth[VolumetricLightmapSettings.MaxRefinementLevels - 1];
const int32 ParentLevel = VolumetricLightmapSettings.MaxRefinementLevels - 2;
TArray<const FImportedVolumetricLightmapBrick*>& ParentLevelBricks = BricksByDepth[ParentLevel];
const int32 BrickSize = VolumetricLightmapSettings.BrickSize;
const float InvTotalBrickSize = 1.0f / (BrickSize * BrickSize * BrickSize);
const int32 BrickSizeLog2 = FMath::FloorLog2(BrickSize);
const int32 DetailCellsPerParentLevelBrick = 1 << ((VolumetricLightmapSettings.MaxRefinementLevels - ParentLevel) * BrickSizeLog2);
const int32 NumParentBottomLevelBricks = DetailCellsPerParentLevelBrick / BrickSize;
for (int32 BrickIndex = 0; BrickIndex < HighestDensityBricks.Num(); BrickIndex++)
{
const FImportedVolumetricLightmapBrick& Brick = *HighestDensityBricks[BrickIndex];
const FImportedVolumetricLightmapBrick* ParentBrick = NULL;
for (int32 ParentBrickIndex = 0; ParentBrickIndex < ParentLevelBricks.Num(); ParentBrickIndex++)
{
const FImportedVolumetricLightmapBrick& ParentLevelBrick = *ParentLevelBricks[ParentBrickIndex];
if (Brick.IndirectionTexturePosition.X >= ParentLevelBrick.IndirectionTexturePosition.X && Brick.IndirectionTexturePosition.Y >= ParentLevelBrick.IndirectionTexturePosition.Y && Brick.IndirectionTexturePosition.Z >= ParentLevelBrick.IndirectionTexturePosition.Z && Brick.IndirectionTexturePosition.X < ParentLevelBrick.IndirectionTexturePosition.X + NumParentBottomLevelBricks && Brick.IndirectionTexturePosition.Y < ParentLevelBrick.IndirectionTexturePosition.Y + NumParentBottomLevelBricks && Brick.IndirectionTexturePosition.Z < ParentLevelBrick.IndirectionTexturePosition.Z + NumParentBottomLevelBricks)
{
ParentBrick = &ParentLevelBrick;
break;
}
}
check(ParentBrick);
FVector ErrorSquared(0, 0, 0);
const FVector ChildOffset = FVector(Brick.IndirectionTexturePosition - ParentBrick->IndirectionTexturePosition);
for (int32 Z = 0; Z < BrickSize; Z++)
{
for (int32 Y = 0; Y < BrickSize; Y++)
{
for (int32 X = 0; X < BrickSize; X++)
{
const FIntVector VoxelCoordinate(X, Y, Z);
const int32 LinearVoxelIndex = ComputeLinearVoxelIndex(VoxelCoordinate, FIntVector(BrickSize, BrickSize, BrickSize));
const FVector AmbientVector = FVector(Brick.AmbientVector[LinearVoxelIndex].ToLinearColor());
const FVector ParentCoordinate = FVector(VoxelCoordinate) / (float)BrickSize + ChildOffset;
const FVector ParentAmbientVector = FVector(FilteredVolumeLookup(ParentCoordinate, FIntVector(BrickSize, BrickSize, BrickSize), ParentBrick->AmbientVector.GetData()));
ErrorSquared += (AmbientVector - ParentAmbientVector) * (AmbientVector - ParentAmbientVector);
}
}
}
const float RMSE = FMath::Sqrt((ErrorSquared * InvTotalBrickSize).GetMax());
const bool bCullBrick = RMSE < VolumetricLightmapSettings.MinBrickError;
if (bCullBrick)
{
HighestDensityBricks.RemoveAt(BrickIndex);
BrickIndex--;
NumBricksRemoved++;
}
}
}
return NumBricksRemoved;
}
int32 TrimBricksForMemoryLimit(
TArray<TArray<const FImportedVolumetricLightmapBrick*>>& BricksByDepth,
const Lightmass::FVolumetricLightmapSettings& VolumetricLightmapSettings,
int32 VoxelSizeBytes,
const float MaximumBrickMemoryMb)
{
int32 NumBricksBeforeTrimming = 0;
for (int32 CurrentDepth = 0; CurrentDepth < VolumetricLightmapSettings.MaxRefinementLevels; CurrentDepth++)
{
const TArray<const FImportedVolumetricLightmapBrick*>& BricksAtCurrentDepth = BricksByDepth[CurrentDepth];
NumBricksBeforeTrimming += BricksAtCurrentDepth.Num();
}
const int32 PaddedBrickSize = VolumetricLightmapSettings.BrickSize + 1;
TArray<const FImportedVolumetricLightmapBrick*>& HighestDensityBricks = BricksByDepth[VolumetricLightmapSettings.MaxRefinementLevels - 1];
const uint64 BrickSizeBytes = VoxelSizeBytes * PaddedBrickSize * PaddedBrickSize * PaddedBrickSize;
const uint64 MaxBrickBytes = MaximumBrickMemoryMb * 1024 * 1024;
check(FMath::DivideAndRoundUp(MaxBrickBytes, BrickSizeBytes) <= 0x7FFFFFFFull);
const int32 NumBricksBudgeted = (int32)FMath::DivideAndRoundUp(MaxBrickBytes, BrickSizeBytes);
const int32 NumBricksToRemove = FMath::Clamp<int32>(NumBricksBeforeTrimming - NumBricksBudgeted, 0, HighestDensityBricks.Num());
if (NumBricksToRemove > 0)
{
struct FCompareBricksByVisualImpact
{
FORCEINLINE bool operator()(const FImportedVolumetricLightmapBrick& A, const FImportedVolumetricLightmapBrick& B) const
{
// Sort smallest to largest
return A.AverageClosestGeometryDistance < B.AverageClosestGeometryDistance;
}
};
HighestDensityBricks.Sort(FCompareBricksByVisualImpact());
HighestDensityBricks.RemoveAt(HighestDensityBricks.Num() - NumBricksToRemove, NumBricksToRemove);
}
return NumBricksToRemove;
}
void BuildIndirectionTexture(
const TArray<TArray<const FImportedVolumetricLightmapBrick*>>& BricksByDepth,
const Lightmass::FVolumetricLightmapSettings& VolumetricLightmapSettings,
int32 MaxBricksInLayoutOneDim,
FIntVector BrickLayoutDimensions,
int32 IndirectionTextureDataStride,
FPrecomputedVolumetricLightmapData& CurrentLevelData,
bool bOnlyBuildForPersistentLevel)
{
const int32 BrickSizeLog2 = FMath::FloorLog2(VolumetricLightmapSettings.BrickSize);
int32 BrickAllocation = 0;
for (int32 CurrentDepth = 0; CurrentDepth < VolumetricLightmapSettings.MaxRefinementLevels; CurrentDepth++)
{
const TArray<const FImportedVolumetricLightmapBrick*>& BricksAtCurrentDepth = BricksByDepth[CurrentDepth];
for (int32 BrickIndex = 0; BrickIndex < BricksAtCurrentDepth.Num(); BrickIndex++)
{
const FImportedVolumetricLightmapBrick& Brick = *BricksAtCurrentDepth[BrickIndex];
if (CurrentDepth == VolumetricLightmapSettings.MaxRefinementLevels - 1)
{
const FGuid PersistentLevelGuid = FGuid(0, 0, 0, 0);
// Skip non-intersecting, bottom detailed bricks for persistent level
if (bOnlyBuildForPersistentLevel && Brick.IntersectingLevelGuid != PersistentLevelGuid)
{
continue;
}
}
const FIntVector BrickLayoutPosition = ComputeBrickLayoutPosition(BrickAllocation, BrickLayoutDimensions);
check(BrickLayoutPosition.X < MaxBricksInLayoutOneDim && BrickLayoutPosition.Y < MaxBricksInLayoutOneDim && BrickLayoutPosition.Z < MaxBricksInLayoutOneDim);
checkSlow(IndirectionTextureDataStride == sizeof(uint8) * 4);
const int32 DetailCellsPerCurrentLevelBrick = 1 << ((VolumetricLightmapSettings.MaxRefinementLevels - Brick.TreeDepth) * BrickSizeLog2);
const int32 NumBottomLevelBricks = DetailCellsPerCurrentLevelBrick / VolumetricLightmapSettings.BrickSize;
check(NumBottomLevelBricks < MaxBricksInLayoutOneDim);
for (int32 Z = 0; Z < NumBottomLevelBricks; Z++)
{
for (int32 Y = 0; Y < NumBottomLevelBricks; Y++)
{
for (int32 X = 0; X < NumBottomLevelBricks; X++)
{
const FIntVector IndirectionDestDataCoordinate = Brick.IndirectionTexturePosition + FIntVector(X, Y, Z);
const int32 IndirectionDestDataIndex = ((IndirectionDestDataCoordinate.Z * CurrentLevelData.IndirectionTextureDimensions.Y) + IndirectionDestDataCoordinate.Y) * CurrentLevelData.IndirectionTextureDimensions.X + IndirectionDestDataCoordinate.X;
uint8* IndirectionVoxelPtr = (uint8*)&CurrentLevelData.IndirectionTexture.Data[IndirectionDestDataIndex * IndirectionTextureDataStride];
*(IndirectionVoxelPtr + 0) = BrickLayoutPosition.X;
*(IndirectionVoxelPtr + 1) = BrickLayoutPosition.Y;
*(IndirectionVoxelPtr + 2) = BrickLayoutPosition.Z;
*(IndirectionVoxelPtr + 3) = NumBottomLevelBricks;
}
}
}
BrickAllocation++;
}
}
}
inline void ConvertBGRA8ToRGBA8ForLayer(FVolumetricLightmapDataLayer& Layer)
{
if (Layer.Format == PF_B8G8R8A8)
{
for (int32 PixelIndex = 0; PixelIndex < Layer.Data.Num() / GPixelFormats[PF_B8G8R8A8].BlockBytes; PixelIndex++)
{
FColor Color;
Color.B = Layer.Data[PixelIndex * 4 + 0];
Color.G = Layer.Data[PixelIndex * 4 + 1];
Color.R = Layer.Data[PixelIndex * 4 + 2];
Color.A = Layer.Data[PixelIndex * 4 + 3];
Layer.Data[PixelIndex * 4 + 0] = Color.R;
Layer.Data[PixelIndex * 4 + 1] = Color.G;
Layer.Data[PixelIndex * 4 + 2] = Color.B;
Layer.Data[PixelIndex * 4 + 3] = Color.A;
}
Layer.Format = PF_R8G8B8A8;
}
}
// For debugging
bool bStitchDetailBricksWithLowDensityNeighbors = true;
bool bCopyPaddingFromUniqueData = true;
bool bCopyVolumeBorderFromInterior = true;
void FLightmassProcessor::ImportVolumetricLightmap()
{
const double StartTime = FPlatformTime::Seconds();
Lightmass::FVolumetricLightmapSettings VolumetricLightmapSettings;
GetLightmassExporter()->SetVolumetricLightmapSettings(VolumetricLightmapSettings);
const int32 BrickSize = VolumetricLightmapSettings.BrickSize;
const int32 PaddedBrickSize = VolumetricLightmapSettings.BrickSize + 1;
const int32 MaxBricksInLayoutOneDim = 1 << 8;
const int32 MaxBrickTextureLayoutSize = PaddedBrickSize * MaxBricksInLayoutOneDim;
FGuid PersistentLevelGuid = FGuid(0, 0, 0, 0);
IConsoleVariable* Variable = IConsoleManager::Get().FindConsoleVariable(TEXT("r.VolumericLightmapNumOneFrame"));
if (Variable)
{
Variable->Set(0, ECVF_SetByCode);
}
if (System.GetWorld()->PersistentLevel->GetWorldSettings()->bEnableWorldComposition && System.GetWorld()->PersistentLevel->GetWorldSettings()->bEnableMultiVolumetricLightmap)
{
for (TActorIterator<AActor> It(System.GetWorld(), ALightmassImportanceVolume::StaticClass()); It; ++It)
{
AActor* Actor = *It;
PersistentLevelGuid = *Exporter->LevelGuids.FindKey(Actor->GetLevel());
break;
}
}
TArray<FImportedVolumetricLightmapTaskData> TaskDataArray;
TaskDataArray.Empty(Exporter->VolumetricLightmapTaskGuids.Num());
bool bGenerateSkyShadowing = false;
int32 LocalNumCompletedVolumetricLightmapTasks = FPlatformAtomics::AtomicRead(&NumCompletedVolumetricLightmapTasks);
if (Exporter->VolumetricLightmapTaskGuids.Num() != LocalNumCompletedVolumetricLightmapTasks)
{
FMessageLog("LightingResults").Error(FText::Format(LOCTEXT("LightmassError_VolumetricLightmapImportFailed_NotAllCompleted", "Import Volumetric Lightmap failed: Expected {0} tasks, only {1} were reported as completed from Swarm"), FText::AsNumber(Exporter->VolumetricLightmapTaskGuids.Num()), FText::AsNumber(LocalNumCompletedVolumetricLightmapTasks)));
return;
}
ImportIrradianceTasks(bGenerateSkyShadowing, TaskDataArray);
if (TaskDataArray.Num() != Exporter->VolumetricLightmapTaskGuids.Num())
{
FMessageLog("LightingResults").Error(FText::Format(LOCTEXT("LightmassError_VolumetricLightmapImportFailed_FewerThanExpectedImported", "Import Volumetric Lightmap failed: Expected {0} tasks, only found {1}"), FText::AsNumber(Exporter->VolumetricLightmapTaskGuids.Num()), FText::AsNumber(TaskDataArray.Num())));
return;
}
TArray<TArray<const FImportedVolumetricLightmapBrick*>> BricksByDepth;
BricksByDepth.Empty(VolumetricLightmapSettings.MaxRefinementLevels);
BricksByDepth.AddDefaulted(VolumetricLightmapSettings.MaxRefinementLevels);
for (int32 TaskDataIndex = 0; TaskDataIndex < TaskDataArray.Num(); TaskDataIndex++)
{
const FImportedVolumetricLightmapTaskData& TaskData = TaskDataArray[TaskDataIndex];
for (int32 BrickIndex = 0; BrickIndex < TaskData.Bricks.Num(); BrickIndex++)
{
const FImportedVolumetricLightmapBrick& Brick = TaskData.Bricks[BrickIndex];
BricksByDepth[Brick.TreeDepth].Add(&Brick);
}
}
FPrecomputedVolumetricLightmapData CurrentLevelData;
{
CurrentLevelData.InitializeOnImport(FBox(VolumetricLightmapSettings.VolumeMin, VolumetricLightmapSettings.VolumeMin + VolumetricLightmapSettings.VolumeSize), BrickSize);
// Temporarily assign format as PF_B8G8R8A8 since that matches FColor and makes our operations easier
// Will be converted to PF_R8G8B8A8 by ConvertBGRA8ToRGBA8ForLayer() later
CurrentLevelData.BrickData.AmbientVector.Format = PF_FloatR11G11B10;
CurrentLevelData.BrickData.SkyBouncedAmbientVector.Format = PF_FloatR11G11B10;
CurrentLevelData.BrickData.AmbientVectorMobile.Format = PF_B8G8R8A8;
CurrentLevelData.BrickData.SkyBentNormal.Format = PF_B8G8R8A8;
CurrentLevelData.BrickData.DirectionalLightShadowing.Format = PF_G8;
for (int32 i = 0; i < UE_ARRAY_COUNT(CurrentLevelData.BrickData.SHCoefficients); i++)
{
CurrentLevelData.BrickData.SHCoefficients[i].Format = PF_B8G8R8A8;
}
for (int32 i = 0; i < UE_ARRAY_COUNT(CurrentLevelData.BrickData.SHCoefficientsMobile); i++)
{
CurrentLevelData.BrickData.SHCoefficientsMobile[i].Format = PF_B8G8R8A8;
}
for (int32 i = 0; i < UE_ARRAY_COUNT(CurrentLevelData.BrickData.SkyVisibilitySHCoefficients); ++i)
{
CurrentLevelData.BrickData.SkyVisibilitySHCoefficients[i].Format = PF_B8G8R8A8;
}
for (int32 i = 0; i < UE_ARRAY_COUNT(CurrentLevelData.BrickData.SkyBouncedSHCoefficients); ++i)
{
CurrentLevelData.BrickData.SkyBouncedSHCoefficients[i].Format = PF_B8G8R8A8;
}
CurrentLevelData.BrickData.LQLightColor.Format = PF_FloatR11G11B10;
CurrentLevelData.BrickData.LQLightDirection.Format = PF_B8G8R8A8;
}
const bool onlyBakeSkyVisibility = System.GetWorld()->GetWorldSettings()->LightmassSettings.OnlyBakeSkyVisibility;
const int32 NumBottomLevelBricksTrimmedByInterpolationError = TrimBricksByInterpolationError(BricksByDepth, VolumetricLightmapSettings, onlyBakeSkyVisibility);
const float MaximumBrickMemoryMb = System.GetWorld()->GetWorldSettings()->LightmassSettings.VolumetricLightmapMaximumBrickMemoryMb;
const int32 NumBottomLevelBricksTrimmedForMemoryLimit = TrimBricksForMemoryLimit(BricksByDepth, VolumetricLightmapSettings, CurrentLevelData.BrickData.GetMinimumVoxelSize(), MaximumBrickMemoryMb);
int32 BrickTextureLinearAllocator = 0;
for (int32 CurrentDepth = 0; CurrentDepth < VolumetricLightmapSettings.MaxRefinementLevels; CurrentDepth++)
{
const TArray<const FImportedVolumetricLightmapBrick*>& BricksAtCurrentDepth = BricksByDepth[CurrentDepth];
BrickTextureLinearAllocator += BricksAtCurrentDepth.Num();
}
FIntVector BrickLayoutDimensions;
BrickLayoutDimensions.X = FMath::Min(BrickTextureLinearAllocator, MaxBricksInLayoutOneDim);
BrickTextureLinearAllocator = FMath::DivideAndRoundUp(BrickTextureLinearAllocator, BrickLayoutDimensions.X);
BrickLayoutDimensions.Y = FMath::Min(BrickTextureLinearAllocator, MaxBricksInLayoutOneDim);
BrickTextureLinearAllocator = FMath::DivideAndRoundUp(BrickTextureLinearAllocator, BrickLayoutDimensions.Y);
BrickLayoutDimensions.Z = FMath::Min(BrickTextureLinearAllocator, MaxBricksInLayoutOneDim);
const int32 BrickSizeLog2 = FMath::FloorLog2(BrickSize);
const int32 DetailCellsPerTopLevelBrick = 1 << (VolumetricLightmapSettings.MaxRefinementLevels * BrickSizeLog2);
const int32 IndirectionCellsPerTopLevelCell = DetailCellsPerTopLevelBrick / BrickSize;
int32 IndirectionTextureDataStride;
{
CurrentLevelData.IndirectionTextureDimensions = VolumetricLightmapSettings.TopLevelGridSize * IndirectionCellsPerTopLevelCell;
CurrentLevelData.IndirectionTexture.Format = PF_R8G8B8A8_UINT;
IndirectionTextureDataStride = GPixelFormats[CurrentLevelData.IndirectionTexture.Format].BlockBytes;
const int32 TotalIndirectionTextureSize = CurrentLevelData.IndirectionTextureDimensions.X * CurrentLevelData.IndirectionTextureDimensions.Y * CurrentLevelData.IndirectionTextureDimensions.Z;
CurrentLevelData.IndirectionTexture.Resize(TotalIndirectionTextureSize * IndirectionTextureDataStride);
}
BuildIndirectionTexture(BricksByDepth, VolumetricLightmapSettings, MaxBricksInLayoutOneDim, BrickLayoutDimensions, IndirectionTextureDataStride, CurrentLevelData, false);
TArray<Lightmass::FIrradianceVoxelImportProcessingData> VoxelImportProcessingData;
{
CurrentLevelData.BrickDataDimensions = BrickLayoutDimensions * PaddedBrickSize;
const int32 TotalBrickDataSize = CurrentLevelData.BrickDataDimensions.X * CurrentLevelData.BrickDataDimensions.Y * CurrentLevelData.BrickDataDimensions.Z;
CurrentLevelData.BrickData.AmbientVector.Resize(TotalBrickDataSize * GPixelFormats[CurrentLevelData.BrickData.AmbientVector.Format].BlockBytes);
CurrentLevelData.BrickData.SkyBouncedAmbientVector.Resize(TotalBrickDataSize * GPixelFormats[CurrentLevelData.BrickData.SkyBouncedAmbientVector.Format].BlockBytes);
CurrentLevelData.BrickData.AmbientVectorMobile.Resize(TotalBrickDataSize * GPixelFormats[CurrentLevelData.BrickData.AmbientVectorMobile.Format].BlockBytes);
if (bGenerateSkyShadowing)
{
CurrentLevelData.BrickData.SkyBentNormal.Resize(TotalBrickDataSize * GPixelFormats[CurrentLevelData.BrickData.SkyBentNormal.Format].BlockBytes);
}
CurrentLevelData.BrickData.DirectionalLightShadowing.Resize(TotalBrickDataSize * GPixelFormats[CurrentLevelData.BrickData.DirectionalLightShadowing.Format].BlockBytes);
for (int32 i = 0; i < UE_ARRAY_COUNT(CurrentLevelData.BrickData.SHCoefficients); i++)
{
const int32 Stride = GPixelFormats[CurrentLevelData.BrickData.SHCoefficients[i].Format].BlockBytes;
CurrentLevelData.BrickData.SHCoefficients[i].Resize(TotalBrickDataSize * Stride);
}
for (int32 i = 0; i < UE_ARRAY_COUNT(CurrentLevelData.BrickData.SHCoefficientsMobile); i++)
{
const int32 Stride = GPixelFormats[CurrentLevelData.BrickData.SHCoefficientsMobile[i].Format].BlockBytes;
CurrentLevelData.BrickData.SHCoefficientsMobile[i].Resize(TotalBrickDataSize * Stride);
}
for (int32 i = 0; i < UE_ARRAY_COUNT(CurrentLevelData.BrickData.SkyVisibilitySHCoefficients); i++)
{
CurrentLevelData.BrickData.SkyVisibilitySHCoefficients[i].Resize(TotalBrickDataSize * GPixelFormats[CurrentLevelData.BrickData.SkyVisibilitySHCoefficients[i].Format].BlockBytes);
}
for (int32 i = 0; i < UE_ARRAY_COUNT(CurrentLevelData.BrickData.SkyBouncedSHCoefficients); i++)
{
CurrentLevelData.BrickData.SkyBouncedSHCoefficients[i].Resize(TotalBrickDataSize * GPixelFormats[CurrentLevelData.BrickData.SkyBouncedSHCoefficients[i].Format].BlockBytes);
}
CurrentLevelData.BrickData.LQLightColor.Resize(TotalBrickDataSize * GPixelFormats[CurrentLevelData.BrickData.LQLightColor.Format].BlockBytes);
CurrentLevelData.BrickData.LQLightDirection.Resize(TotalBrickDataSize * GPixelFormats[CurrentLevelData.BrickData.LQLightDirection.Format].BlockBytes);
VoxelImportProcessingData.Empty(TotalBrickDataSize);
VoxelImportProcessingData.AddZeroed(TotalBrickDataSize);
}
int32 BrickStartAllocation = 0;
for (int32 CurrentDepth = 0; CurrentDepth < VolumetricLightmapSettings.MaxRefinementLevels; CurrentDepth++)
{
const TArray<const FImportedVolumetricLightmapBrick*>& BricksAtCurrentDepth = BricksByDepth[CurrentDepth];
for (int32 BrickIndex = 0; BrickIndex < BricksAtCurrentDepth.Num(); BrickIndex++)
{
const FImportedVolumetricLightmapBrick& Brick = *BricksAtCurrentDepth[BrickIndex];
const FIntVector BrickLayoutPosition = ComputeBrickLayoutPosition(BrickStartAllocation + BrickIndex, BrickLayoutDimensions) * PaddedBrickSize;
CopyBrickToAtlasVolumeTexture(
GPixelFormats[CurrentLevelData.BrickData.AmbientVector.Format].BlockBytes,
CurrentLevelData.BrickDataDimensions,
BrickLayoutPosition,
FIntVector(BrickSize),
(const uint8*)Brick.AmbientVector.GetData(),
CurrentLevelData.BrickData.AmbientVector.Data.GetData());
CopyBrickToAtlasVolumeTexture(
GPixelFormats[CurrentLevelData.BrickData.SkyBouncedAmbientVector.Format].BlockBytes,
CurrentLevelData.BrickDataDimensions,
BrickLayoutPosition,
FIntVector(BrickSize),
(const uint8*)Brick.SkyBouncedAmbientVector.GetData(),
CurrentLevelData.BrickData.SkyBouncedAmbientVector.Data.GetData());
CopyBrickToAtlasVolumeTexture(
GPixelFormats[CurrentLevelData.BrickData.AmbientVectorMobile.Format].BlockBytes,
CurrentLevelData.BrickDataDimensions,
BrickLayoutPosition,
FIntVector(BrickSize),
(const uint8*)Brick.AmbientVectorMobile.GetData(),
CurrentLevelData.BrickData.AmbientVectorMobile.Data.GetData());
for (int32 i = 0; i < UE_ARRAY_COUNT(Brick.SHCoefficients); i++)
{
CopyBrickToAtlasVolumeTexture(
GPixelFormats[CurrentLevelData.BrickData.SHCoefficients[i].Format].BlockBytes,
CurrentLevelData.BrickDataDimensions,
BrickLayoutPosition,
FIntVector(BrickSize),
(const uint8*)Brick.SHCoefficients[i].GetData(),
CurrentLevelData.BrickData.SHCoefficients[i].Data.GetData());
}
for (int32 i = 0; i < UE_ARRAY_COUNT(Brick.SHCoefficientsMobile); i++)
{
CopyBrickToAtlasVolumeTexture(
GPixelFormats[CurrentLevelData.BrickData.SHCoefficientsMobile[i].Format].BlockBytes,
CurrentLevelData.BrickDataDimensions,
BrickLayoutPosition,
FIntVector(BrickSize),
(const uint8*)Brick.SHCoefficientsMobile[i].GetData(),
CurrentLevelData.BrickData.SHCoefficientsMobile[i].Data.GetData());
}
for (int32 i = 0; i < UE_ARRAY_COUNT(Brick.SkyVisibilitySHCoefficients); i++)
{
CopyBrickToAtlasVolumeTexture(
GPixelFormats[CurrentLevelData.BrickData.SkyVisibilitySHCoefficients[i].Format].BlockBytes,
CurrentLevelData.BrickDataDimensions,
BrickLayoutPosition,
FIntVector(BrickSize),
(const uint8*)Brick.SkyVisibilitySHCoefficients[i].GetData(),
CurrentLevelData.BrickData.SkyVisibilitySHCoefficients[i].Data.GetData());
}
// @Todo Deal with SkyBouncedSHCoefficients
for (int32 i = 0; i < UE_ARRAY_COUNT(Brick.SkyBouncedSHCoefficients); i++)
{
CopyBrickToAtlasVolumeTexture(
GPixelFormats[CurrentLevelData.BrickData.SkyBouncedSHCoefficients[i].Format].BlockBytes,
CurrentLevelData.BrickDataDimensions,
BrickLayoutPosition,
FIntVector(BrickSize),
(const uint8*)Brick.SkyBouncedSHCoefficients[i].GetData(),
CurrentLevelData.BrickData.SkyBouncedSHCoefficients[i].Data.GetData());
}
CopyBrickToAtlasVolumeTexture(
GPixelFormats[CurrentLevelData.BrickData.LQLightColor.Format].BlockBytes,
CurrentLevelData.BrickDataDimensions,
BrickLayoutPosition,
FIntVector(BrickSize),
(const uint8*)Brick.LQLightColor.GetData(),
CurrentLevelData.BrickData.LQLightColor.Data.GetData());
CopyBrickToAtlasVolumeTexture(
GPixelFormats[CurrentLevelData.BrickData.LQLightDirection.Format].BlockBytes,
CurrentLevelData.BrickDataDimensions,
BrickLayoutPosition,
FIntVector(BrickSize),
(const uint8*)Brick.LQLightDirection.GetData(),
CurrentLevelData.BrickData.LQLightDirection.Data.GetData());
if (bGenerateSkyShadowing)
{
CopyBrickToAtlasVolumeTexture(
GPixelFormats[CurrentLevelData.BrickData.SkyBentNormal.Format].BlockBytes,
CurrentLevelData.BrickDataDimensions,
BrickLayoutPosition,
FIntVector(BrickSize),
(const uint8*)Brick.SkyBentNormal.GetData(),
CurrentLevelData.BrickData.SkyBentNormal.Data.GetData());
}
CopyBrickToAtlasVolumeTexture(
GPixelFormats[CurrentLevelData.BrickData.DirectionalLightShadowing.Format].BlockBytes,
CurrentLevelData.BrickDataDimensions,
BrickLayoutPosition,
FIntVector(BrickSize),
(const uint8*)Brick.DirectionalLightShadowing.GetData(),
CurrentLevelData.BrickData.DirectionalLightShadowing.Data.GetData());
CopyBrickToAtlasVolumeTexture(
VoxelImportProcessingData.GetTypeSize(),
CurrentLevelData.BrickDataDimensions,
BrickLayoutPosition,
FIntVector(BrickSize),
(const uint8*)Brick.TaskVoxelImportProcessingData.GetData(),
(uint8*)VoxelImportProcessingData.GetData());
}
BrickStartAllocation += BricksAtCurrentDepth.Num();
}
const float InvBrickSize = 1.0f / BrickSize;
const FVector DetailCellSize = VolumetricLightmapSettings.VolumeSize / FVector(VolumetricLightmapSettings.TopLevelGridSize * DetailCellsPerTopLevelBrick);
for (int32 DilatePassIndex = 0; DilatePassIndex < NumDilateOverEmbeddedVoxelsPasses; DilatePassIndex++)
{
BrickStartAllocation = 0;
// Compute the allocation start for the highest density level bricks
for (int32 CurrentDepth = 0; CurrentDepth < VolumetricLightmapSettings.MaxRefinementLevels - 1; CurrentDepth++)
{
BrickStartAllocation += BricksByDepth[CurrentDepth].Num();
}
TArray<const FImportedVolumetricLightmapBrick*>& HighestDensityBricks = BricksByDepth[VolumetricLightmapSettings.MaxRefinementLevels - 1];
// Need to double buffer bInsideGeometry as it is both read and written
TArray<Lightmass::FIrradianceVoxelImportProcessingData> NewVoxelImportProcessingData = VoxelImportProcessingData;
// Reads from unique data of any density bricks, writes to unique data
// This is doing a filter in-place, which is only reliable because source and dest voxels are mutually exclusive
FilterWithNeighbors(
HighestDensityBricks,
BrickStartAllocation,
VolumetricLightmapSettings.MaxRefinementLevels - 1,
BrickLayoutDimensions,
VolumetricLightmapSettings,
CurrentLevelData,
VoxelImportProcessingData,
NewVoxelImportProcessingData);
VoxelImportProcessingData = MoveTemp(NewVoxelImportProcessingData);
}
BrickStartAllocation = 0;
for (int32 CurrentDepth = 0; CurrentDepth < VolumetricLightmapSettings.MaxRefinementLevels; CurrentDepth++)
{
const TArray<const FImportedVolumetricLightmapBrick*>& BricksAtCurrentDepth = BricksByDepth[CurrentDepth];
if (bStitchDetailBricksWithLowDensityNeighbors && CurrentDepth > 0)
{
// Reads from both unique and padding data of lower density bricks, writes to unique data
StitchDetailBricksWithLowDensityNeighbors(
BricksAtCurrentDepth,
BrickStartAllocation,
CurrentDepth,
BrickLayoutDimensions,
VolumetricLightmapSettings,
CurrentLevelData);
}
if (bCopyPaddingFromUniqueData)
{
// Compute padding for all the bricks
// Reads from unique data, writes to padding data of bricks
// Padding must be computed after all operations that might modify the unique data
CopyPaddingFromUniqueData(
BricksAtCurrentDepth,
BrickStartAllocation,
CurrentDepth,
BrickLayoutDimensions,
VolumetricLightmapSettings,
CurrentLevelData,
CurrentLevelData.BrickData);
}
if (bCopyVolumeBorderFromInterior)
{
// The volume border padding had no unique data to copy from, replicate the neighboring interior value
CopyVolumeBorderFromInterior(
BricksAtCurrentDepth,
BrickStartAllocation,
CurrentDepth,
BrickLayoutDimensions,
VolumetricLightmapSettings,
CurrentLevelData,
CurrentLevelData.BrickData);
}
BrickStartAllocation += BricksAtCurrentDepth.Num();
}
TMap<FGuid, int32> SubLevelTotalBricks;
TMap<FGuid, int32> SubLevelBrickAllocator;
TMap<FGuid, FIntVector> SubLevelBrickLayoutDimensions;
{
for (int32 CurrentDepth = 0; CurrentDepth < VolumetricLightmapSettings.MaxRefinementLevels; CurrentDepth++)
{
const TArray<const FImportedVolumetricLightmapBrick*>& BricksAtCurrentDepth = BricksByDepth[CurrentDepth];
for (int32 BrickIndex = 0; BrickIndex < BricksAtCurrentDepth.Num(); BrickIndex++)
{
const FImportedVolumetricLightmapBrick& Brick = *BricksAtCurrentDepth[BrickIndex];
FGuid LevelGuid;
if (CurrentDepth == VolumetricLightmapSettings.MaxRefinementLevels - 1)
{
LevelGuid = Brick.IntersectingLevelGuid;
}
else
{
// Top level bricks are put into persistent level
LevelGuid = PersistentLevelGuid;
}
if (!SubLevelTotalBricks.Contains(LevelGuid))
{
SubLevelTotalBricks.Add(LevelGuid, 0);
SubLevelBrickAllocator.Add(LevelGuid, 0);
SubLevelBrickLayoutDimensions.Add(LevelGuid, FIntVector(0));
}
SubLevelTotalBricks[LevelGuid]++;
}
}
for (auto Pair: SubLevelTotalBricks)
{
bool bPersistentLevel = Pair.Key == PersistentLevelGuid;
FIntVector& SubLevelBrickLayoutDimension = SubLevelBrickLayoutDimensions[Pair.Key];
int32 SubLevelBrickTextureLinearAllocator = SubLevelTotalBricks[Pair.Key];
SubLevelBrickLayoutDimension.X = FMath::Min(SubLevelBrickTextureLinearAllocator, MaxBricksInLayoutOneDim);
SubLevelBrickTextureLinearAllocator = FMath::DivideAndRoundUp(SubLevelBrickTextureLinearAllocator, SubLevelBrickLayoutDimension.X);
SubLevelBrickLayoutDimension.Y = FMath::Min(SubLevelBrickTextureLinearAllocator, MaxBricksInLayoutOneDim);
SubLevelBrickTextureLinearAllocator = FMath::DivideAndRoundUp(SubLevelBrickTextureLinearAllocator, SubLevelBrickLayoutDimension.Y);
SubLevelBrickLayoutDimension.Z = FMath::Min(SubLevelBrickTextureLinearAllocator, MaxBricksInLayoutOneDim);
{
ULevel* SubLevelStorageLevel;
UMapBuildDataRegistry* SubLevelRegistry;
if (System.LightingScenario)
{
if (GLightingScenraioSeperateData)
{
SubLevelStorageLevel = FindLevel(Pair.Key);
SubLevelRegistry = SubLevelStorageLevel->GetOrCreateMapBuildData(System.LightingScenario);
SubLevelStorageLevel = System.LightingScenario;
}
else
{
SubLevelStorageLevel = System.LightingScenario;
SubLevelRegistry = SubLevelStorageLevel->GetOrCreateMapBuildData();
}
}
else
{
SubLevelStorageLevel = FindLevel(Pair.Key);
SubLevelRegistry = SubLevelStorageLevel->GetOrCreateMapBuildData();
}
FPrecomputedVolumetricLightmapData& SubLevelData = SubLevelRegistry->AllocateLevelPrecomputedVolumetricLightmapBuildData(FindLevel(Pair.Key)->LevelBuildDataId);
if (SubLevelStorageLevel->GetWorld()->GetWorldSettings()->bRemovePcVolumetricLightmap)
SubLevelData.Platform &= ~TEXTURE_SET_PC;
if (SubLevelStorageLevel->GetWorld()->GetWorldSettings()->bRemoveMobileVolumetricLightmap)
SubLevelData.Platform &= ~TEXTURE_SET_MOBILE;
SubLevelData.InitializeOnImport(FBox(VolumetricLightmapSettings.VolumeMin, VolumetricLightmapSettings.VolumeMin + VolumetricLightmapSettings.VolumeSize), BrickSize);
{
// Temporarily assign format as PF_B8G8R8A8 since that matches FColor and makes our operations easier
// Will be converted to PF_R8G8B8A8 by ConvertBGRA8ToRGBA8ForLayer() later
SubLevelData.BrickData.AmbientVector.Format = PF_FloatR11G11B10;
SubLevelData.BrickData.SkyBouncedAmbientVector.Format = PF_FloatR11G11B10;
SubLevelData.BrickData.AmbientVectorMobile.Format = PF_B8G8R8A8;
SubLevelData.BrickData.SkyBentNormal.Format = PF_B8G8R8A8;
SubLevelData.BrickData.DirectionalLightShadowing.Format = PF_G8;
for (int32 i = 0; i < UE_ARRAY_COUNT(SubLevelData.BrickData.SHCoefficients); i++)
{
SubLevelData.BrickData.SHCoefficients[i].Format = PF_B8G8R8A8;
}
for (int32 i = 0; i < UE_ARRAY_COUNT(SubLevelData.BrickData.SHCoefficientsMobile); i++)
{
SubLevelData.BrickData.SHCoefficientsMobile[i].Format = PF_B8G8R8A8;
}
for (int32 i = 0; i < UE_ARRAY_COUNT(SubLevelData.BrickData.SkyVisibilitySHCoefficients); i++)
{
SubLevelData.BrickData.SkyVisibilitySHCoefficients[i].Format = PF_B8G8R8A8;
}
for (int32 i = 0; i < UE_ARRAY_COUNT(SubLevelData.BrickData.SkyBouncedSHCoefficients); i++)
{
SubLevelData.BrickData.SkyBouncedSHCoefficients[i].Format = PF_B8G8R8A8;
}
SubLevelData.BrickData.LQLightColor.Format = PF_FloatR11G11B10;
SubLevelData.BrickData.LQLightDirection.Format = PF_B8G8R8A8;
}
SubLevelData.BrickDataDimensions = SubLevelBrickLayoutDimension * PaddedBrickSize;
const int32 TotalBrickDataSize = SubLevelData.BrickDataDimensions.X * SubLevelData.BrickDataDimensions.Y * SubLevelData.BrickDataDimensions.Z;
SubLevelData.BrickData.AmbientVector.Resize(TotalBrickDataSize * GPixelFormats[SubLevelData.BrickData.AmbientVector.Format].BlockBytes);
SubLevelData.BrickData.SkyBouncedAmbientVector.Resize(TotalBrickDataSize * GPixelFormats[SubLevelData.BrickData.SkyBouncedAmbientVector.Format].BlockBytes);
SubLevelData.BrickData.AmbientVectorMobile.Resize(TotalBrickDataSize * GPixelFormats[SubLevelData.BrickData.AmbientVectorMobile.Format].BlockBytes);
if (bGenerateSkyShadowing)
{
SubLevelData.BrickData.SkyBentNormal.Resize(TotalBrickDataSize * GPixelFormats[SubLevelData.BrickData.SkyBentNormal.Format].BlockBytes);
}
SubLevelData.BrickData.DirectionalLightShadowing.Resize(TotalBrickDataSize * GPixelFormats[SubLevelData.BrickData.DirectionalLightShadowing.Format].BlockBytes);
for (int32 i = 0; i < UE_ARRAY_COUNT(SubLevelData.BrickData.SHCoefficients); i++)
{
const int32 Stride = GPixelFormats[SubLevelData.BrickData.SHCoefficients[i].Format].BlockBytes;
SubLevelData.BrickData.SHCoefficients[i].Resize(TotalBrickDataSize * Stride);
}
for (int32 i = 0; i < UE_ARRAY_COUNT(SubLevelData.BrickData.SHCoefficientsMobile); i++)
{
const int32 Stride = GPixelFormats[SubLevelData.BrickData.SHCoefficientsMobile[i].Format].BlockBytes;
SubLevelData.BrickData.SHCoefficientsMobile[i].Resize(TotalBrickDataSize * Stride);
}
for (int32 i = 0; i < UE_ARRAY_COUNT(SubLevelData.BrickData.SkyVisibilitySHCoefficients); i++)
{
const int32 Stride = GPixelFormats[SubLevelData.BrickData.SkyVisibilitySHCoefficients[i].Format].BlockBytes;
SubLevelData.BrickData.SkyVisibilitySHCoefficients[i].Resize(TotalBrickDataSize * Stride);
}
for (int32 i = 0; i < UE_ARRAY_COUNT(SubLevelData.BrickData.SkyBouncedSHCoefficients); i++)
{
const int32 Stride = GPixelFormats[SubLevelData.BrickData.SkyBouncedSHCoefficients[i].Format].BlockBytes;
SubLevelData.BrickData.SkyBouncedSHCoefficients[i].Resize(TotalBrickDataSize * Stride);
}
SubLevelData.BrickData.LQLightColor.Resize(TotalBrickDataSize * GPixelFormats[SubLevelData.BrickData.LQLightColor.Format].BlockBytes);
SubLevelData.BrickData.LQLightDirection.Resize(TotalBrickDataSize * GPixelFormats[SubLevelData.BrickData.LQLightDirection.Format].BlockBytes);
}
}
BrickStartAllocation = 0;
for (int32 CurrentDepth = 0; CurrentDepth < VolumetricLightmapSettings.MaxRefinementLevels; CurrentDepth++)
{
const TArray<const FImportedVolumetricLightmapBrick*>& BricksAtCurrentDepth = BricksByDepth[CurrentDepth];
for (int32 BrickIndex = 0; BrickIndex < BricksAtCurrentDepth.Num(); BrickIndex++)
{
const FImportedVolumetricLightmapBrick& Brick = *BricksAtCurrentDepth[BrickIndex];
FGuid LevelGuid;
if (CurrentDepth == VolumetricLightmapSettings.MaxRefinementLevels - 1)
{
LevelGuid = Brick.IntersectingLevelGuid;
}
else
{
// Top level bricks are put into persistent level
LevelGuid = PersistentLevelGuid;
}
ULevel* SubLevelStorageLevel;
UMapBuildDataRegistry* SubLevelRegistry;
if (System.LightingScenario)
{
if (GLightingScenraioSeperateData)
{
SubLevelStorageLevel = FindLevel(LevelGuid);
SubLevelRegistry = SubLevelStorageLevel->GetOrCreateMapBuildData(System.LightingScenario);
SubLevelStorageLevel = System.LightingScenario;
}
else
{
SubLevelStorageLevel = System.LightingScenario;
SubLevelRegistry = SubLevelStorageLevel->GetOrCreateMapBuildData();
}
}
else
{
SubLevelStorageLevel = FindLevel(LevelGuid);
SubLevelRegistry = SubLevelStorageLevel->GetOrCreateMapBuildData();
}
FPrecomputedVolumetricLightmapData& SubLevelData = *SubLevelRegistry->GetLevelPrecomputedVolumetricLightmapBuildData(FindLevel(LevelGuid)->LevelBuildDataId);
const FIntVector BrickLayoutPosition = ComputeBrickLayoutPosition(BrickStartAllocation + BrickIndex, BrickLayoutDimensions) * PaddedBrickSize;
const FIntVector SubLevelBrickLayoutPosition = ComputeBrickLayoutPosition(SubLevelBrickAllocator[LevelGuid], SubLevelBrickLayoutDimensions[LevelGuid]) * PaddedBrickSize;
CopyBetweenAtlasVolumeTextures(
GPixelFormats[CurrentLevelData.BrickData.AmbientVector.Format].BlockBytes,
FIntVector(PaddedBrickSize),
CurrentLevelData.BrickDataDimensions,
BrickLayoutPosition,
CurrentLevelData.BrickData.AmbientVector.Data,
SubLevelBrickLayoutDimensions[LevelGuid] * PaddedBrickSize,
SubLevelBrickLayoutPosition,
SubLevelData.BrickData.AmbientVector.Data);
CopyBetweenAtlasVolumeTextures(
GPixelFormats[CurrentLevelData.BrickData.SkyBouncedAmbientVector.Format].BlockBytes,
FIntVector(PaddedBrickSize),
CurrentLevelData.BrickDataDimensions,
BrickLayoutPosition,
CurrentLevelData.BrickData.SkyBouncedAmbientVector.Data,
SubLevelBrickLayoutDimensions[LevelGuid] * PaddedBrickSize,
SubLevelBrickLayoutPosition,
SubLevelData.BrickData.SkyBouncedAmbientVector.Data);
CopyBetweenAtlasVolumeTextures(
GPixelFormats[CurrentLevelData.BrickData.AmbientVectorMobile.Format].BlockBytes,
FIntVector(PaddedBrickSize),
CurrentLevelData.BrickDataDimensions,
BrickLayoutPosition,
CurrentLevelData.BrickData.AmbientVectorMobile.Data,
SubLevelBrickLayoutDimensions[LevelGuid] * PaddedBrickSize,
SubLevelBrickLayoutPosition,
SubLevelData.BrickData.AmbientVectorMobile.Data);
for (int32 i = 0; i < UE_ARRAY_COUNT(Brick.SHCoefficients); i++)
{
CopyBetweenAtlasVolumeTextures(
GPixelFormats[CurrentLevelData.BrickData.SHCoefficients[i].Format].BlockBytes,
FIntVector(PaddedBrickSize),
CurrentLevelData.BrickDataDimensions,
BrickLayoutPosition,
CurrentLevelData.BrickData.SHCoefficients[i].Data,
SubLevelBrickLayoutDimensions[LevelGuid] * PaddedBrickSize,
SubLevelBrickLayoutPosition,
SubLevelData.BrickData.SHCoefficients[i].Data);
}
for (int32 i = 0; i < UE_ARRAY_COUNT(Brick.SHCoefficientsMobile); i++)
{
CopyBetweenAtlasVolumeTextures(
GPixelFormats[CurrentLevelData.BrickData.SHCoefficientsMobile[i].Format].BlockBytes,
FIntVector(PaddedBrickSize),
CurrentLevelData.BrickDataDimensions,
BrickLayoutPosition,
CurrentLevelData.BrickData.SHCoefficientsMobile[i].Data,
SubLevelBrickLayoutDimensions[LevelGuid] * PaddedBrickSize,
SubLevelBrickLayoutPosition,
SubLevelData.BrickData.SHCoefficientsMobile[i].Data);
}
for (int32 i = 0; i < UE_ARRAY_COUNT(Brick.SkyVisibilitySHCoefficients); i++)
{
CopyBetweenAtlasVolumeTextures(
GPixelFormats[CurrentLevelData.BrickData.SkyVisibilitySHCoefficients[0].Format].BlockBytes,
FIntVector(PaddedBrickSize),
CurrentLevelData.BrickDataDimensions,
BrickLayoutPosition,
CurrentLevelData.BrickData.SkyVisibilitySHCoefficients[i].Data,
SubLevelBrickLayoutDimensions[LevelGuid] * PaddedBrickSize,
SubLevelBrickLayoutPosition,
SubLevelData.BrickData.SkyVisibilitySHCoefficients[i].Data);
}
// @Todo Deal with SkyBouncedSHCoefficients
for (int32 i = 0; i < UE_ARRAY_COUNT(Brick.SkyBouncedSHCoefficients); i++)
{
CopyBetweenAtlasVolumeTextures(
GPixelFormats[CurrentLevelData.BrickData.SkyBouncedSHCoefficients[i].Format].BlockBytes,
FIntVector(PaddedBrickSize),
CurrentLevelData.BrickDataDimensions,
BrickLayoutPosition,
CurrentLevelData.BrickData.SkyBouncedSHCoefficients[i].Data,
SubLevelBrickLayoutDimensions[LevelGuid] * PaddedBrickSize,
SubLevelBrickLayoutPosition,
SubLevelData.BrickData.SkyBouncedSHCoefficients[i].Data);
}
CopyBetweenAtlasVolumeTextures(
GPixelFormats[CurrentLevelData.BrickData.LQLightColor.Format].BlockBytes,
FIntVector(PaddedBrickSize),
CurrentLevelData.BrickDataDimensions,
BrickLayoutPosition,
CurrentLevelData.BrickData.LQLightColor.Data,
SubLevelBrickLayoutDimensions[LevelGuid] * PaddedBrickSize,
SubLevelBrickLayoutPosition,
SubLevelData.BrickData.LQLightColor.Data);
CopyBetweenAtlasVolumeTextures(
GPixelFormats[CurrentLevelData.BrickData.LQLightDirection.Format].BlockBytes,
FIntVector(PaddedBrickSize),
CurrentLevelData.BrickDataDimensions,
BrickLayoutPosition,
CurrentLevelData.BrickData.LQLightDirection.Data,
SubLevelBrickLayoutDimensions[LevelGuid] * PaddedBrickSize,
SubLevelBrickLayoutPosition,
SubLevelData.BrickData.LQLightDirection.Data);
if (bGenerateSkyShadowing)
{
CopyBetweenAtlasVolumeTextures(
GPixelFormats[CurrentLevelData.BrickData.SkyBentNormal.Format].BlockBytes,
FIntVector(PaddedBrickSize),
CurrentLevelData.BrickDataDimensions,
BrickLayoutPosition,
CurrentLevelData.BrickData.SkyBentNormal.Data,
SubLevelBrickLayoutDimensions[LevelGuid] * PaddedBrickSize,
SubLevelBrickLayoutPosition,
SubLevelData.BrickData.SkyBentNormal.Data);
}
CopyBetweenAtlasVolumeTextures(
GPixelFormats[CurrentLevelData.BrickData.DirectionalLightShadowing.Format].BlockBytes,
FIntVector(PaddedBrickSize),
CurrentLevelData.BrickDataDimensions,
BrickLayoutPosition,
CurrentLevelData.BrickData.DirectionalLightShadowing.Data,
SubLevelBrickLayoutDimensions[LevelGuid] * PaddedBrickSize,
SubLevelBrickLayoutPosition,
SubLevelData.BrickData.DirectionalLightShadowing.Data);
SubLevelBrickAllocator[LevelGuid]++;
if (LevelGuid != PersistentLevelGuid)
{
SubLevelData.SubLevelBrickPositions.Add(Brick.IndirectionTexturePosition);
check(SubLevelData.SubLevelBrickPositions.Num() == SubLevelBrickAllocator[LevelGuid]);
}
}
BrickStartAllocation += BricksAtCurrentDepth.Num();
}
{
const int32 TotalIndirectionTextureSize = CurrentLevelData.IndirectionTextureDimensions.X * CurrentLevelData.IndirectionTextureDimensions.Y * CurrentLevelData.IndirectionTextureDimensions.Z;
CurrentLevelData.IndirectionTexture.Resize(TotalIndirectionTextureSize * IndirectionTextureDataStride);
BuildIndirectionTexture(BricksByDepth, VolumetricLightmapSettings, MaxBricksInLayoutOneDim, SubLevelBrickLayoutDimensions[PersistentLevelGuid], IndirectionTextureDataStride, CurrentLevelData, true);
ULevel* SubLevelStorageLevel;
UMapBuildDataRegistry* SubLevelRegistry;
if (System.LightingScenario)
{
if (GLightingScenraioSeperateData)
{
SubLevelStorageLevel = FindLevel(PersistentLevelGuid);
SubLevelRegistry = SubLevelStorageLevel->GetOrCreateMapBuildData(System.LightingScenario);
SubLevelStorageLevel = System.LightingScenario;
}
else
{
SubLevelStorageLevel = System.LightingScenario;
SubLevelRegistry = SubLevelStorageLevel->GetOrCreateMapBuildData();
}
}
else
{
SubLevelStorageLevel = FindLevel(PersistentLevelGuid);
SubLevelRegistry = SubLevelStorageLevel->GetOrCreateMapBuildData();
}
FPrecomputedVolumetricLightmapData& PersistentLevelData = *SubLevelRegistry->GetLevelPrecomputedVolumetricLightmapBuildData(FindLevel(PersistentLevelGuid)->LevelBuildDataId);
PersistentLevelData.InitializeOnImport(FBox(VolumetricLightmapSettings.VolumeMin, VolumetricLightmapSettings.VolumeMin + VolumetricLightmapSettings.VolumeSize), BrickSize);
PersistentLevelData.IndirectionTexture = CurrentLevelData.IndirectionTexture;
PersistentLevelData.IndirectionTextureDimensions = CurrentLevelData.IndirectionTextureDimensions;
}
{
for (int32 CurrentDepth = 0; CurrentDepth < VolumetricLightmapSettings.MaxRefinementLevels; CurrentDepth++)
{
const TArray<const FImportedVolumetricLightmapBrick*>& BricksAtCurrentDepth = BricksByDepth[CurrentDepth];
for (int32 BrickIndex = 0; BrickIndex < BricksAtCurrentDepth.Num(); BrickIndex++)
{
const FImportedVolumetricLightmapBrick& Brick = *BricksAtCurrentDepth[BrickIndex];
FGuid LevelGuid;
if (CurrentDepth == VolumetricLightmapSettings.MaxRefinementLevels - 1)
{
LevelGuid = Brick.IntersectingLevelGuid;
}
else
{
// Top level bricks are put into persistent level
LevelGuid = PersistentLevelGuid;
}
if (LevelGuid != PersistentLevelGuid)
{
ULevel* SubLevelStorageLevel;
UMapBuildDataRegistry* SubLevelRegistry;
if (System.LightingScenario)
{
if (GLightingScenraioSeperateData)
{
SubLevelStorageLevel = FindLevel(LevelGuid);
SubLevelRegistry = SubLevelStorageLevel->GetOrCreateMapBuildData(System.LightingScenario);
SubLevelStorageLevel = System.LightingScenario;
}
else
{
SubLevelStorageLevel = System.LightingScenario;
SubLevelRegistry = SubLevelStorageLevel->GetOrCreateMapBuildData();
}
}
else
{
SubLevelStorageLevel = FindLevel(LevelGuid);
SubLevelRegistry = SubLevelStorageLevel->GetOrCreateMapBuildData();
}
FPrecomputedVolumetricLightmapData& SubLevelData = *SubLevelRegistry->GetLevelPrecomputedVolumetricLightmapBuildData(FindLevel(LevelGuid)->LevelBuildDataId);
FIntVector IndirectionBrickOffset;
int32 IndirectionBrickSize;
const FVector IndirectionDataSourceCoordinate = FVector(Brick.IndirectionTexturePosition) + FVector(0.5f, 0.5f, 0.5f);
SampleIndirectionTexture(IndirectionDataSourceCoordinate, CurrentLevelData.IndirectionTextureDimensions, CurrentLevelData.IndirectionTexture.Data.GetData(), IndirectionBrickOffset, IndirectionBrickSize);
SubLevelData.IndirectionTextureOriginalValues.Add(FColor((uint8)IndirectionBrickOffset.X, (uint8)IndirectionBrickOffset.Y, (uint8)IndirectionBrickOffset.Z, (uint8)IndirectionBrickSize));
}
}
}
}
{
for (auto Pair: SubLevelTotalBricks)
{
FGuid LevelGuid = Pair.Key;
ULevel* SubLevelStorageLevel;
UMapBuildDataRegistry* SubLevelRegistry;
if (System.LightingScenario)
{
if (GLightingScenraioSeperateData)
{
SubLevelStorageLevel = FindLevel(LevelGuid);
SubLevelRegistry = SubLevelStorageLevel->GetOrCreateMapBuildData(System.LightingScenario);
SubLevelStorageLevel = System.LightingScenario;
}
else
{
SubLevelStorageLevel = System.LightingScenario;
SubLevelRegistry = SubLevelStorageLevel->GetOrCreateMapBuildData();
}
}
else
{
SubLevelStorageLevel = FindLevel(LevelGuid);
SubLevelRegistry = SubLevelStorageLevel->GetOrCreateMapBuildData();
}
FPrecomputedVolumetricLightmapData& SubLevelData = *SubLevelRegistry->GetLevelPrecomputedVolumetricLightmapBuildData(FindLevel(LevelGuid)->LevelBuildDataId);
ConvertBGRA8ToRGBA8ForLayer(SubLevelData.BrickData.AmbientVectorMobile);
ConvertBGRA8ToRGBA8ForLayer(SubLevelData.BrickData.SkyBentNormal);
for (int32 i = 0; i < UE_ARRAY_COUNT(SubLevelData.BrickData.SHCoefficients); i++)
{
ConvertBGRA8ToRGBA8ForLayer(SubLevelData.BrickData.SHCoefficients[i]);
}
for (int32 i = 0; i < UE_ARRAY_COUNT(SubLevelData.BrickData.SHCoefficientsMobile); i++)
{
ConvertBGRA8ToRGBA8ForLayer(SubLevelData.BrickData.SHCoefficientsMobile[i]);
}
for (int32 i = 0; i < UE_ARRAY_COUNT(SubLevelData.BrickData.SkyVisibilitySHCoefficients); i++)
{
ConvertBGRA8ToRGBA8ForLayer(SubLevelData.BrickData.SkyVisibilitySHCoefficients[i]);
}
for (int32 i = 0; i < UE_ARRAY_COUNT(SubLevelData.BrickData.SkyBouncedSHCoefficients); i++)
{
ConvertBGRA8ToRGBA8ForLayer(SubLevelData.BrickData.SkyBouncedSHCoefficients[i]);
}
ConvertBGRA8ToRGBA8ForLayer(SubLevelData.BrickData.LQLightDirection);
}
}
/**
* Discard VolumetricLightmap
*/
{
for (auto Pair: SubLevelTotalBricks)
{
FGuid LevelGuid = Pair.Key;
ULevel* SubLevelStorageLevel;
UMapBuildDataRegistry* SubLevelRegistry;
if (System.LightingScenario)
{
if (GLightingScenraioSeperateData)
{
SubLevelStorageLevel = FindLevel(LevelGuid);
SubLevelRegistry = SubLevelStorageLevel->GetOrCreateMapBuildData(System.LightingScenario);
SubLevelStorageLevel = System.LightingScenario;
}
else
{
SubLevelStorageLevel = System.LightingScenario;
SubLevelRegistry = SubLevelStorageLevel->GetOrCreateMapBuildData();
}
}
else
{
SubLevelStorageLevel = FindLevel(LevelGuid);
SubLevelRegistry = SubLevelStorageLevel->GetOrCreateMapBuildData();
}
FPrecomputedVolumetricLightmapData& SubLevelData = *SubLevelRegistry->GetLevelPrecomputedVolumetricLightmapBuildData(FindLevel(LevelGuid)->LevelBuildDataId);
if (!(SubLevelData.Platform & TEXTURE_SET_PC))
{
SubLevelData.BrickData.AmbientVector.Discard();
SubLevelData.BrickData.SkyBouncedAmbientVector.Discard();
for (int32 i = 0; i < UE_ARRAY_COUNT(SubLevelData.BrickData.SHCoefficients); i++)
{
SubLevelData.BrickData.SHCoefficients[i].Discard();
}
for (int32 i = 0; i < UE_ARRAY_COUNT(SubLevelData.BrickData.SkyVisibilitySHCoefficients); i++)
{
SubLevelData.BrickData.SkyVisibilitySHCoefficients[i].Discard();
}
for (int32 i = 0; i < UE_ARRAY_COUNT(SubLevelData.BrickData.SkyBouncedSHCoefficients); i++)
{
SubLevelData.BrickData.SkyBouncedSHCoefficients[i].Discard();
}
SubLevelData.BrickData.SkyBentNormal.Discard();
SubLevelData.BrickData.DirectionalLightShadowing.Discard();
}
if (!(SubLevelData.Platform & TEXTURE_SET_MOBILE))
{
SubLevelData.BrickData.AmbientVectorMobile.Discard();
for (int32 i = 0; i < UE_ARRAY_COUNT(SubLevelData.BrickData.SHCoefficientsMobile); i++)
{
SubLevelData.BrickData.SHCoefficientsMobile[i].Discard();
}
}
}
}
}
{
/**
* Statistics
*/
float ImportTime = FPlatformTime::Seconds() - StartTime;
UE_LOG(LogVolumetricLightmapImport, Log, TEXT("Imported Volumetric Lightmap in %.3fs"), ImportTime);
UE_LOG(LogVolumetricLightmapImport, Log, TEXT(" Indirection Texture %ux%ux%u = %.1fMb"),
CurrentLevelData.IndirectionTextureDimensions.X,
CurrentLevelData.IndirectionTextureDimensions.Y,
CurrentLevelData.IndirectionTextureDimensions.Z,
CurrentLevelData.IndirectionTexture.Data.Num() / 1024.0f / 1024.0f);
uint64 BrickDataSize = 0;
for (auto Pair: SubLevelTotalBricks)
{
ULevel* SubLevelStorageLevel;
UMapBuildDataRegistry* SubLevelRegistry;
if (System.LightingScenario)
{
if (GLightingScenraioSeperateData)
{
SubLevelStorageLevel = FindLevel(Pair.Key);
SubLevelRegistry = SubLevelStorageLevel->GetOrCreateMapBuildData(System.LightingScenario);
SubLevelStorageLevel = System.LightingScenario;
}
else
{
SubLevelStorageLevel = System.LightingScenario;
SubLevelRegistry = SubLevelStorageLevel->GetOrCreateMapBuildData();
}
}
else
{
SubLevelStorageLevel = FindLevel(Pair.Key);
SubLevelRegistry = SubLevelStorageLevel->GetOrCreateMapBuildData();
}
FPrecomputedVolumetricLightmapData& SubLevelData = *SubLevelRegistry->GetLevelPrecomputedVolumetricLightmapBuildData(FindLevel(Pair.Key)->LevelBuildDataId);
SubLevelData.FinalizeImport();
check(SubLevelData.IndirectionTextureOriginalValues.Num() == SubLevelData.SubLevelBrickPositions.Num());
BrickDataSize +=
SubLevelData.BrickData.AmbientVector.Data.Num() +
SubLevelData.BrickData.SkyBouncedAmbientVector.Data.Num() +
SubLevelData.BrickData.AmbientVectorMobile.Data.Num() +
SubLevelData.BrickData.SHCoefficients[0].Data.Num() +
SubLevelData.BrickData.SHCoefficients[1].Data.Num() +
SubLevelData.BrickData.SHCoefficients[2].Data.Num() +
SubLevelData.BrickData.SHCoefficients[3].Data.Num() +
SubLevelData.BrickData.SHCoefficients[4].Data.Num() +
SubLevelData.BrickData.SHCoefficients[5].Data.Num() +
SubLevelData.BrickData.SkyVisibilitySHCoefficients[0].Data.Num() +
SubLevelData.BrickData.SkyVisibilitySHCoefficients[1].Data.Num() +
SubLevelData.BrickData.SkyVisibilitySHCoefficients[2].Data.Num() +
SubLevelData.BrickData.SkyVisibilitySHCoefficients[3].Data.Num() +
SubLevelData.BrickData.SkyVisibilitySHCoefficients[4].Data.Num() +
SubLevelData.BrickData.SkyVisibilitySHCoefficients[5].Data.Num() +
SubLevelData.BrickData.SkyBouncedSHCoefficients[0].Data.Num() +
SubLevelData.BrickData.SkyBouncedSHCoefficients[1].Data.Num() +
SubLevelData.BrickData.SkyBouncedSHCoefficients[2].Data.Num() +
SubLevelData.BrickData.SkyBouncedSHCoefficients[3].Data.Num() +
SubLevelData.BrickData.SkyBouncedSHCoefficients[4].Data.Num() +
SubLevelData.BrickData.SkyBouncedSHCoefficients[5].Data.Num() +
SubLevelData.BrickData.SkyBentNormal.Data.Num() +
SubLevelData.BrickData.DirectionalLightShadowing.Data.Num() +
SubLevelData.BrickData.LQLightColor.Data.Num() +
SubLevelData.BrickData.LQLightDirection.Data.Num() +
SubLevelData.BrickData.SHCoefficientsMobile[0].Data.Num() +
SubLevelData.BrickData.SHCoefficientsMobile[1].Data.Num() +
SubLevelData.SubLevelBrickPositions.Num() * SubLevelData.SubLevelBrickPositions.GetTypeSize() +
SubLevelData.IndirectionTextureOriginalValues.Num() * SubLevelData.IndirectionTextureOriginalValues.GetTypeSize();
}
int32 TotalNumBricks = 0;
for (int32 CurrentDepth = 0; CurrentDepth < VolumetricLightmapSettings.MaxRefinementLevels; CurrentDepth++)
{
const TArray<const FImportedVolumetricLightmapBrick*>& BricksAtCurrentDepth = BricksByDepth[CurrentDepth];
TotalNumBricks += BricksAtCurrentDepth.Num();
}
const int32 ActualBrickSizeBytes = BrickDataSize / TotalNumBricks;
FString TrimmedString;
if (NumBottomLevelBricksTrimmedByInterpolationError > 0)
{
TrimmedString += FString::Printf(TEXT(" (trimmed %.1fMb due to %f MinBrickError)"),
NumBottomLevelBricksTrimmedByInterpolationError * ActualBrickSizeBytes / 1024.0f / 1024.0f,
VolumetricLightmapSettings.MinBrickError);
}
if (NumBottomLevelBricksTrimmedForMemoryLimit > 0)
{
TrimmedString += FString::Printf(TEXT(" (trimmed %.1fMb due to %.1fMb MaximumBrickMemoryMb)"),
NumBottomLevelBricksTrimmedForMemoryLimit * ActualBrickSizeBytes / 1024.0f / 1024.0f,
MaximumBrickMemoryMb);
}
UE_LOG(LogVolumetricLightmapImport, Log, TEXT(" BrickData (all levels) %.1fMb%s"),
BrickDataSize / 1024.0f / 1024.0f,
*TrimmedString);
UE_LOG(LogVolumetricLightmapImport, Log, TEXT(" Bricks at depth"));
const float TotalVolume = VolumetricLightmapSettings.VolumeSize.X * VolumetricLightmapSettings.VolumeSize.Y * VolumetricLightmapSettings.VolumeSize.Z;
for (int32 CurrentDepth = 0; CurrentDepth < VolumetricLightmapSettings.MaxRefinementLevels; CurrentDepth++)
{
const int32 DetailCellsPerCurrentLevelBrick = 1 << ((VolumetricLightmapSettings.MaxRefinementLevels - CurrentDepth) * BrickSizeLog2);
const FVector CurrentDepthBrickSize = DetailCellSize * DetailCellsPerCurrentLevelBrick;
const TArray<const FImportedVolumetricLightmapBrick*>& BricksAtCurrentDepth = BricksByDepth[CurrentDepth];
const float CurrentDepthBrickVolume = CurrentDepthBrickSize.X * CurrentDepthBrickSize.Y * CurrentDepthBrickSize.Z;
UE_LOG(LogVolumetricLightmapImport, Log, TEXT(" %u: %.1f%% covering %.1f%% of volume"),
CurrentDepth,
100.0f * BricksAtCurrentDepth.Num() / TotalNumBricks,
100.0f * BricksAtCurrentDepth.Num() * CurrentDepthBrickVolume / TotalVolume);
}
UE_LOG(LogVolumetricLightmapImport, Log, TEXT(" Bricks in each level"));
for (auto Pair: SubLevelTotalBricks)
{
FString LevelName = FindLevel(Pair.Key)->GetOuter()->GetName();
if (FindLevel(Pair.Key)->IsPersistentLevel())
{
UE_LOG(LogVolumetricLightmapImport, Log, TEXT(" %s \t\t\t %d bricks \t %.1f%% (Persistent Level)"), *LevelName, Pair.Value, 100.0f * Pair.Value / TotalNumBricks);
}
else
{
UE_LOG(LogVolumetricLightmapImport, Log, TEXT(" %s \t\t\t %d bricks \t %.1f%%"), *LevelName, Pair.Value, 100.0f * Pair.Value / TotalNumBricks);
}
}
}
}
#undef LOCTEXT_NAMESPACE