fix(hero): the albedo renders - two blockers, both at the map level

The generated ashlar albedo never showed for two reasons that had nothing to do with the material graph,
which was verified correct the whole time (TextureSampleParameter2D -> Multiply -> Base Color, colour sampler,
texture set, 6x UV tiling):

1. **The map overrode every hero slot.** The generic per-slot material pass in ue_build_demo_map.py assigned
   the facade KIT's flat stone to every actor whose slot names looked like stone, banner, glass - including
   Hero_Facade. Whatever the import authored was replaced at map build time. The hero is now excluded and gets
   its own materials.
2. **Mesh slot assignment does not persist.** Assigning to mesh.get_editor_property('static_materials') returns a
   copy; after a reload the mesh still pointed at the original imported materials (measured twice). Component
   level overrides do persist in the saved map, so apply_hero_materials() sets them in both builders.

The elevation now renders as coursed, grainy warm limestone with banners, window bays and recessed portals.

Rows 4-6 re-rendered and pending review; the comparison plate is regenerated. Still missing against the
reference: carved inscription, medallion relief, banner lettering, crimson banner tone. The two-model judge
returns no/no (0.90/0.95) - it wants the text and detail that are still absent, which is at least the right
complaint this time.
This commit is contained in:
zwitschi committed 2026-11-10 13:57:57 +01:00
1 parent 0189ff00fe
commit ff9eb0a4ad
118 files changed
+242 -189

No files matched your search

+36
View File
@@ -45,6 +45,34 @@ def log(msg):
lines.append(str(msg))
unreal.log(str(msg))
def apply_hero_materials(actor):
"""Point the hero's component slots at the authored *_Ref materials, if they exist.
The mesh's own slot list cannot be relied on: `get_editor_property("static_materials")` returns a copy, so
assigning to it does not persist across a reload. A component-level override does persist in the saved map,
and it is what the captures actually render.
"""
if actor is None or actor.get_actor_label() != "Hero_Facade":
return 0
comp = actor.get_component_by_class(unreal.StaticMeshComponent)
if not comp or not comp.static_mesh:
return 0
slots = comp.static_mesh.get_editor_property("static_materials")
applied = 0
for index, slot in enumerate(slots):
current = slot.get_editor_property("material_interface")
if not current:
continue
ref = unreal.EditorAssetLibrary.load_asset(
f"/Game/Hollywood/ElCapitan/{current.get_name()}_Ref")
if ref:
comp.set_material(index, ref)
applied += 1
if applied:
log(f"hero materials: {applied}/{len(slots)} slots use the authored *_Ref materials")
return applied
def spawn(cls, loc, label, rot=None):
actor = unreal.EditorLevelLibrary.spawn_actor_from_class(cls, loc)
@@ -618,6 +646,14 @@ def build():
assigned = 0
for actor in unreal.EditorLevelLibrary.get_all_level_actors():
# The hero carries its own materials, authored per slot by
# Tools/unreal/ue_hero_facade_import.py from the reference photographs (including the generated
# ashlar albedo). This generic pass used to override them with the kit's flat stone, which is why the
# elevation kept rendering as a plain card no matter what the import did.
if actor.get_actor_label() == "Hero_Facade":
log("materials Hero_Facade: skipped (own authored materials)")
apply_hero_materials(actor)
continue
fallback = LABEL_MATERIALS.get(actor.get_actor_label())
comp = actor.get_component_by_class(unreal.StaticMeshComponent)
if not comp or not comp.static_mesh: