# Landscape and Foliage System

> Use `query_ue_api` and `dir()` at runtime to discover exact class names, properties, and methods before writing any Python code.

## Landscape Architecture

A Landscape actor is a heightmap-based terrain system optimized for large outdoor environments.

### Key Parameters (set at creation, immutable after)
- **Section Size**: quads per section — 7x7, 15x15, 31x31, 63x63, 127x127, 255x255
- **Sections per Component**: 1x1 or 2x2 (affects LOD granularity and culling)
- **Component Count**: total components — more components = finer LOD control, higher overhead
- **Recommended overall sizes**: 8129x8129 (large open world), 4033x4033 (medium), 2017x2017 (small), 1009x1009 (arena)
- **Height range**: -256 to +256 in landscape space; scale the actor for real-world height

### Finding Existing Landscapes

```python
import unreal

eas = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)
all_actors = eas.get_all_level_actors()

# Find landscape actors
landscapes = [a for a in all_actors if 'Landscape' in a.get_class().get_name()]
for ls in landscapes:
    print(f'{ls.get_actor_label()} — {ls.get_class().get_name()}')
    print(f'  Transform: {ls.get_actor_transform()}')
    # Explore properties
    print(f'  Properties: {sorted(ls.get_editor_property_names())[:20]}')
```

## Landscape Materials

Landscape materials use specialized material nodes that differ from standard materials.

### Architecture
- **LandscapeLayerBlend** node: blends between multiple texture layers based on painted weights
- **LandscapeGrassOutput** node: auto-spawns ground cover meshes based on material layers
- **LandscapeLayerCoords** node: provides UV coordinates optimized for landscape tiling

### Creating a Landscape Material

```python
import unreal, json

# Create material asset (see asset_creation.md for full workflow)
bridge = unreal.UNDUniversalBridge

mat = unreal.load_asset('/Game/Materials/M_Landscape_Base')
# Or create via MaterialFactoryNew if doesn't exist

# Build landscape material graph via BuildGraphFromSpec
spec = {
    "nodes": [
        {"id": "blend",    "action_query": "LandscapeLayerBlend", "x": -200, "y": 0},
        {"id": "grass_tex","action_query": "TextureSample",       "x": -600, "y": -100},
        {"id": "rock_tex", "action_query": "TextureSample",       "x": -600, "y": 200},
        {"id": "coords",   "action_query": "LandscapeLayerCoords","x": -800, "y": 50}
    ],
    "connections": [
        {"from": "blend",     "from_pin": "", "to": "output", "to_pin": "BaseColor"},
        {"from": "coords",    "from_pin": "", "to": "grass_tex", "to_pin": "UVs"},
        {"from": "coords",    "from_pin": "", "to": "rock_tex",  "to_pin": "UVs"}
    ]
}
result = json.loads(bridge.build_graph_from_spec(mat, json.dumps(spec)))
print(result)
```

### Layer Setup
- Each layer in a LandscapeLayerBlend has a name (e.g., "Grass", "Rock", "Dirt") and a blend type (LB_WeightBlend or LB_AlphaBlend)
- Layer names must match the Landscape Layer Info assets used for painting
- Typically add normal maps and roughness per layer for realistic terrain

## Foliage System

### Two Placement Approaches
1. **Instanced Foliage (Foliage Types)** — efficient GPU instancing, painted via Foliage tool or spawned via LandscapeGrassOutput
2. **Regular Actor Placement** — individual StaticMeshActors or Blueprint actors for unique/interactive objects

### Creating Foliage Type Assets

```python
import unreal

# Discover the foliage type factory:
# query_ue_api('FoliageTypeFactory')

# Create foliage type asset
helpers = unreal.UNDBlueprintHelpers
ft = helpers.create_asset_with_factory(
    '/Game/Foliage/FT_Grass',
    '/Script/FoliageEdit.FoliageType_InstancedStaticMeshFactory',
    '{}'
)

if ft:
    # Set mesh reference
    mesh = unreal.load_asset('/Game/Meshes/SM_Grass_01')
    if mesh:
        ft.set_editor_property('mesh', mesh)

    # Configure density and placement
    ft.set_editor_property('density', 400.0)
    ft.set_editor_property('align_to_normal', True)
    ft.set_editor_property('random_yaw', True)

    # Set scale range — discover property names via dir()
    props = ft.get_editor_property_names()
    scale_props = [p for p in sorted(props) if 'scale' in str(p).lower()]
    print(f'Scale properties: {scale_props}')

    unreal.EditorAssetLibrary.save_asset('/Game/Foliage/FT_Grass')
    print(f'Created foliage type: {ft.get_path_name()}')
```

### Spawning Actors for Environment

```python
import unreal

eas = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)

# Spawn a static mesh actor
actor = eas.spawn_actor_from_class(
    unreal.StaticMeshActor,
    unreal.Vector(500, 0, 0),
    unreal.Rotator(0, 45, 0)
)

if actor:
    mesh = unreal.load_asset('/Game/Meshes/SM_Tree_01')
    if mesh:
        actor.static_mesh_component.set_static_mesh(mesh)
    actor.set_actor_label('Tree_01')
    print(f'Spawned: {actor.get_actor_label()}')
```

## Environment Setup for Outdoor Levels

A complete outdoor level typically requires these atmospheric actors:

```python
import unreal

eas = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)

# Discover atmosphere actor classes:
# query_ue_api('SkyAtmosphere')
# query_ue_api('VolumetricCloud')
# query_ue_api('DirectionalLight')

# Spawn sky atmosphere
sky = eas.spawn_actor_from_class(unreal.SkyAtmosphereComponent.static_class(), unreal.Vector(0,0,0))
# Note: discover actual spawnable actor class via query_ue_api first!

# Typical outdoor level actors:
# - SkyAtmosphere — physically-based atmospheric scattering
# - SkyLight — ambient from captured scene (set source_type to Captured Scene)
# - DirectionalLight — sun (enable atmosphere_sun_light, pitch ~-50)
# - ExponentialHeightFog — distance fog with height falloff
# - VolumetricCloud — real-time volumetric clouds (UE5)
```

## Foliage Performance Tips

- Use Instanced Static Mesh (ISM/HISM) for dense foliage — Foliage Types do this automatically
- Set cull distances per foliage type (grass: ~10000, trees: ~50000)
- Enable Nanite for foliage meshes in UE5 for massive polygon budgets
- LOD transitions: at least 3 LODs for non-Nanite meshes (full, medium, billboard)
- Use LandscapeGrassOutput material node for auto-spawned ground cover (most efficient)
- Avoid spawning individual actors for dense vegetation — use Foliage Types instead

## Naming Conventions

| Prefix | Usage |
|--------|-------|
| M_Landscape_ | Landscape materials |
| T_Landscape_ | Landscape textures (with _D, _N, _R suffixes) |
| SM_Tree_, SM_Bush_, SM_Rock_, SM_Grass_ | Environment meshes |
| FT_ | Foliage Type assets |
| LI_ | Landscape Layer Info assets |

## Critical Rules

- Landscape size cannot be changed after creation — plan dimensions ahead
- Always use landscape-specific material nodes (LandscapeLayerBlend, LandscapeGrassOutput)
- Foliage painting requires Foliage Type assets — placing regular StaticMeshActors does not use GPU instancing
- Nanite landscapes are supported in UE5.1+ (enable in landscape properties)
- Discover ALL actor classes and spawn methods via `query_ue_api` — do NOT hardcode class names
- Always use `query_ue_api` and `dir()` to discover API at runtime
