qa(0.1.4): verdict document with budgets, evidence and defect severities

Per the qa-review skill: explicit verdict, measured budgets, the screenshots that
carry the claim, defects by severity and what remains unverified.

Verdict SHIP WITH NOTES - geometry/placement/budgets verified by measurement, the
visual half explicitly pending human review (the vision pre-check is unreliable on
this content and was not used as evidence).

Also fixes the last QA finding: kit pieces kept the grey M_Kit_* material the FBX
import creates, so glazing was not glass and shopfronts were not stone. Assembly now
assigns M_FacadeGlass to glazing, M_FacadeStone to surrounds, M_FacadeStoneShadow to
doors and M_FacadeBanner to fascias (6/6 materials resolved), and the importer
deletes the unused M_Kit_* materials (Kit folder now exactly 16 SM_ meshes).

Packaged QA re-verified after the change: walk, transect 0 holes, wall probe 0/35,
facade probe 0/9, RESULT PASS.
This commit is contained in:
zwitschi committed 2026-11-08 19:44:17 +01:00
1 parent 1b069188a2
commit ad5bb25009
43 files changed
+176 -89

No files matched your search

+81
View File
@@ -0,0 +1,81 @@
# QA review — `0.1.4` façade work
Checklist gate for the façade module kit, the replacement materials and the corrected blockout frame. Written per the `qa-review` skill: budgets, the screenshots that carry the claim, defects by severity, and an explicit verdict.
Date: 2026-11-09 · Branch: `facades-0.1.4` · Build under test: packaged `GigLineQA`, Development, UE 5.8.4, Vulkan SM5.
## Result: SHIP WITH NOTES
The geometry, placement and budgets are verified by measurement. The **visual** half of this review is **not** complete: nobody has looked at the rendered set since the kit went on, so the reference-match verdict is pending a human. The notes at the end are what that review has to confirm.
## Budgets
| Asset group | Count | Triangles | Texture | Notes |
| --- | --: | --: | --- | --- |
| Hero façade | 148 parts, 1 mesh | 3 044 | none (graph materials) | was 1 892 before the Ionic detail |
| Façade kit | 16 meshes | 12 tris each = 192 | none | boxes; complex-as-simple collision |
| Kit instances in `DemoMap` | 625 actors | ~7 500 | shared materials | 8 buildings |
| Frontage buildings | 6 meshes | blockout volumes | 8 × 512² maps | `M_FacadeStone`, `M_Stucco`, … |
| North buildings | 2 meshes | blockout volumes | shared | TCL, Tussaud's |
| Site meshes | 7 | 55 k tris (terrain-dominated) | shared | terrain/road/sidewalks/kerbs/crossing |
Texture set: `T_StoneAshlar`, `T_Stucco`, `T_Asphalt`, `T_Concrete`, each `_BC` + `_R` at 512², deterministic and regenerable.
## Evidence
Behaviour, packaged build (`-qaselftest`):
```
walk moved 761 cm in 2 s -> PASS (Z 456 -> 456)
transect 1456 samples, 0 holes -> PASS
wall probe 0/35 blocking hits inside 21 m -> PASS
facade probe 0/9 directions without a blocking wall -> PASS
RESULT walk=1 reset=1 pause=1 unpause=1 -> PASS
```
Geometry and placement, probed from the live map:
- Frontage spans match the verified block: 6922 −24276..−17700, 6902 −17749..−14591, **hero −14644..−12028 between them**, theatre −12082..−8210, 6822 −7854..−7252, 6806 −6608..−3307, 6800 −3320..−1437. North: TCL −22889..−18442, Tussaud's −27071..−22799.
- Kit placed from world bounds (`get_actor_bounds`), e.g. theatre pieces at −12082..−8694, inside the theatre's own span.
- Kit module **4.96 m**, storey **3.10 m**, door **2.45 m**, window **1.60 × 1.85 m**.
- Kit topology: 16 pieces, **all manifold**, no loose vertices, 12 tris each.
- Asset hygiene: `/Game/Hollywood/Kit` holds exactly 16 `SM_` meshes and no leftover auto-imported materials.
Screenshots: the committed set in [`Screenshots/`](../Screenshots/README.md), all 14 re-rendered for this state — hero shots 8–10 and demo views 11–14 carry the change.
## Defects
### Blockers
None outstanding. Two were found and fixed during this cycle:
- **Wrong frame (fixed).** The `0.1.2` blockout was regenerated without `--origin`, shifting the whole frontage ≈119 m in X and 2.4 m in Y and dropping the hero out of the 6902/6838 gap. Regenerated with `--origin 34.101545,-118.338718` (affine residual 0.0 cm); spans re-verified against [`Geo/verification/2026-11-06-el-capitan-block.md`](../Geo/verification/2026-11-06-el-capitan-block.md).
- **Kit placed 12 km away (fixed).** `ue_apply_facade_kit` used the static-mesh bounding box, which is building-line-local. Now uses `get_actor_bounds()` (world space).
### Majors
- **Window bays were inside the solid wall (fixed).** The first piece modelled the whole bay at +Y, so every window sat inside the blockout volume and was invisible. The surround now projects 3 cm with glazing 6 cm behind its face.
- **Kit had no materials (fixed).** Pieces kept the grey `M_Kit_*` material the FBX import creates, so the glazing was not glass and the shopfronts were not stone. Assembly now assigns `M_FacadeGlass` to glazing, `M_FacadeStone` to surrounds, `M_FacadeStoneShadow` to doors and `M_FacadeBanner` to fascias.
### Minors
- Kit pieces are boxes: no reveals, sills or mouldings beyond the kit's own bands. Acceptable at this patch's fidelity; the kit is the vocabulary, not the detail.
- `M_Kit_*` materials are deleted at import rather than reused, so the kit's Blender materials carry no shading intent — the Unreal set is authoritative. Documented, not silent.
- Actor labels for kit pieces are unique per building but long (`Kit_elcapitantheatre_window_3_2`).
### Notes (polish, not required for `v0.1.4`)
- Hero fluting, and the motto band/medallions as geometry, are still pending.
- The El Capitan Theatre has no marquee or blade sign yet — the features that make it read as itself.
- Cornice dentil spacing, abacus and plinth dimensions remain recorded assumptions.
## Not verified here
- **Reference match.** Needs a human comparing `Screenshots/demo/*` against `Art/reference/hollywood/`. The vision pre-check is demonstrably unreliable on this content (it rejected shots the owner has since approved), so it was not used as evidence.
- **Silhouette and shading at street level** — the same review covers it.
## Next actions
1. Owner review of the 14 demo/review shots; record approval in `Screenshots/README.md`.
2. Then the remaining `0.1.4` modelling: theatre marquee + blade sign, hero fluting/motto band, blockout cleaning pass.
3. `CHANGELOG.md` entry and `v0.1.4` tag once (1) is signed off.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+26 -2
View File
@@ -31,6 +31,18 @@ import ue_common # noqa: E402
KIT = "/Game/Hollywood/Kit"
RESULT = "/tmp/gigline/kit-apply.txt"
# Material per kit piece. Without this the pieces keep the grey material the FBX
# import creates (M_Kit_*), so the glazing is not glass and the shopfronts are not
# stone - the kit reads as a plastic scaffold over the buildings.
KIT_MATERIALS = {
"Kit_WindowGlass": "M_FacadeGlass",
"Kit_ShopGlass": "M_FacadeGlass",
"Kit_Door": "M_FacadeStoneShadow",
"Kit_WindowBay": "M_FacadeStone",
"Kit_ShopFascia": "M_FacadeBanner",
}
KIT_MATERIAL_FALLBACK = "M_FacadeStone"
BAY_CM = 496.0 # the kit module
STOREY_CM = 310.0
SHOP_FLOOR_CM = 310.0 # ground floor is one storey tall
@@ -119,7 +131,10 @@ def apply_to_actor(actor, kit, materials=None, prefix=None):
storeys = max(1, int((height - SHOP_FLOOR_CM) // STOREY_CM))
def kit_material(name):
return (materials or {}).get(name)
if materials is None:
return None
want = KIT_MATERIALS.get(name, KIT_MATERIAL_FALLBACK)
return materials.get(want)
placed = 0
for bay in range(bays):
@@ -177,6 +192,15 @@ def main() -> None:
return
log(f"kit meshes: {len(kit)}")
# Site materials built by Tools/ue_build_demo_map.py.
materials = {}
for name in ("M_FacadeStone", "M_FacadeStoneShadow", "M_FacadeGlass",
"M_FacadeBanner", "M_Stucco", "M_Concrete"):
asset = unreal.EditorAssetLibrary.load_asset(f"/Game/Hollywood/Site/{name}")
if asset:
materials[name] = asset
log(f"kit materials resolved: {len(materials)}/6")
level = "/Game/Hollywood/DemoMap"
if not unreal.EditorLevelLibrary.load_level(level):
log(f"could not load {level}")
@@ -198,7 +222,7 @@ def main() -> None:
log(f"frontage actors: {len(targets)}")
total = 0
for actor in targets:
total += apply_to_actor(actor, kit)
total += apply_to_actor(actor, kit, materials)
log(f"placed {total} kit pieces")
saved = unreal.EditorLoadingAndSavingUtils.save_map(
+6
View File
@@ -83,6 +83,12 @@ def main() -> None:
if mesh is None:
log(f"{asset_name}: IMPORT FAILED")
continue
# The FBX import also creates an M_Kit_* material per piece. The demo
# assigns real site materials at assembly time, so those are clutter.
for slot in mesh.get_editor_property("static_materials"):
mat = slot.get_editor_property("material_interface")
if mat and mat.get_name().startswith("M_Kit_"):
unreal.EditorAssetLibrary.delete_asset(mat.get_path_name().split(".")[0])
unreal.GigLineAssetTools.set_complex_collision_as_simple(mesh, True)
unreal.EditorAssetLibrary.save_loaded_asset(mesh)
box = mesh.get_bounding_box()