docs: community mapmaking documentation (7-file SDK guide)

- docs/mapmaking/00-index.md — SDK overview with workflow diagram
- docs/mapmaking/01-getting-started.md — template setup & first map
- docs/mapmaking/02-building-geometry.md — CSG guide & prefab reference
- docs/mapmaking/03-lighting-and-env.md — LightmapGI baking guide
- docs/mapmaking/04-validation.md — validator CLI & CI/CD usage
- docs/mapmaking/05-packaging-and-shipping.md — .pck pipeline
- docs/mapmaking/06-faq-and-troubleshooting.md — 30+ common issues
- README.md — add mapmaking SDK link to features list
This commit is contained in:
2026-07-01 18:46:10 -04:00
parent 159c554a86
commit 34507f9043
8 changed files with 1382 additions and 1 deletions
+235
View File
@@ -0,0 +1,235 @@
# Map Validation
The validator catches structural problems, performance budget violations, and
lighting issues before your map ships. Two validation modes are available:
1. **Editor validation** — runs automatically when you open a map scene
2. **Headless CLI validation** — runs from the terminal, suitable for CI/CD
## Editor Validation (template_map.gd)
Every map created from the template includes `template_map.gd`, a `@tool`
script that runs automatically when the scene opens in the Godot editor.
**How to use it:**
1. Open your map's `.tscn` file in the Godot editor
2. Look at the **Output** panel (bottom dock)
3. The script prints a validation report like:
```
=== Map Template: Validate Scene ===
+ CT Spawn points (5 found)
+ T Spawn points (5 found)
+ Buy zones (2 found)
+ Bomb sites (2 found)
+ Cubemap capture origins (1 found)
+ Map boundary walls (8 found)
+ LightmapGI configured ✓
+ LightmapGI: NOT YET BAKED
+ DirectionalLight3D (sun) present
```
**Checks performed:**
- Required gameplay groups exist (`ct_spawn`, `t_spawn`, `buy_zone`, etc.)
- CSG floor and wall geometry present
- LightmapGI configured and baked
- ReflectionProbe and WorldEnvironment present
- DirectionalLight3D (sun) present
- Playable area extents estimated
## Headless CLI Validator (validate_map.gd)
The headless validator is a 4-module CLI tool at `client/tools/validate_map.gd`.
Use it for automated checking before packaging or in CI/CD pipelines.
### Usage
```bash
# From the tactical-shooter repository root
godot --path client --script tools/validate_map.gd -- res://maps/mymap.tscn
```
> The `--` separates Godot engine arguments from user arguments.
> The scene path must be a `res://`-prefixed Godot resource path.
### Exit Codes
| Code | Meaning | Description |
|------|---------|-------------|
| 0 | **PASS** | All checks passed, no warnings |
| 1 | **WARNINGS** | Passed with non-blocking suggestions |
| 2 | **ERRORS** | Failed — must-fix items present |
### CI/CD Integration
```bash
#!/bin/bash
# Gate map merges on validation
godot --path client --script tools/validate_map.gd -- res://maps/mymap.tscn
exit_code=$?
if [ $exit_code -eq 2 ]; then
echo "❌ Map validation FAILED — fix errors before submitting"
exit 1
elif [ $exit_code -eq 1 ]; then
echo "⚠️ Map passed with warnings — review suggestions"
fi
echo "✅ Map validated successfully"
exit 0
```
### Example Output
```
═══════════════════════════════════════════
MAP VALIDATOR
Scene: res://maps/de_dust2.tscn
═══════════════════════════════════════════
── [validate_scene] ────────────────────────────
✓ Root node: World (Node3D)
✓ WorldEnvironment (WorldEnvironment)
✓ LightmapGI (LightmapGI)
✓ Group "ct_spawn" → CT_SpawnPos (5 node(s))
✓ Group "t_spawn" → T_SpawnPos (5 node(s))
✓ Group "buy_zone" → BuyZone_A (2 node(s))
✓ Group "bomb_site" → BombsiteA (2 node(s))
✓ Group "cubemap_origin" → CubemapOrigin (1 node(s))
✓ pass
── [validate_polycount] ────────────────────────
Meshes scanned: 24
Total triangles: 12,842
Budget: 50,000
✓ Within budget (25.7% of 50000)
Top meshes by triangle count:
4200 Building_01 (MeshInstance3D)
2100 Wall_Detail (MeshInstance3D)
1042 Floor_Tiles (MeshInstance3D)
✓ pass
── [validate_textures] ─────────────────────────
Textures inspected: 18
✓ All textures within 1024×1024 limit
✓ pass
── [validate_lights] ───────────────────────────
Light breakdown:
DirectionalLight3D: 1
OmniLight3D: 2
SpotLight3D: 0
Dynamic (non-baked): 1
Baked: 2
✓ Dynamic light count: 1 (limit: 4)
✓ LightmapGI baked (light_data present)
Quality: 2 (0=Low, 1=Med, 2=High, 3=Ultra)
Bounces: 3
Texel Scale: 1.0
Max Texture Size: 1024
✓ WorldEnvironment configured
ReflectionProbes: 1
✓ pass
═══════════════════════════════════════════
SUMMARY
═══════════════════════════════════════════
[PASS] validate_scene
[PASS] validate_polycount
[PASS] validate_textures
[PASS] validate_lights
Errors: 0
Warnings: 0
Result: PASS
```
## Validator Modules
### 1. Scene Structure (`validate_scene.gd`)
**What it checks:**
- Scene root is a `Node3D` type
- `WorldEnvironment` node present and named correctly
- `LightmapGI` node present and named correctly
- All 5 required groups assigned (`ct_spawn`, `t_spawn`, `buy_zone`, `bomb_site`, `cubemap_origin`)
- Node names follow PascalCase convention (no spaces)
- No duplicate required groups on different nodes
**Typical errors:**
```
✖ Missing required group: "ct_spawn"
✖ Missing WorldEnvironment — map needs a WorldEnvironment node
✖ Node name contains spaces: "My Room Section"
```
### 2. Polygon Count (`validate_polycount.gd`)
**What it checks:**
- Total triangle count across all `MeshInstance3D` nodes ≤ **50,000**
- Per-mesh triangle breakdown printed (helps identify heavy assets)
- Warns on individual meshes > **5,000** triangles
- Warns if no `MeshInstance3D` nodes found at all
**Typical errors:**
```
✖ Map exceeds 50,000 triangle budget: 68,420 total (36.8% over budget)
```
### 3. Texture Sizes (`validate_textures.gd`)
**What it checks:**
- All material textures ≤ **1024×1024**
- Checks `StandardMaterial3D`, `ORMMaterial3D`, and `ShaderMaterial` texture params
- Warns on textures > **512×512** (suggested for secondary surfaces)
- LightmapGI textures checked separately (baked textures can be larger)
**Typical errors:**
```
✖ Texture exceeds 1024×1024: floor_tiles.png (albedo) — 2048×
```
### 4. Lights & Lightmap (`validate_lights.gd`)
**What it checks:**
- Dynamic (non-baked) OmniLight3D + SpotLight3D ≤ **4**
- DirectionalLight3D counts toward dynamic budget if not baked
- LightmapGI present and baked (`light_data != null`)
- Lightmap quality, bounces, texel scale, max texture size
- WorldEnvironment assigned with Environment resource
- ReflectionProbe presence and `max_distance` coverage
- Warns on dynamic lights with shadow enabled (performance cost)
**Typical errors:**
```
✖ Too many dynamic lights: 6 (max 4)
✖ No LightmapGI node found
✖ No WorldEnvironment node found
```
## Passing the Validator
The goal is exit code 0: **PASS**, no errors, no warnings.
| Code | Action |
|------|--------|
| 0 (PASS) | Your map is ready to package |
| 1 (WARNINGS) | Fix suggestions before shipping (non-blocking) |
| 2 (ERRORS) | Fix all errors — map must pass before packaging |
> **CI gate rule:** Treat exit code 2 as a hard block. Exit code 1 is advisory
> but should be reviewed. Only exit code 0 means "ship it."
## Adding a Custom Validator Module
Advanced users can extend the validator:
1. Create `client/tools/validate_map/validate_<name>.gd` extending `RefCounted`
2. Implement `func validate(scene_root: Node, scene_path: String) -> Dictionary`
3. Return `{"pass": bool, "errors": [String], "warnings": [String]}`
4. Add a `_run_module("<name>", instance, scene_path)` call in
`validate_map.gd:_ready()`
## Next Steps
- [Packaging & Shipping](05-packaging-and-shipping.md) — ship your validated map