PlayAreaHeight

PlayAreaHeight

#Overview

name: PlayAreaHeight

The value of this variable can be defined or overridden in .ini config files. 1 .ini config file referencing this setting variable.

It is referenced in 14 C++ source files.

#Summary

#Usage in the C++ source code

The purpose of PlayAreaHeight is to define the height of visibility cells used in Unreal Engine’s precomputed visibility system. This setting is part of the rendering system, specifically the precomputed visibility optimization.

This setting variable is primarily used in the Unreal Engine’s lightmass and precomputed visibility subsystems. It is referenced in the UnrealEd module, particularly in the Lightmass exporter and processor.

The value of this variable is typically set in the engine’s configuration files, specifically in the “DevOptions.PrecomputedVisibility” section of the GLightmassIni file. It is read using the GConfig->GetFloat() function.

PlayAreaHeight interacts with other visibility-related variables such as CellSize, VisibilityCellSize, and various other precomputed visibility settings.

Developers must be aware that this variable directly affects the size and placement of visibility cells in the z-dimension. It influences how visibility is calculated and can impact performance and visual quality.

Best practices when using this variable include:

  1. Carefully tuning the value to balance between visibility accuracy and performance.
  2. Considering the typical vertical scale of your game environments when setting this value.
  3. Testing different values to find the optimal setting for your specific game.
  4. Coordinating this setting with other visibility-related variables for best results.
  5. Being mindful of how changes to this value might affect existing level designs and visibility calculations.

#Setting Variables

#References In INI files

Location: <Workspace>/Engine/Config/BaseLightmass.ini:111, section: [DevOptions.PrecomputedVisibility]

#References in C++ code

#Callsites

This variable is referenced in the following C++ source code:

#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Private/Lightmass/Lightmass.cpp:2282

Scope (from outer to inner):

file
function     void FLightmassExporter::WriteSceneSettings

Source code excerpt:

		Scene.PrecomputedVisibilitySettings.CellSize = World->GetWorldSettings()->VisibilityCellSize;
		VERIFYLIGHTMASSINI(GConfig->GetInt(TEXT("DevOptions.PrecomputedVisibility"), TEXT("NumCellDistributionBuckets"), Scene.PrecomputedVisibilitySettings.NumCellDistributionBuckets, GLightmassIni));
		VERIFYLIGHTMASSINI(GConfig->GetFloat(TEXT("DevOptions.PrecomputedVisibility"), TEXT("PlayAreaHeight"), Scene.PrecomputedVisibilitySettings.PlayAreaHeight, GLightmassIni));
		VERIFYLIGHTMASSINI(GConfig->GetFloat(TEXT("DevOptions.PrecomputedVisibility"), TEXT("MeshBoundsScale"), Scene.PrecomputedVisibilitySettings.MeshBoundsScale, GLightmassIni));
		VERIFYLIGHTMASSINI(GConfig->GetInt(TEXT("DevOptions.PrecomputedVisibility"), TEXT("MinMeshSamples"), Scene.PrecomputedVisibilitySettings.MinMeshSamples, GLightmassIni));
		VERIFYLIGHTMASSINI(GConfig->GetInt(TEXT("DevOptions.PrecomputedVisibility"), TEXT("MaxMeshSamples"), Scene.PrecomputedVisibilitySettings.MaxMeshSamples, GLightmassIni));
		VERIFYLIGHTMASSINI(GConfig->GetInt(TEXT("DevOptions.PrecomputedVisibility"), TEXT("NumCellSamples"), Scene.PrecomputedVisibilitySettings.NumCellSamples, GLightmassIni));
		VERIFYLIGHTMASSINI(GConfig->GetInt(TEXT("DevOptions.PrecomputedVisibility"), TEXT("NumImportanceSamples"), Scene.PrecomputedVisibilitySettings.NumImportanceSamples, GLightmassIni));
	}

#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Private/Lightmass/Lightmass.cpp:3660

Scope (from outer to inner):

file
function     void SpreadVisibilityCell

Source code excerpt:

void SpreadVisibilityCell(
	float CellSize, 
	float PlayAreaHeight,
	const FUncompressedPrecomputedVisibilityCell& OtherCell,
	FUncompressedPrecomputedVisibilityCell& VisibilityCell,
	int32& QueriesVisibleFromSpreadingNeighbors)
{
	// Determine whether the cell is a world space neighbor
	if (!(OtherCell.Bounds.Min == VisibilityCell.Bounds.Min && OtherCell.Bounds.Max == VisibilityCell.Bounds.Max)

#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Private/Lightmass/Lightmass.cpp:3670

Scope (from outer to inner):

file
function     void SpreadVisibilityCell

Source code excerpt:

		&& FMath::Abs(VisibilityCell.Bounds.Min.Y - OtherCell.Bounds.Min.Y) < CellSize + KINDA_SMALL_NUMBER
		// Don't spread from cells below, they're probably below the ground and see too much
		&& OtherCell.Bounds.Min.Z - VisibilityCell.Bounds.Min.Z > -PlayAreaHeight * 0.5f
		// Only spread from one cell above
		&& OtherCell.Bounds.Min.Z - VisibilityCell.Bounds.Min.Z < PlayAreaHeight * 1.5f)
	{
		// Combine the neighbor's visibility with the current cell's visibility
		// This reduces visibility errors at the cost of less effective culling
		QueriesVisibleFromSpreadingNeighbors += AccumulateVisibility(OtherCell.VisibilityData, VisibilityCell.VisibilityData);
	}
}

#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Private/Lightmass/Lightmass.cpp:3703

Scope (from outer to inner):

file
function     void FLightmassProcessor::ApplyPrecomputedVisibility

Source code excerpt:

		VERIFYLIGHTMASSINI(GConfig->GetBool(TEXT("DevOptions.PrecomputedVisibility"), TEXT("bCompressVisibilityData"), bCompressVisibilityData, GLightmassIni));
		const float CellSize = System.GetWorld()->GetWorldSettings()->VisibilityCellSize;
		float PlayAreaHeight = 0;
		VERIFYLIGHTMASSINI(GConfig->GetFloat(TEXT("DevOptions.PrecomputedVisibility"), TEXT("PlayAreaHeight"), PlayAreaHeight, GLightmassIni));
		int32 CellBucketSize = 0;
		VERIFYLIGHTMASSINI(GConfig->GetInt(TEXT("DevOptions.PrecomputedVisibility"), TEXT("CellRenderingBucketSize"), CellBucketSize, GLightmassIni));
		int32 NumCellBuckets = 0;
		VERIFYLIGHTMASSINI(GConfig->GetInt(TEXT("DevOptions.PrecomputedVisibility"), TEXT("NumCellRenderingBuckets"), NumCellBuckets, GLightmassIni));

		int32 TotalNumQueries = 0;

#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Private/Lightmass/Lightmass.cpp:3776

Scope: file

Source code excerpt:

								{
									const FUncompressedPrecomputedVisibilityCell& OtherCell = CurrentSortCell[VisibilityCellIndex];
									SpreadVisibilityCell(CellSize, PlayAreaHeight, OtherCell, CurrentCell, QueriesVisibleFromSpreadingNeighbors);
								}
							}
						}
					}
				}
			}

#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Private/Lightmass/Lightmass.cpp:3796

Scope (from outer to inner):

file
function     void FLightmassProcessor::ApplyPrecomputedVisibility

Source code excerpt:

						const FUncompressedPrecomputedVisibilityCell& OtherCell = OriginalPrecomputedVisibilityCells[OtherCellIndex];

						SpreadVisibilityCell(CellSize, PlayAreaHeight, OtherCell, CurrentCell, QueriesVisibleFromSpreadingNeighbors);
					}
				}
			}
		}	

		const FVector2D CellBucketOriginXY(CombinedPrecomputedVisibilityCells[0].Bounds.Min.X, CombinedPrecomputedVisibilityCells[0].Bounds.Min.Y);

#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Private/Lightmass/Lightmass.cpp:3830

Scope (from outer to inner):

file
function     void FLightmassProcessor::ApplyPrecomputedVisibility

Source code excerpt:

		System.GetWorld()->PersistentLevel->PrecomputedVisibilityHandler.PrecomputedVisibilityCellBucketOriginXY = CellBucketOriginXY;
		System.GetWorld()->PersistentLevel->PrecomputedVisibilityHandler.PrecomputedVisibilityCellSizeXY = CellSize;
		System.GetWorld()->PersistentLevel->PrecomputedVisibilityHandler.PrecomputedVisibilityCellSizeZ = PlayAreaHeight;
		System.GetWorld()->PersistentLevel->PrecomputedVisibilityHandler.PrecomputedVisibilityCellBucketSizeXY = CellBucketSize;
		System.GetWorld()->PersistentLevel->PrecomputedVisibilityHandler.PrecomputedVisibilityNumCellBuckets = NumCellBuckets;
		System.GetWorld()->PersistentLevel->PrecomputedVisibilityHandler.PrecomputedVisibilityCellBuckets.Empty(NumCellBuckets * NumCellBuckets);
		System.GetWorld()->PersistentLevel->PrecomputedVisibilityHandler.PrecomputedVisibilityCellBuckets.AddZeroed(NumCellBuckets * NumCellBuckets);

		// Split visibility data into ~32Kb chunks, to limit decompression time

#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Private/Lightmass/Lightmass.cpp:3852

Scope (from outer to inner):

file
function     void FLightmassProcessor::ApplyPrecomputedVisibility

Source code excerpt:

				NewCell.Min = CurrentCell.Bounds.Min;
				// We're only storing Min per cell with a shared SizeXY and SizeZ for reduced memory storage
				checkSlow(CurrentCell.Bounds.Max.Equals(CurrentCell.Bounds.Min + FVector(CellSize, CellSize, PlayAreaHeight), KINDA_SMALL_NUMBER * 10.0f));
				NewCell.ChunkIndex = ChunkIndex;
				NewCell.DataOffset = UncompressedVisibilityData.Num();
				OutputBucket.Cells.Add(NewCell);
				UncompressedVisibilityData.Append(CurrentCell.VisibilityData);
				// Create a new chunk if we've reached the size limit or this is the last cell in a bucket
				if (UncompressedVisibilityData.Num() > ChunkSizeTarget || CellIndex == CellRenderingBuckets[BucketIndex].Num() - 1)

#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/Lighting/PrecomputedVisibility.cpp:328

Scope (from outer to inner):

file
namespace    Lightmass
function     void FStaticLightingSystem::SetupPrecomputedVisibility

Source code excerpt:

					// Place a new cell if this is the highest height
					if (HeightIndex + 1 == Cell.HitTriangles.Num()
						// Or if there's a gap above this height of size PlayAreaHeight
						|| ((Cell.HitTriangles[HeightIndex + 1].HeightRange.Y - CurrentMaxHeight) > PrecomputedVisibilitySettings.PlayAreaHeight
						// And this height is not within a cell that was just placed
						&& CurrentMaxHeight - LastSampleHeight > PrecomputedVisibilitySettings.PlayAreaHeight))
					{
						FPrecomputedVisibilityCell NewCell;
						NewCell.Bounds = FBox3f(
							FVector4f(
								CurrentPosition.X - PrecomputedVisibilitySettings.CellSize / 2,
								CurrentPosition.Y - PrecomputedVisibilitySettings.CellSize / 2,

#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/Lighting/PrecomputedVisibility.cpp:342

Scope (from outer to inner):

file
function     void FStaticLightingSystem::SetupPrecomputedVisibility

Source code excerpt:

								CurrentPosition.X + PrecomputedVisibilitySettings.CellSize / 2,
								CurrentPosition.Y + PrecomputedVisibilitySettings.CellSize / 2,
								CurrentMaxHeight + PrecomputedVisibilitySettings.PlayAreaHeight));

						AllPrecomputedVisibilityCells.Add(NewCell);
						LastSampleHeight = CurrentMaxHeight;
						PlacedHeightRanges.Add(FVector2f(NewCell.Bounds.Min.Z, NewCell.Bounds.Max.Z));
					}
				}

				// Fractions of PrecomputedVisibilitySettings.PlayAreaHeight to guarantee have cell coverage
				float TestHeights[3] = {.4f, .6f, .8f};
				
				// Pass 2 - make sure the space above every triangle is covered by precomputed visibility cells, even if the cells are placed poorly (intersecting the floor)
				for (int32 HeightIndex = 0; HeightIndex < Cell.HitTriangles.Num() - 1; HeightIndex++)
				{
					for (int32 ExtremaIndex = 0; ExtremaIndex < 2; ExtremaIndex++)
					{
						const float CurrentMaxHeight = ExtremaIndex == 0 ? Cell.HitTriangles[HeightIndex].HeightRange.X : Cell.HitTriangles[HeightIndex].HeightRange.Y;
						const float CompareHeight = CurrentMaxHeight + .5f * PrecomputedVisibilitySettings.PlayAreaHeight;

						for (int32 TestIndex = 0; TestIndex < UE_ARRAY_COUNT(TestHeights); TestIndex++)
						{
							const float TestHeight = CurrentMaxHeight + TestHeights[TestIndex] * PrecomputedVisibilitySettings.PlayAreaHeight;

							int32 ClosestCellInZIndex = -1;
							float ClosestCellInZDistance = FLT_MAX;
							bool bInsideCell = false;

							for (int32 PlacedHeightIndex = 0; PlacedHeightIndex < PlacedHeightRanges.Num(); PlacedHeightIndex++)

#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/Lighting/PrecomputedVisibility.cpp:403

Scope: file

Source code excerpt:

									if (CompareHeight < NearestCellCompareHeight)
									{
										DesiredCellBottom = FMath::Min(DesiredCellBottom, NearestCellHeightRange.X - PrecomputedVisibilitySettings.PlayAreaHeight);
									}
									else if (CompareHeight > NearestCellCompareHeight)
									{
										DesiredCellBottom = FMath::Max(DesiredCellBottom, NearestCellHeightRange.Y);
									}
								}

#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/Lighting/PrecomputedVisibility.cpp:419

Scope: file

Source code excerpt:

									CurrentPosition.X + PrecomputedVisibilitySettings.CellSize / 2,
									CurrentPosition.Y + PrecomputedVisibilitySettings.CellSize / 2,
									DesiredCellBottom + PrecomputedVisibilitySettings.PlayAreaHeight));

								AllPrecomputedVisibilityCells.Add(NewCell);
								PlacedHeightRanges.Add(FVector2f(NewCell.Bounds.Min.Z, NewCell.Bounds.Max.Z));
							}
						}
					}

#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Private/Lighting/PrecomputedVisibility.cpp:467

Scope (from outer to inner):

file
namespace    Lightmass
function     void FStaticLightingSystem::SetupPrecomputedVisibility

Source code excerpt:

					SnappedPosition.X,
					SnappedPosition.Y,
					SnappedPosition.Z - .5f * PrecomputedVisibilitySettings.PlayAreaHeight),
				FVector4f(
					SnappedPosition.X + PrecomputedVisibilitySettings.CellSize,
					SnappedPosition.Y + PrecomputedVisibilitySettings.CellSize,
					SnappedPosition.Z + .5f * PrecomputedVisibilitySettings.PlayAreaHeight));

			AllPrecomputedVisibilityCells.Add(NewCell);

			// Verify that the camera track position is inside the placed cell
			checkSlow(NewCell.Bounds.IsInside(CurrentPosition));
		}

#Loc: <Workspace>/Engine/Source/Programs/UnrealLightmass/Public/SceneExport.h:397

Scope (from outer to inner):

file
namespace    Lightmass
class        class FPrecomputedVisibilitySettings

Source code excerpt:


	/** World space size of visibility cells in the z dimension. */
	float PlayAreaHeight;

	/** Amount to increase the bounds of meshes when querying their visibility.  Larger scales reduce visibility errors at the cost of less effective culling. */
	float MeshBoundsScale;

	/** Minimum number of samples on the mesh for each cell - mesh query.  Small meshes use less samples. */
	int32 MinMeshSamples;