Files
gig-line/Docs/build.md
zwitschi 4d27109668 refactor(tools): group the sixty scripts by what they talk to
One directory with sixty scripts stopped being navigable, and the grouping is also the sandbox boundary:

  Tools/unreal/    commandlets and the two map builders, with ue_common.py and ue_placement.py as the
                   shared imports (they need the engine)
  Tools/blender/   builders, exporters, the FBX preset, the MPFB character pipeline, blender_screenshots
                   (they need the Blender Flatpak)
  Tools/review/    the visual loop: render_screenshots.sh, judge_screenshot.py, hero_compare.py,
                   build_comparison_md.py, e2e_asset_check.py, hero_iterate.sh, visual_loop.py,
                   visual_targets.json
  Tools/gen/       image and texture generation: the ComfyUI driver, deterministic texture writers,
                   reference fetching
  Tools/input/     OS-level input injection
  root             _common.py, check-lfs.sh, addon_compat_probe.py, sibling_asset_audit.py, tests/

Every moved script now resolves the repository root as ../.. and puts the tools root plus its siblings on
sys.path, so 'import ue_common' still works from unreal/. The test suite bootstrap does the same for the five
folders. render_screenshots.sh takes the folder as a third argument to run_cmdlet, and the e2e driver,
hero_iterate.sh and the comparison generator were repointed.

51 README rows now link to their new location, the README gains a Layout section naming each folder's
sandbox, and the architecture notes and Blender README follow the moves.

Verified: 108 tests pass, prettier is clean, judge_screenshot.py and hero_compare.py run from their new
homes, and the e2e run was aborted mid-way (it had been rebuilding an empty DemoMap for 15 minutes) and is
being rerun against the new layout.
2026-11-10 12:40:30 +01:00

10 KiB

Build

Fresh-checkout build, content-pipeline and packaging procedure for the walkable demo. Begun 2026-11-07 from a blank build and extended through the 0.1.x patches; the release checks that exercise it are in release-checks.md.

Constraints

  • Engine installation is external to the repository. Record its path and exact version in toolchain.md.
  • Build output (Binaries/, Intermediate/, Saved/, DerivedDataCache/) is never committed.
  • Large content is LFS-tracked, so a fresh clone needs git lfs install && git lfs pull before any build.

Engine

Pinned install: Unreal Engine 5.8.4 (CL 58902052, promoted build) at /run/media/system/DATA/Engines/UE_5.8.4. The engine bundles no Linux clang; the system clang (23.1.4, Fedora 45) is the game-code toolchain. Game targets must use BuildSettingsVersion.V7 to share the installed editor's build environment.

Fresh-checkout build

git clone <remote> gig-line && cd gig-line
git lfs install && git lfs pull
Tools/check-lfs.sh                 # must pass before any content build

# Compile the project's editor target (first build also proves the toolchain)
cd /run/media/system/DATA/Engines/UE_5.8.4
Engine/Build/BatchFiles/Linux/Build.sh GigLineEditor Linux Development \
  -project="$PWD/../../Documents/06-games/gig-line/Game/GigLine/GigLine.uproject" \
  -WaitMutex
# Expected tail: "Result: Succeeded"; module binary lands in
# Game/GigLine/Binaries/Linux/libUnrealEditor-GigLine.so

# Open in the editor (GUI, Vulkan)
./Engine/Binaries/Linux/UnrealEditor \
  /run/media/system/DATA/Documents/06-games/gig-line/Game/GigLine/GigLine.uproject

Adapt the -project= path to the actual checkout location.

Package a Linux build

cd /run/media/system/DATA/Engines/UE_5.8.4
Engine/Build/BatchFiles/RunUAT.sh BuildCookRun \
  -project="/run/media/system/DATA/Documents/06-games/gig-line/Game/GigLine/GigLine.uproject" \
  -noP4 -platform=Linux -clientconfig=Development \
  -cook -build -stage -pak \
  -archive -archivedirectory="/run/media/system/DATA/Builds/GigLineQA" \
  -unattended
# Expected: "AutomationTool exiting with ExitCode=0 (Success)" and "EXIT=0", ~90 s
# Launch test (plain, no -map): /run/media/system/DATA/Builds/GigLineQA/Linux/GigLine.sh

Launch test must open the DemoMap. A plain ./GigLine.sh should load /Game/Hollywood/DemoMap (set via DefaultEngine.ini GameMapsSettings). If it instead opens /Engine/Maps/Templates/OpenWorld, the game shows an empty non-interactive "stuck" scene — check that GameDefaultMap is in DefaultEngine.ini, not DefaultGame.ini. Confirm the loaded map in the engine log: .../Linux/GigLine/Saved/Logs/GigLine.log (LoadMap: /Game/Hollywood/DemoMap). The archive directory is deliberately outside the repository; never archive into the checkout.

Headless content checks

Both content-acceptance tests run headless and are the template for all asset import verification:

# Static-mesh scale test (Blender export -> Unreal import -> measure)
#   Blender: Tools/blender/scale_test_export.py, Unreal: Tools/unreal/ue_scale_test_import.py
# MPFB character (generate -> rig -> bake -> export -> skeletal import)
#   Blender: Tools/blender/mpfb_character_export.py, Unreal: Tools/unreal/ue_character_import.py

# Unreal side of any test:
./Engine/Binaries/Linux/UnrealEditor-Cmd \
  /run/media/system/DATA/Documents/06-games/gig-line/Game/GigLine/GigLine.uproject \
  -run=pythonscript -script=<repo>/Tools/<script>.py -nullrhi -unattended -nopause -stdout
# Note: 5.8 swallows Python print() in the commandlet; scripts write result files to /tmp.

Content pipeline (current)

The hero block and the demo level are built by a fixed sequence. Each step writes a result file under /tmp/gigline/; read it, because the commandlet's own stdout can be swallowed.

Step Tool Output
OSM blockout Art/blender/blockout/blosm_blockout.py Art/blender/blockout/blockout-*.blend
Terrain Art/blender/terrain/terrain_from_dem.py Art/blender/terrain/terrain-*.blend
Street Art/blender/street/street_mesh.py Art/blender/street/street-*.blend
Site assembly Art/blender/site/site_assembly.py Art/blender/site/site-*.blend
Site export Art/blender/site/export_site_fbx.py seven FBXs, one per mesh (terrain, road, both sidewalks, both kerbs, crossing)
Hero façade Tools/blender/hero_facade_build.py Art/blender/hero/hero-facade-*.blend + JSON part report
Block frontage Tools/blender/export_block_frontage.py six frontage FBXs from the blockout
North buildings Tools/blender/export_north_buildings.py TCL and Tussaud's FBXs from the blockout
Site import Tools/unreal/ue_import_site.py /Game/Hollywood/Site/*
Frontage import Tools/unreal/ue_import_block_frontage.py /Game/Hollywood/Frontage/*
North import Tools/unreal/ue_import_north_buildings.py /Game/Hollywood/North/*
Hero import Tools/unreal/ue_hero_facade_import.py /Game/Hollywood/ElCapitan/SM_HeroFacade
Demo map Tools/unreal/ue_build_demo_map.py /Game/Hollywood/DemoMap
Façade kit assembly Tools/unreal/ue_apply_facade_kit.py kit actors in DemoMap
Theatre trim Tools/unreal/ue_stage_theatre_trim.py marquee + blade sign actors
Scene audit Tools/unreal/ue_audit_scene.py materials, placement spans, labels (gate)
Review shots Tools/unreal/ue_build_review_map.py + Tools/review/render_screenshots.sh Screenshots/

Order matters. ue_build_demo_map.py rebuilds the site materials, so any actor staged by another tool must be (re)staged after it, or it keeps a reference to a material that no longer exists — that is how 604 actors once ended up untethered from their materials while the map only looked slightly flat. Tools/review/render_screenshots.sh runs the full chain in the right order and gates on ue_audit_scene.py.

Every Blender exporter calls Tools/blender/fbx_export_preset.py (ue_metric_v1 / ue_character_v1) instead of repeating the FBX argument block — one place for the scale conversion, the axis mapping and mesh_smooth_type="FACE" (without which every Unreal import warns about missing smoothing groups and round geometry arrives faceted).

Two rules that the pipeline enforces rather than documents:

  • One conversion. Metres to Unreal centimetres happens only in Geo/tools/osm_common.py::to_unreal_local. Sidecars (<asset>.geo.json) record the result and Geo/tools/asset_metadata.py audit re-derives it.
  • Collision is decided at import, not later: terrain, road and sidewalks are complex-as-simple; kerbs and props stay simple. A simple hull over a kerbed street ramps straight across the kerb.

Known engine quirks (2026-11-07)

  • One handled PSOPrecache ensure fires at packaged-build exit only; not blocking.
  • Interchange 5.8 does not create material/texture assets for skeletal FBX imports — Tools/unreal/ue_character_import.py finishes that chain explicitly.
  • Android Turnkey status errors and datarouter DNS warnings in logs are benign.

Packaged Linux build

Current QA archive: /run/media/system/DATA/Builds/GigLineQA. A plain ./GigLine.sh opens /Game/Hollywood/DemoMap; controls are in controls.md and shown in-game bottom-right. The packaged log is written relative to the wrapper's working directory, i.e. <archive>/Linux/GigLine/Saved/Logs/GigLine.log — not into the repository.

Behaviour check without a human at the keyboard:

cd /run/media/system/DATA/Builds/GigLineQA/Linux
./GigLine.sh -windowed -ResX=1280 -ResY=720 -nosplash -qaselftest
# then read <archive>/Linux/GigLine/Saved/Logs/GigLine.log for "GigLine QA:" lines

-qaselftest walks for 2 s, resets, pauses/unpauses, sweeps a 16-row seam transect, fires a ground-referenced 21 m wall probe along the street, fires 40 m façade-occlusion traces sideways from the road centre wherever a building is staged, and writes a frame-time series to Saved/Profiling/. Every phase logs ... PASS/FAIL and the run ends with RESULT.

Traps that cost real time in 0.1.x

Symptom Cause Fix
A builder reports success but the asset is unchanged .blend/.umap are LFS-tracked and check out read-only; Blender and Unreal fail the save quietly chmod u+w <file> first — ue_build_*/ue_hero_facade_import and every Blender builder need it
File format is not supported when reopening a .blend hero_facade_build.py --out writes the FBX; putting the .blend path there overwrites it with FBX bytes --out /tmp/…fbx --save-blend Art/…blend --report Art/…json
Add-on not loaded: "bl_ext.blender_org.mpfb" mpfb_character_export.py needs the MPFB add-on visible to Blender export BLENDER_USER_SCRIPTS (see the invocation in ../Art/blender/README.md)
The packaged run exits after a couple of seconds during QA GigLineShotActor runs its screenshot list and quits (bQuitWhenDone) it skips its rounds when -qaselftest is on the command line — keep that guard
Headless -nullrhi traces report no hits for everything the null RHI has no physics scene verify traces in the packaged run (-qaselftest), never in a commandlet
Import logs "No smoothing group information" for every mesh mesh_smooth_type="OFF" in the FBX export use Tools/blender/fbx_export_preset.py (FACE)

PIE input test (compare editor vs packaged input intake)

Use to isolate whether the "no controls respond" bug is the packaged standalone's input intake or the Enhanced Input wiring generally.

  • Tools/input/run_pie.sh opens the editor on /Game/Hollywood/DemoMap. Press the green Play (PIE) button and walk / mouse-look.
  • Interpretation:
    • PIE works -> the Enhanced Input wiring is fine and the standalone packaged build's SDL input intake is the environment-dependent bug.
    • PIE fails too -> the Enhanced Input setup itself is broken; rebuild the input config / character from scratch.
  • The DemoMap is PIE-ready as committed: GlobalDefaultGameMode + GameDefaultMap=/Game/Hollywood/DemoMap, and the map stages a PlayerStart at (-13336,-1150,600) on the cut sidewalk with the walkable AGigLineCharacter.