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

+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()