# Procedural Content Generation (PCG)

PCG is the data-driven procedural placement framework in UE5 (5.2+). It generates actors, mesh instances, and spline-based content using node graphs.

## Core concepts

- **PCGGraph**: A node graph asset defining generation rules (created in Content Browser)
- **PCGVolume / PCGComponent**: Placed in a level, references a PCGGraph, defines generation bounds
- **PCGData**: Data flowing through the graph — point clouds, meshes, landscape samples
- **PCGSettings**: Per-node configuration controlling behavior (base class for custom nodes)
- **PCGPoint**: Individual point with transform, density, color, and metadata attributes

## Workflow for setting up PCG in a level

To place a PCG volume and assign a graph via Python:
1. Use `query_ue_api('EditorActorSubsystem')` to find the actor spawning API
2. Spawn a PCGVolume actor at the desired location and scale it to cover the generation area
3. Get the PCGComponent from the spawned volume (use `get_component_by_class`)
4. Load the PCG graph asset and assign it to the component's `graph` property
5. Use `dir()` on the PCGComponent to discover methods for triggering generation

PCG graphs themselves are best created in the Content Browser (right-click -> PCG Graph). Python is mainly used for placing volumes, assigning graphs, and triggering generation.

## Key PCG graph nodes

### Input nodes
- **Get Landscape Data**: Sample landscape height/normals for terrain-aware placement
- **Get Spline Data**: Use spline as path for placement (roads, rivers, fences)
- **Surface Sampler**: Generate points on mesh surfaces
- **Volume Sampler**: Generate points within a volume

### Filter and transform nodes
- **Density Filter**: Remove points by density threshold
- **Distance**: Filter by distance to other points or actors
- **Slope Filter**: Filter by terrain slope angle
- **Transform Points**: Scale, rotate, offset points
- **Bounds Modifier**: Restrict to bounding volume
- **Difference**: Boolean subtract one point cloud from another

### Output nodes
- **Static Mesh Spawner**: Place instanced meshes at point locations
- **Spline Mesh Spawner**: Place meshes along splines
- **Actor Spawner**: Spawn Blueprint actors at points

### Utility nodes
- **Random**: Randomize point properties (scale, rotation, density)
- **Attribute Operation**: Math on point attributes (add, multiply, remap)
- **Merge**: Combine multiple point clouds
- **Copy Points**: Copy attributes from one set to another

## Custom PCG node in C++

```cpp
// Header:
#pragma once
#include "PCGSettings.h"
#include "PCG_CustomScatter.generated.h"

UCLASS()
class UPCGSettings_CustomScatter : public UPCGSettings
{
    GENERATED_BODY()

public:
    UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings")
    int32 PointCount = 100;

    UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings")
    float Radius = 5000.f;

    virtual FPCGElementPtr CreateElement() const override;

protected:
    virtual TArray<FPCGPinProperties> InputPinProperties() const override;
    virtual TArray<FPCGPinProperties> OutputPinProperties() const override;
};

// Source:
FPCGElementPtr UPCGSettings_CustomScatter::CreateElement() const
{
    return MakeShared<FPCGElement_CustomScatter>();
}

bool FPCGElement_CustomScatter::ExecuteInternal(FPCGContext* Context) const
{
    const UPCGSettings_CustomScatter* Settings =
        Context->GetInputSettings<UPCGSettings_CustomScatter>();

    UPCGPointData* PointData = NewObject<UPCGPointData>();
    TArray<FPCGPoint>& Points = PointData->GetMutablePoints();

    for (int32 i = 0; i < Settings->PointCount; ++i)
    {
        FPCGPoint& Point = Points.Emplace_GetRef();
        float Angle = FMath::FRandRange(0.f, 2.f * PI);
        float Dist = FMath::FRandRange(0.f, Settings->Radius);
        Point.Transform.SetLocation(FVector(
            FMath::Cos(Angle) * Dist,
            FMath::Sin(Angle) * Dist, 0.f));
        Point.Density = FMath::FRandRange(0.5f, 1.0f);
    }

    Context->OutputData.TaggedData.Emplace_GetRef().Data = PointData;
    return true;
}
```

Custom node registration: subclass UPCGSettings, override `CreateElement()` to return your FPCGElement subclass. The node automatically appears in the PCG graph editor.

## Common PCG patterns for game levels
- **Forest scatter**: Landscape input -> Slope filter (< 30 degrees) -> Density noise -> Random scale/rotation -> Tree mesh spawner
- **Road/path**: Spline input -> Sample along spline -> Offset for road width -> Mesh spawner (road tiles)
- **Village layout**: Grid sampler -> Distance filter (spacing) -> Random selection -> Building actor spawner
- **Cliff placement**: Landscape input -> Steep slope filter (> 60 degrees) -> Rock mesh spawner with alignment to normal
- **River foliage**: Spline input -> Offset scatter -> Distance-from-water density -> Fern/reed spawner
- **Interior decoration**: Volume sampler -> Grid snap -> Random furniture actor spawner

## Build.cs dependencies

```csharp
PublicDependencyModuleNames.AddRange(new string[] {
    "PCG"
});
```

## Naming conventions
- PCG_ prefix for PCG Graph assets: PCG_ForestScatter, PCG_RoadDecor
- PCGSettings_ prefix for custom C++ node classes
- PCGVol_ prefix for PCG Volume actors in levels

## Performance tips
- Use partitioning for large worlds (PCG auto-partitions in World Partition levels)
- Enable GPU point generation for high point counts
- Set appropriate generation trigger (OnLoad, InEditor, or Manual) per use case
- Use LOD on spawned meshes — PCG can generate thousands of instances
- Hierarchical generation: generate large features first, then populate details

## Critical rules
- PCG requires UE5.2+ and the PCG plugin must be enabled in the project
- Add "PCG" to Build.cs PublicDependencyModuleNames
- PCGGraph is an asset — create in Content Browser, then reference from PCGComponent
- Always test PCG graphs with the "Generate" button in editor before relying on auto-generation
- Use seed values for reproducible generation (same seed = same output)
- Use `query_ue_api('PCGComponent')` or `query_ue_api('PCGGraph')` to discover PCG classes and their properties
- Use `dir()` on PCG objects in Python to discover available methods at runtime
