// 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& SourceData, FIntVector DestAtlasSize, FIntVector DestBrickMin, TArray& 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 AmbientVector; TArray SkyBouncedAmbientVector; TArray AmbientVectorMobile; TArray SHCoefficients[6]; TArray SkyVisibilitySHCoefficients[3]; TArray SkyBouncedSHCoefficients[6]; TArray LQLightColor; TArray LQLightDirection; TArray SHCoefficientsMobile[2]; TArray SkyBentNormal; TArray DirectionalLightShadowing; TArray TaskVoxelImportProcessingData; }; struct FImportedVolumetricLightmapTaskData { TArray 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(BrickTextureCoordinate, CurrentLevelData.BrickDataDimensions, (const FFloat3Packed*)BrickData.AmbientVector.Data.GetData()); *(FFloat3Packed*)&BrickData.SkyBouncedAmbientVector.Data[LinearDestCellIndex * sizeof(FFloat3Packed)] = FilteredVolumeLookupReconverted(BrickTextureCoordinate, CurrentLevelData.BrickDataDimensions, (const FFloat3Packed*)BrickData.SkyBouncedAmbientVector.Data.GetData()); *(FColor*)&BrickData.AmbientVectorMobile.Data[LinearDestCellIndex * sizeof(FColor)] = FilteredVolumeLookupReconverted(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(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(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(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(BrickTextureCoordinate, CurrentLevelData.BrickDataDimensions, (const FColor*)BrickData.SkyBouncedSHCoefficients[i].Data.GetData()); } *(FFloat3Packed*)&BrickData.LQLightColor.Data[LinearDestCellIndex * sizeof(FFloat3Packed)] = FilteredVolumeLookupReconverted(BrickTextureCoordinate, CurrentLevelData.BrickDataDimensions, (const FFloat3Packed*)BrickData.LQLightColor.Data.GetData()); *(FColor*)&BrickData.LQLightDirection.Data[LinearDestCellIndex * sizeof(FColor)] = FilteredVolumeLookupReconverted(BrickTextureCoordinate, CurrentLevelData.BrickDataDimensions, (const FColor*)BrickData.LQLightDirection.Data.GetData()); if (BrickData.SkyBentNormal.Data.Num() > 0) { *(FColor*)&BrickData.SkyBentNormal.Data[LinearDestCellIndex * sizeof(FColor)] = FilteredVolumeLookupReconverted(BrickTextureCoordinate, CurrentLevelData.BrickDataDimensions, (const FColor*)BrickData.SkyBentNormal.Data.GetData()); } *(uint8*)&BrickData.DirectionalLightShadowing.Data[LinearDestCellIndex * sizeof(uint8)] = FilteredVolumeLookupReconverted(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& TaskDataArray) { TList* Element = CompletedVolumetricLightmapTasks.ExtractAll(); while (Element) { // If this task has not already been imported, import it now TList* 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& BricksAtCurrentDepth, int32 BrickStartAllocation, int32 CurrentDepth, FIntVector BrickLayoutDimensions, const Lightmass::FVolumetricLightmapSettings& VolumetricLightmapSettings, FPrecomputedVolumetricLightmapData& CurrentLevelData, const TArray& VoxelImportProcessingData, TArray& 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 FilteredBrickData; TArray 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(BrickTextureCoordinate, CurrentLevelData.BrickDataDimensions, VoxelImportProcessingData.GetData()); if (!NeighborVoxelImportData.bInsideGeometry && !NeighborVoxelImportData.bBorderVoxel) { const float Weight = 1.0f / FMath::Max(FMath::Abs(NeighborX) + FMath::Abs(NeighborY) + FMath::Abs(NeighborZ), .5f); FLinearColor NeighborAmbientVector = FilteredVolumeLookup(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(BrickTextureCoordinate, CurrentLevelData.BrickDataDimensions, (const FColor*)CurrentLevelData.BrickData.SHCoefficients[i].Data.GetData()); } FLinearColor NeighborAmbient = FilteredVolumeLookup(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(BrickTextureCoordinate, CurrentLevelData.BrickDataDimensions, (const FColor*)CurrentLevelData.BrickData.SkyVisibilitySHCoefficients[i].Data.GetData()); } FLinearColor SkyBouncedNeighborAmbientVector = FilteredVolumeLookup(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(BrickTextureCoordinate, CurrentLevelData.BrickDataDimensions, (const FColor*)CurrentLevelData.BrickData.SkyBouncedSHCoefficients[i].Data.GetData()); } FLinearColor NeighborDirectionalLightShadowing = FilteredVolumeLookup(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(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(SHCoefficients[i] * InvTotalWeight / AmbientCoefficient); } const float FilteredSkyVisibilityAmbient = SkyVisibilityAmbientWeight * InvTotalWeight; FilteredBrickData[LinearVoxelIndex].SkyVisibilitySHCoefficients[0] = ConvertFromLinearColor(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(SkyVisibilitySHCoefficients[i] * InvTotalWeight / AmbientCoefficient); } const FLinearColor FilteredSkyBouncedAmbientColor = SkyBouncedAmbientVector * InvTotalWeight; FilteredBrickData[LinearVoxelIndex].SkyBouncedAmbientVector = ConvertFromLinearColor(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(SkyBouncedSHCoefficients[i] * InvTotalWeight / AmbientCoefficient); } FilteredBrickData[LinearVoxelIndex].DirectionalLightShadowing = ConvertFromLinearColor(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& 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& 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& 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>& BricksByDepth, const Lightmass::FVolumetricLightmapSettings& VolumetricLightmapSettings, bool onlyBakeSkyVisibility) { int32 NumBricksRemoved = 0; if (onlyBakeSkyVisibility) return NumBricksRemoved; if (VolumetricLightmapSettings.MaxRefinementLevels > 1) { TArray& HighestDensityBricks = BricksByDepth[VolumetricLightmapSettings.MaxRefinementLevels - 1]; const int32 ParentLevel = VolumetricLightmapSettings.MaxRefinementLevels - 2; TArray& 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>& BricksByDepth, const Lightmass::FVolumetricLightmapSettings& VolumetricLightmapSettings, int32 VoxelSizeBytes, const float MaximumBrickMemoryMb) { int32 NumBricksBeforeTrimming = 0; for (int32 CurrentDepth = 0; CurrentDepth < VolumetricLightmapSettings.MaxRefinementLevels; CurrentDepth++) { const TArray& BricksAtCurrentDepth = BricksByDepth[CurrentDepth]; NumBricksBeforeTrimming += BricksAtCurrentDepth.Num(); } const int32 PaddedBrickSize = VolumetricLightmapSettings.BrickSize + 1; TArray& 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(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>& 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& 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 It(System.GetWorld(), ALightmassImportanceVolume::StaticClass()); It; ++It) { AActor* Actor = *It; PersistentLevelGuid = *Exporter->LevelGuids.FindKey(Actor->GetLevel()); break; } } TArray 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> 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& 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 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& 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& HighestDensityBricks = BricksByDepth[VolumetricLightmapSettings.MaxRefinementLevels - 1]; // Need to double buffer bInsideGeometry as it is both read and written TArray 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& 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 SubLevelTotalBricks; TMap SubLevelBrickAllocator; TMap SubLevelBrickLayoutDimensions; { for (int32 CurrentDepth = 0; CurrentDepth < VolumetricLightmapSettings.MaxRefinementLevels; CurrentDepth++) { const TArray& 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& 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& 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& 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& 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