The facade work started as a patch (0.1.4) and the 0.1.6 retail pass showed what that costs: a patch has no release gate of its own, so a first pass that does not yet read as the building can sit inside a done patch series. Realism is also the leading quality goal, which a patch cannot carry. New 0.9.0 - Realistic buildings, before the feature freeze: facade system with more than one order, a profile library, storefront types, signage as decals, per-building photo-matched models, a measured material library with a texel-density rule, and LODs plus collision proxies. Former 0.9.0 freeze/beta/audit becomes 0.10.0 with patches 0.10.1-0.10.3; 1.0.0 moves to weeks 39-44. Recorded as ADR-8 (append-only) with a measurable gate BR-1..BR-5 in the quality requirements, mirrored into the combined architecture.md. The 0.1.6 item now says which of its gaps move to 0.9.0 and why more geometry cannot close them.
4.7 KiB
Architecture Decisions
ADR-1: Dynamic-only lighting for the demo
-
Context: The demo is a fixed daytime scene. Stationary lights demand a lighting build and produce "LIGHTING NEEDS TO BE REBUILT" warnings.
-
Decision:
r.AllowStaticLighting=False, all lightsMOVABLE. -
Consequences: No bake step, no warning, simpler iteration. Slightly higher GPU cost per frame (no baked shadows) — acceptable for the demo scope.
ADR-2: Materials authored in Unreal, not imported from Blender
-
Context: FBX→Unreal import maps all Blender diffuse colours to white. Flat colour materials do not survive the pipeline.
-
Decision: Build all materials (asphalt, concrete, stucco, façade stone, road paint) directly in Unreal with procedural nodes.
-
Consequences: Materials cannot be previewed in Blender look-dev. Must round-trip to Unreal for visual check. But: full PBR control, world-space noise, no import surprise.
ADR-3: Complex-as-simple collision on terrain via C++ helper
-
Context: UE 5.8 removed
bUseComplexAsSimpleCollision. Python cannot setCollisionTraceFlagonStaticMesh/BodySetupat all. The Interchange default is bbox-only simple collision, which placed an invisible lid 8 m above the street. -
Decision:
UGigLineAssetTools::SetComplexCollisionAsSimple(C++) forcesCTF_UseComplexAsSimpleand saves the asset. -
Consequences: One C++ helper class. The flag must be saved on the asset (not just the map). No Python workaround exists.
ADR-4: Reset captures StartLocation on first grounded frame
-
Context:
BeginPlaycapturesStartLocationat the PlayerStart, which is above grade (Z 600) so the capsule does not spawn embedded. Every reset dropped the player 1.4 m back into the air. -
Decision: Repeating 0.25 s timer captures
StartLocationonce the character reportsIsMovingOnGround(), then clears itself. -
Consequences: Reset is correct. A one-shot timer at 2 s is wrong (fires after walking begins, records mid-stride position).
ADR-5: Core ticker for QA self-test, not world timer
-
Context: Pausing the game stops world timers. A timer-driven test freezes exactly when it proves pause works.
-
Decision:
FTSTicker::GetCoreTicker()drives the-qaselftestharness. -
Consequences: Test runs even while paused. Must be enabled explicitly (
-qaselftest), never in normal play.
ADR-6: South = +Y, north = −Y in Unreal
-
Context: The street mesh established this convention:
Sidewalk_Northhas Y range −1912..−1053,Sidewalk_Southhas +718..+1447. -
Decision: Adopt it project-wide. Never place a building by assumption; always verify against a street mesh or neighbour.
-
Consequences: Matches Blender’s north→+Y after the Y-flip in the
ue_metric_v1export preset.
ADR-7: Headless Blender via bpy scripts, not MCP
-
Context: Blender skill packs assume an MCP server; the project does not run one.
-
Decision: All Blender work driven by Python scripts in
Tools/. Skill packs used as method guides (checklists, naming, proportions) only. -
Consequences: Scripts are version-controlled and repeatable. No live Blender session needed for CI.
ADR-8: Realistic building façades are a minor milestone, not a patch
-
Context:
0.1.4carried the first façade pass as a patch, while the release policy says patches contain fixes and refinements and never new scope. The0.1.6retail pass then showed what that costs: a façade built in a patch has no release gate of its own, so a first pass that does not yet read as the building (Screenshots/README.md, 0.1.6 gaps) can sit inside a "done" patch series. Realism is also the project's leading quality goal, which a patch cannot carry. -
Decision: Building realism gets its own minor milestone,
0.9.0 — Realistic buildings, placed before the feature freeze. Its scope is the façade system (orders, profile library, storefront types, signage as decals), per-building photo-matched models, a measured material library with a texel-density rule, and LODs plus collision proxies. Patches keep fixes and refinements only. Renumbered by this decision: the former0.9.0freeze/beta/audit milestone becomes0.10.0, its patches0.10.1–0.10.3, and1.0.0moves to weeks 39–44. -
Consequences: Façade work now has a release gate — every building measured against its own photograph, every building with a collision proxy and a LOD, no placeholder materials — so "modelled" means verified, not merely present. The price is that realism work stops being sliced into patches, and the schedule shifts by one minor cycle. Blockouts without a photograph stay blockouts and are listed as such, rather than being dressed up to fill the milestone.