ShowFlag.BuilderBrush

ShowFlag.BuilderBrush

#Overview

name: ShowFlag.BuilderBrush

This variable is created as a Console Variable (cvar).

It is referenced in 22 C++ source files.

#Summary

#Usage in the C++ source code

The purpose of ShowFlag.BuilderBrush is to control the rendering of the builder brush in the Unreal Engine editor. The builder brush is a special brush used for level editing and geometry creation.

This setting variable is primarily used by the rendering system and the editor’s geometry manipulation tools. Based on the callsites, it is utilized in the following subsystems and modules:

  1. GeometryMode plugin
  2. UnrealEd module
  3. Engine’s rendering system

The value of this variable is set in the engine’s show flags system, as evident from the SHOWFLAG_FIXED_IN_SHIPPING macro used in the Engine/Source/Runtime/Engine/Public/ShowFlagsValues.inl file.

The associated variable BuilderBrush interacts closely with ShowFlag.BuilderBrush. It represents the actual builder brush object in the level editor.

Developers should be aware of the following when using this variable:

  1. It’s specifically for editor use and is not accessible in shipping builds.
  2. It controls the visibility of the builder brush in the editor viewports.
  3. It’s used in conjunction with other editor tools for geometry manipulation and level editing.

Best practices when using this variable include:

  1. Only modify it in editor-specific code.
  2. Use it in conjunction with other relevant show flags for consistent editor behavior.
  3. Be mindful of its impact on editor performance, especially when dealing with complex geometry.

Regarding the associated variable BuilderBrush:

The purpose of BuilderBrush is to represent the actual builder brush actor in the level. It’s used extensively in geometry editing operations, such as adding, subtracting, or modifying level geometry.

This variable is primarily used in the GeometryMode plugin and the UnrealEd module. It’s typically accessed through the world’s default brush or the current level’s actor list.

The value of BuilderBrush is usually set when the level is created or when performing specific geometry operations in the editor.

Developers should be aware that:

  1. BuilderBrush is a crucial part of the level editing process.
  2. It’s used in various geometry operations, including lathing, pen tool operations, and CSG (Constructive Solid Geometry) operations.
  3. It interacts closely with the level’s world settings.

Best practices for using BuilderBrush include:

  1. Always check for null before using it, as it might not always be available.
  2. Use it in conjunction with appropriate transaction systems for undo/redo support.
  3. Be cautious when modifying its properties, as it can affect the entire level geometry.

#References in C++ code

#Callsites

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

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Public/ShowFlagsValues.inl:197

Scope: file

Source code excerpt:

SHOWFLAG_ALWAYS_ACCESSIBLE(SkeletalMeshes, SFG_Normal, NSLOCTEXT("UnrealEd", "SkeletalMeshesSF", "Skeletal Meshes"))
/** if the builder brush (editor) is getting rendered */
SHOWFLAG_FIXED_IN_SHIPPING(0, BuilderBrush, SFG_Hidden, NSLOCTEXT("UnrealEd", "BuilderBrushSF", "Builder Brush"))
/** Render translucency, for now SHOWFLAG_ALWAYS_ACCESSIBLE because it's exposed in SceneCapture */
SHOWFLAG_ALWAYS_ACCESSIBLE(Translucency, SFG_Normal, NSLOCTEXT("UnrealEd", "TranslucencySF", "Translucency"))
/** Draw billboard components */
SHOWFLAG_FIXED_IN_SHIPPING(1, BillboardSprites, SFG_Advanced, NSLOCTEXT("UnrealEd", "BillboardSpritesSF", "Billboard Sprites"))
/** Use LOD parenting, MinDrawDistance, etc. If disabled, will show LOD parenting lines */
SHOWFLAG_ALWAYS_ACCESSIBLE(LOD, SFG_Advanced, NSLOCTEXT("UnrealEd", "LODSF", "LOD Parenting"))

#Associated Variable and Callsites

This variable is associated with another variable named BuilderBrush. They share the same value. See the following C++ source code.

#Loc: <Workspace>/Engine/Plugins/Editor/GeometryMode/Source/GeometryMode/Private/GeometryModifiers.cpp:157

Scope (from outer to inner):

file
function     void UGeomModifier::CacheBrushState

Source code excerpt:

{
	FEdModeGeometry* GeomMode = (FEdModeGeometry*)GLevelEditorModeTools().GetActiveMode(FGeometryEditingModes::EM_Geometry);
	ABrush* BuilderBrush = GeomMode->GetWorld()->GetDefaultBrush();
	if( !CachedPolys )
	{
		//Create the list of polys
		CachedPolys = NewObject<UPolys>(this);
	}
	CachedPolys->Element.Empty();

	//Create duplicates of all of the polys in the brush
	for( int32 polyIndex = 0 ; polyIndex < BuilderBrush->Brush->Polys->Element.Num() ; ++polyIndex )
	{
		FPoly currentPoly = BuilderBrush->Brush->Polys->Element[polyIndex];
		FPoly newPoly;
		newPoly.Init();
		newPoly.Base = currentPoly.Base;

		//Add all of the verts to the new poly
		for( int32 vertIndex = 0; vertIndex < currentPoly.Vertices.Num(); ++vertIndex )

#Loc: <Workspace>/Engine/Plugins/Editor/GeometryMode/Source/GeometryMode/Private/GeometryModifiers.cpp:187

Scope (from outer to inner):

file
function     void UGeomModifier::RestoreBrushState

Source code excerpt:

{
	FEdModeGeometry* GeomMode = (FEdModeGeometry*)GLevelEditorModeTools().GetActiveMode(FGeometryEditingModes::EM_Geometry);
	ABrush* BuilderBrush = GeomMode->GetWorld()->GetDefaultBrush();

	//Remove all of the current polys
	BuilderBrush->Brush->Polys->Element.Empty();

	//Add all of the cached polys
	for( int32 polyIndex = 0 ; polyIndex < CachedPolys->Element.Num() ; polyIndex++ )
	{
		BuilderBrush->Brush->Polys->Element.Push(CachedPolys->Element[polyIndex]);
	}

	BuilderBrush->Brush->BuildBound();
	
	BuilderBrush->ReregisterAllComponents();

	GeomMode->FinalizeSourceData();
	GeomMode->GetFromSource();

	GEditor->SelectNone( true, true );

#Loc: <Workspace>/Engine/Plugins/Editor/GeometryMode/Source/GeometryMode/Private/GeometryModifiers.cpp:1012

Scope (from outer to inner):

file
function     void UGeomModifier_Lathe::Apply

Source code excerpt:

	// We will be replacing the builder brush, so get it prepped.

	ABrush* BuilderBrush = GeomMode->GetWorld()->GetDefaultBrush();

	BuilderBrush->SetActorLocation(GeomMode->GetWidgetLocation(), false);
	BuilderBrush->SetPivotOffset(FVector::ZeroVector);
	BuilderBrush->SetFlags( RF_Transactional );
	BuilderBrush->Brush->Polys->Element.Empty();

	// Ensure the builder brush is unhidden.
	BuilderBrush->SetHidden(false);
	BuilderBrush->bHiddenEdLayer = false;
	BuilderBrush->SetIsTemporarilyHiddenInEditor( false );

	// Some convenience flags
	bool bNeedCaps = (InSegments < InTotalSegments);

	// Lathe every selected ABrushShape actor into the builder brush

#Loc: <Workspace>/Engine/Plugins/Editor/GeometryMode/Source/GeometryMode/Private/GeometryModifiers.cpp:1110

Scope (from outer to inner):

file
function     void UGeomModifier_Lathe::Apply

Source code excerpt:

						FPoly NewPoly;
						NewPoly.Init();
						NewPoly.Base = (FVector3f)BuilderBrush->GetActorLocation();

						NewPoly.Vertices.Add( (FVector3f)vtx0 );
						NewPoly.Vertices.Add( (FVector3f)vtx1 );
						NewPoly.Vertices.Add( (FVector3f)vtx2 );
						NewPoly.Vertices.Add( (FVector3f)vtx3 );

						if( NewPoly.Finalize( BuilderBrush, 1 ) == 0 )
						{
							BuilderBrush->Brush->Polys->Element.Add( NewPoly );
						}
						
					}
				}
			}

#Loc: <Workspace>/Engine/Plugins/Editor/GeometryMode/Source/GeometryMode/Private/GeometryModifiers.cpp:1149

Scope (from outer to inner):

file
function     void UGeomModifier_Lathe::Apply

Source code excerpt:

					}

					Poly.Finalize( BuilderBrush, 1 );

					// Break the shape down into convex shapes.

					TArray<FPoly> Polygons;
					Poly.Triangulate( BuilderBrush, Polygons );
					FPoly::OptimizeIntoConvexPolys( BuilderBrush, Polygons );

					// Add the resulting convex polygons into the brush

					for( int32 p = 0 ; p < Polygons.Num() ; ++p )
					{
						FPoly Polygon = Polygons[p];

						if (Polygon.Finalize(BuilderBrush, 1) == 0)
						{
							BuilderBrush->Brush->Polys->Element.Add(Polygon);
						}
					}

					//
					// Create the end cap
					//

#Loc: <Workspace>/Engine/Plugins/Editor/GeometryMode/Source/GeometryMode/Private/GeometryModifiers.cpp:1195

Scope (from outer to inner):

file
function     void UGeomModifier_Lathe::Apply

Source code excerpt:

					}

					Poly.Finalize( BuilderBrush, 1 );

					// Break the shape down into convex shapes.

					Polygons.Empty();
					Poly.Triangulate( BuilderBrush, Polygons );
					FPoly::OptimizeIntoConvexPolys( BuilderBrush, Polygons );

					// Add the resulting convex polygons into the brush

					for( int32 p = 0 ; p < Polygons.Num() ; ++p )
					{
						FPoly Polygon = Polygons[p];
						Polygon.Reverse();

						if (Polygon.Finalize(BuilderBrush, 1) == 0)
						{
							BuilderBrush->Brush->Polys->Element.Add(Polygon);
						}
					}
				}
			}
		}
	}

#Loc: <Workspace>/Engine/Plugins/Editor/GeometryMode/Source/GeometryMode/Private/GeometryModifiers.cpp:1222

Scope (from outer to inner):

file
function     void UGeomModifier_Lathe::Apply

Source code excerpt:

	// Finalize the builder brush

	BuilderBrush->Brush->BuildBound();

	BuilderBrush->ReregisterAllComponents();

	GeomMode->FinalizeSourceData();
	GeomMode->GetFromSource();

	GEditor->SelectNone( true, true );
	GEditor->SelectActor( BuilderBrush, true, true );

	if( DoEdgesOverlap() )
	{//Overlapping edges yielded an invalid brush state
		RestoreBrushState();
	}
	else

#Loc: <Workspace>/Engine/Plugins/Editor/GeometryMode/Source/GeometryMode/Private/GeometryModifiers.cpp:1245

Scope (from outer to inner):

file
function     void UGeomModifier_Lathe::Apply

Source code excerpt:


	// Deselect & hide builder brush
	BuilderBrush->SetIsTemporarilyHiddenInEditor(true);
	GEditor->SelectActor(BuilderBrush, false, false);
}

/*------------------------------------------------------------------------------
	UGeomModifier_Pen
------------------------------------------------------------------------------*/
UGeomModifier_Pen::UGeomModifier_Pen(const FObjectInitializer& ObjectInitializer)

#Loc: <Workspace>/Engine/Plugins/Editor/GeometryMode/Source/GeometryMode/Private/GeometryModifiers.cpp:1298

Scope (from outer to inner):

file
function     void UGeomModifier_Pen::Apply

Source code excerpt:

		FEdModeGeometry* GeomMode = (FEdModeGeometry*)GLevelEditorModeTools().GetActiveMode(FGeometryEditingModes::EM_Geometry);
		ABrush* ResultingBrush = GeomMode->GetWorld()->GetDefaultBrush();
		ABrush* BuilderBrush = GeomMode->GetWorld()->GetDefaultBrush();

		// Move all the vertices that the user placed to the same "height" as the builder brush, based on
		// viewport orientation.  This is preferable to always creating the new builder brush at height zero.
		
		for( int32 v = 0 ; v < ShapeVertices.Num() ; ++v )
		{

#Loc: <Workspace>/Engine/Plugins/Editor/GeometryMode/Source/GeometryMode/Private/GeometryModifiers.cpp:1310

Scope (from outer to inner):

file
function     void UGeomModifier_Pen::Apply

Source code excerpt:

			{
			case LVT_OrthoXY:
				vtx->Z = BuilderBrush->GetActorLocation().Z;
				break;

			case LVT_OrthoXZ:
				vtx->Y = BuilderBrush->GetActorLocation().Y;
				break;

			case LVT_OrthoYZ:
				vtx->X = BuilderBrush->GetActorLocation().X;
				break;
			}
		}

		// Generate center location from the shape's center
		FBox WorldBounds(ShapeVertices.GetData(), ShapeVertices.Num());

#Loc: <Workspace>/Engine/Plugins/Editor/GeometryMode/Source/GeometryMode/Private/GeometryModifiers.cpp:1334

Scope (from outer to inner):

file
function     void UGeomModifier_Pen::Apply

Source code excerpt:

		{	
			// Create a shape brush instead of modifying the builder brush
			ResultingBrush = BuilderBrush->GetWorld()->SpawnActor<ABrushShape>(BaseLocation, FRotator::ZeroRotator);
			
			ResultingBrush->PreEditChange(NULL);
			// It's OK to create an empty brush here as we are going to re-create the polys anyway.
			FBSPOps::csgCopyBrush( ResultingBrush, BuilderBrush, PF_DefaultFlags,  BuilderBrush->GetFlags(), true, true, true );
			ResultingBrush->PostEditChange();			

		}
		else
		{
			ResultingBrush = FBSPOps::csgAddOperation( BuilderBrush, PF_DefaultFlags, Brush_Add );
			if (ResultingBrush == nullptr)
			{
				return;
			}

			if (ResultingBrush->GetBrushBuilder())

#Loc: <Workspace>/Engine/Plugins/Editor/GeometryMode/Source/GeometryMode/Private/GeometryModifiers.cpp:2045

Scope (from outer to inner):

file
namespace    GeometryClipping
function     static ABrush* ClipBrushAgainstPlane

Source code excerpt:


	// Move the new brush to where the new brush was to preserve brush ordering.
	ABrush* BuilderBrush = World->GetDefaultBrush();
	if( InBrush == BuilderBrush )
	{
		// Special-case behavior for the builder brush.

		// Copy the temporary brush back over onto the builder brush (keeping object flags)
		BuilderBrush->Modify(false);
		FBSPOps::csgCopyBrush( BuilderBrush, ClippedBrush, BuilderBrush->PolyFlags, BuilderBrush->GetFlags(), 0, true );
		ULayersSubsystem* Layers = GEditor->GetEditorSubsystem<ULayersSubsystem>();
		Layers->DisassociateActorFromLayers( ClippedBrush );
		World->EditorDestroyActor( ClippedBrush, false );
		// Note that we're purposefully returning non-NULL here to report that the clip was successful,
		// even though the ClippedBrush has been destroyed!
	}

#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Private/EditorBrushBuilder.cpp:57

Scope (from outer to inner):

file
function     bool UEditorBrushBuilder::EndBrush

Source code excerpt:


	check( InWorld != nullptr );
	ABrush* BuilderBrush = (InBrush != nullptr) ? InBrush : InWorld->GetDefaultBrush();

	// Ensure the builder brush is unhidden.
	BuilderBrush->SetHidden(false);
	BuilderBrush->bHiddenEdLayer = false;

	AActor* Actor = GEditor->GetSelectedActors()->GetTop<AActor>();
	FVector Location;
	if ( InBrush == nullptr )
	{
		Location = Actor ? Actor->GetActorLocation() : BuilderBrush->GetActorLocation();
	}
	else
	{
		Location = InBrush->GetActorLocation();
	}

	UModel* Brush = BuilderBrush->Brush;
	if (Brush == nullptr)
	{
		return true;
	}

	Brush->Modify(false);
	BuilderBrush->Modify(false);

	FRotator Temp(0.0f,0.0f,0.0f);
	FSnappingUtils::SnapToBSPVertex( Location, FVector::ZeroVector, Temp );
	BuilderBrush->SetActorLocation(Location, false);
	BuilderBrush->SetPivotOffset( FVector::ZeroVector );

	// Try and maintain the materials assigned to the surfaces. 
	TArray<FPoly> CachedPolys;
	UMaterialInterface* CachedMaterial = nullptr;
	if( Brush->Polys->Element.Num() == Polys.Num() )
	{

#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Private/EditorBrushBuilder.cpp:141

Scope (from outer to inner):

file
function     bool UEditorBrushBuilder::EndBrush

Source code excerpt:

			Poly.Vertices.Emplace(Vertices[It->VertexIndices[j]]);
		}
		if( Poly.Finalize( BuilderBrush, 1 ) == 0 )
		{
			Brush->Polys->Element.Add(Poly);
		}
	}

	if( MergeCoplanars )

#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Private/EditorBrushBuilder.cpp:158

Scope (from outer to inner):

file
function     bool UEditorBrushBuilder::EndBrush

Source code excerpt:


	GEditor->RedrawLevelEditingViewports();
	GEditor->SetPivot(BuilderBrush->GetActorLocation(), false, true);

	BuilderBrush->ReregisterAllComponents();

	return true;
}

int32 UEditorBrushBuilder::GetVertexCount() const
{

#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Private/EditorModeManager.cpp:1600

Scope (from outer to inner):

file
function     void FEditorModeTools::DrawHUD

Source code excerpt:

	const bool bShowBrushes = View->Family->EngineShowFlags.Brushes;
	const bool bShowBSP = View->Family->EngineShowFlags.BSP;
	const bool bShowBuilderBrush = View->Family->EngineShowFlags.BuilderBrush != 0;

	UTexture2D* VertexTexture = GetVertexTexture();
	const float TextureSizeX = VertexTexture->GetSizeX() * (bLargeVertices ? 1.0f : 0.5f);
	const float TextureSizeY = VertexTexture->GetSizeY() * (bLargeVertices ? 1.0f : 0.5f);

	GetEditorSelectionSet()->ForEachSelectedObject<AStaticMeshActor>([View, Canvas, VertexTexture, TextureSizeX, TextureSizeY, bIsHitTesting](AStaticMeshActor* Actor)

#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Private/Factories/EditorFactories.cpp:778

Scope (from outer to inner):

file
function     UObject* ULevelFactory::FactoryCreateText

Source code excerpt:

	// We need to detect if the .t3d file is the entire level or just selected actors, because we
	// don't want to replace the WorldSettings and BuildBrush if they already exist. To know if we
	// can skip the WorldSettings and BuilderBrush (which will always be the first two actors if the entire
	// level was exported), we make sure the first actor is a WorldSettings, if it is, and we already had
	// a WorldSettings, then we skip the builder brush
	// In other words, if we are importing a full level into a full level, we don't want to import
	// the WorldSettings and BuildBrush
	bool bShouldSkipImportSpecialActors = false;
	bool bHitLevelToken = false;

#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Private/Factories/EditorFactories.cpp:970

Scope (from outer to inner):

file
function     UObject* ULevelFactory::FactoryCreateText

Source code excerpt:

				}

				// If we need to skip the WorldSettings and BuilderBrush, skip the first two actors.  Note that
				// at this point, we already know that we have a WorldSettings and BuilderBrush in the .t3d.
				if ( FLevelUtils::IsLevelLocked(World->GetCurrentLevel()) )
				{
					UE_LOG(LogEditorFactories, Warning, TEXT("Import actor: The requested operation could not be completed because the level is locked."));
					GEditor->GetEditorSubsystem<UImportSubsystem>()->BroadcastAssetPostImport(this, nullptr );
					return nullptr;
				}

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/ActorEditorUtils.cpp:25

Scope (from outer to inner):

file
namespace    FActorEditorUtils
function     bool IsABuilderBrush

Source code excerpt:

			{
				// If the builder brush exists then it will be the 2nd actor in the actors array.
				ABrush* BuilderBrush = Cast<ABrush>(ActorLevel->Actors[1]);
				// If the second actor is not a brush then it certainly cannot be the builder brush.
				if ((BuilderBrush != nullptr) && (BuilderBrush->GetBrushComponent() != nullptr) && (BuilderBrush->Brush != nullptr))
				{
					bIsBuilder = (BuilderBrush == InActor);
				}
			}			
		}
#endif
		return bIsBuilder;
	}

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Components/BrushComponent.cpp:307

Scope (from outer to inner):

file
class        class FBrushSceneProxy final : public FPrimitiveSceneProxy
function     virtual FPrimitiveViewRelevance GetViewRelevance

Source code excerpt:

			if( GIsEditor )
			{
				const bool bShowBuilderBrush = View->Family->EngineShowFlags.BuilderBrush != 0;

				// Only render builder brush and only if the show flags indicate that we should render builder brushes.
				if( bBuilder && (!bShowBuilderBrush) )
				{
					bNeverShow = true;
				}

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Public/ShowFlagsValues.inl:197

Scope: file

Source code excerpt:

SHOWFLAG_ALWAYS_ACCESSIBLE(SkeletalMeshes, SFG_Normal, NSLOCTEXT("UnrealEd", "SkeletalMeshesSF", "Skeletal Meshes"))
/** if the builder brush (editor) is getting rendered */
SHOWFLAG_FIXED_IN_SHIPPING(0, BuilderBrush, SFG_Hidden, NSLOCTEXT("UnrealEd", "BuilderBrushSF", "Builder Brush"))
/** Render translucency, for now SHOWFLAG_ALWAYS_ACCESSIBLE because it's exposed in SceneCapture */
SHOWFLAG_ALWAYS_ACCESSIBLE(Translucency, SFG_Normal, NSLOCTEXT("UnrealEd", "TranslucencySF", "Translucency"))
/** Draw billboard components */
SHOWFLAG_FIXED_IN_SHIPPING(1, BillboardSprites, SFG_Advanced, NSLOCTEXT("UnrealEd", "BillboardSpritesSF", "Billboard Sprites"))
/** Use LOD parenting, MinDrawDistance, etc. If disabled, will show LOD parenting lines */
SHOWFLAG_ALWAYS_ACCESSIBLE(LOD, SFG_Advanced, NSLOCTEXT("UnrealEd", "LODSF", "LOD Parenting"))