# Lighting & Environment Setup

Proper lighting is the difference between a map that looks professional and one
that looks like a grey box. Tactical Shooter uses Godot's **LightmapGI** system
for baked global illumination: light bounces are pre-calculated and stored in
lightmaps, giving you realistic lighting at zero runtime cost.

## Lighting Architecture

```
DirectionalLight3D (sun)   — main directional light, shadows
    └─ OmniLight3D (fill)  — ambient fill lights, no shadow
        └─ LightmapGI      — bakes all static lighting into textures
            └─ baked lightmaps (zero runtime cost)
```

## 1. WorldEnvironment

Every map needs a `WorldEnvironment` node. This controls:

- **Sky** — Background appearance (skybox or solid colour)
- **Ambient light** — Base light level for unlit surfaces
- **Tonemapping** — Colour grading and exposure

**Setup:**
1. The template already includes a `WorldEnvironment` node
2. If creating from scratch, add one via the scene tree → + button → `WorldEnvironment`
3. In the Inspector, assign an `Environment` resource (New Environment)
4. Configure:
   - **Background:** Sky (for outdoor) or Clear Color (for underground maps)
   - **Ambient Light:** Sky (mimics sky colour) or Color (custom ambient)
   - **Tonemap:** Reinhard or Filmic (prevents blown-out highlights)

> The validator warns if WorldEnvironment is missing — add it early.

## 2. DirectionalLight3D (Sun)

The main sun light. One per map.

**Recommended settings:**
| Property | Value | Reason |
|----------|-------|--------|
| `light_bake_mode` | Static (2) | Bakes into lightmap |
| `shadow_enabled` | true | Required for competitive visibility |
| `shadow_bias` | 0.1 | Prevents shadow acne on CSG |
| `angular_distance` | 0.01 | Sharp sunlight (competitive visual clarity) |
| `range` | 50–100 | Large enough to cover the map |

**Positioning:**
- Rotate the sun to create interesting shadows through windows/doorways
- Avoid straight-down lighting (makes everything look flat)
- A 45° angle works well for most maps

## 3. Fill Lights (OmniLight3D)

Fill lights illuminate areas the sun doesn't reach (interiors, tunnels).

**Rules:**
- **Maximum 4 dynamic (non-baked) lights** — enforced by the validator
- Fill lights should use `light_bake_mode = Static (2)` so they bake into
  the lightmap and don't count toward the dynamic budget
- Use low intensity (0.3–0.5) for ambient fill

**Performance tip:** Every omni/spot light with `shadow_enabled = true` is
expensive. The validator warns on dynamic lights with shadows. Baked lights
with shadows disabled are free — use them.

## 4. LightmapGI — Baked Global Illumination

LightmapGI is the backbone of Tactical Shooter's lighting. It pre-computes
light bouncing and stores the result as textures (lightmaps).

### Configuration

| Property | Recommended | Notes |
|----------|-------------|-------|
| `quality` | 2 (High) | Higher = better GI, longer bake |
| `bounces` | 3 | Minimum 2 for realistic indirect lighting |
| `texel_scale` | 1.0–2.0 | Lower = higher res, longer bake |
| `max_texture_size` | 1024 | Controls lightmap atlas size |
| `use_denoiser` | true | Removes noise from baked result |
| `environment_mode` | 0 (Baked Environment) | Uses WorldEnvironment for sky lighting |

### Baking

To bake: select the `LightmapGI` node → click **Bake Lightmap** in the top
toolbar (or `Ctrl+Shift+C`).

**Baking checklist:**
- [ ] All geometry is finalised (moving geometry after baking invalidates it)
- [ ] All lights have correct bake mode (Static for baked, Dynamic for real-time)
- [ ] CSG has `use_collision = true` (non-collidable CSG may not bake properly)
- [ ] WorldEnvironment is configured with a sky or ambient colour
- [ ] Save the scene before baking

**Baking time:** Varies by map complexity. A small map (10×14 units) bakes in
30–60 seconds. A full competitive map (20×20) may take 5–10 minutes.

### After Baking

- The LightmapGI node's `light_data` property will show a `LightmapGIData` resource
- The validator checks for `light_data != null` to confirm the map is baked
- Baked lightmaps are stored in the `.tscn` file as embedded resources
- You can rebake at any time — it overwrites the previous lightmaps

**Warning:** Opening an unbaked map on a server will default to unlit rendering.
Always bake before packaging.

## 5. ReflectionProbe

ReflectionProbes add specular reflections to shiny surfaces (metal, glass, water).

- Place one `ReflectionProbe` near the `cubemap_origin` marker
- Set `max_distance` to cover the visible area (10–30 units)
- Enable `box_projection` for interior spaces (corrects reflection distortion)
- Set `intensity` to 0.5–0.8 (realistic reflection strength)

**Tip:** You need reflection probes even if your map has no mirrors — they make
metal and plastic materials look correct.

## 6. Lighting Performance Budget

| Metric | Limit | Enforced |
|--------|-------|----------|
| Dynamic (non-baked) lights | ≤ 4 | Validator (error) |
| Total lights (baked + dynamic) | Unlimited | — |
| LightmapGI bounces | ≥ 2 | Validator (warning) |
| ReflectionProbes | ≥ 1 | Validator (warning) |
| Lightmap max texture | ≤ 2048 | Project settings |

If you exceed the dynamic light budget, set more lights to
`light_bake_mode = Static (2)` — baked lights cost nothing at runtime.

## 7. Quick Start Checklist

- [ ] WorldEnvironment with Environment resource assigned
- [ ] DirectionalLight3D (sun) with shadows
- [ ] 1–3 fill OmniLight3D with Static bake mode
- [ ] LightmapGI with quality=2, bounces=3, texel_scale=1.0
- [ ] ReflectionProbe at cubemap origin
- [ ] All geometry CSG has `use_collision = true`
- [ ] Save scene → Bake LightmapGI (Ctrl+Shift+C)
- [ ] Verify `light_data` is populated (not null)

## Troubleshooting

| Problem | Likely Cause | Fix |
|---------|-------------|-----|
| Light leaks at corners | CSG gap < 0.01 units | Overlap CSG edges by 0.1 |
| Baked lighting too dark | bounces too low | Set bounces ≥ 2 |
| Lightmap seams visible | texel_scale too high | Lower texel_scale to 1.0 |
| Shadow acne (striped shadows) | shadow_bias too low | Increase shadow_bias to 0.1 |
| ReflectionProbe visible as sphere | probe_mode wrong | Set to Box or disable visibility |
| Bake fails with error | Scene not saved | Save `.tscn` before baking |

## Next Steps

- [Map Validation](04-validation.md) — run all checks on your scene
- [Packaging & Shipping](05-packaging-and-shipping.md) — publish your map
