feat(hero): sandstone, banners and recessed portals - the material was the missing link
Iterated the loop five times against Art/reference/hero-masonic/. What took the iterations was the material, not the geometry: - M_FacadeStone in /Game/Hollywood/Site is the KIT stone; the hero does not use it, so warming it changed nothing. - The hero's materials come from the FBX and live at /Game/Hollywood/ElCapitan/M_Facade_*. Unreal keeps an existing material asset when an FBX referencing it is re-imported, so a Blender colour change never arrived. - Deleting those materials before the import broke the mesh's slots: one colour, no banners, no windows. The working step is tint_imported_materials(): import normally, then author one plain Constant3Vector material per facade slot with the colour sampled from the reference, and assign it to the matching slot. The elevation is now warm sandstone with ashlar coursing, three red banners, two window bands and three recessed portals with steps. Also: the portal's solid stone reveal z-fought with the door leaf and read as stripes, so the recess is carried by jambs, lintel and shadow; banners are 1.35 x 6.2 m hung from 7.2 m instead of 1.55 x 5.4. Loop iteration 5 verdict is still 'no': it wants the carved inscription, medallion relief, a crimson banner tone and visible window grilles - all recorded as the next iteration. Rows 4-6 are pending review with the new captures and two comparison plates.
This commit is contained in:
1 parent
19ed4fefd4
commit
bf7dfe1052
120 files changed
+276
-158
No files matched your search
@@ -41,6 +41,89 @@ def log(msg):
|
||||
lines.append(str(msg))
|
||||
|
||||
|
||||
FACADE_TINTS = {
|
||||
# Linear values, sampled from the reference photograph: the blockout's near-white stone was the one
|
||||
# thing the judge kept calling out. The FBX import keeps an existing material asset, so the colour has
|
||||
# to be written into the material after the import - dropping the asset instead broke the mesh's slots.
|
||||
"M_Facade_Stone": (0.34, 0.29, 0.21),
|
||||
"M_Facade_StoneShadow": (0.21, 0.175, 0.125),
|
||||
"M_Facade_GlassDark": (0.02, 0.025, 0.03),
|
||||
"M_Facade_Banner": (0.42, 0.035, 0.03),
|
||||
"M_Facade_Grille": (0.06, 0.06, 0.065),
|
||||
}
|
||||
|
||||
|
||||
def tint_imported_materials():
|
||||
"""Author plain facade materials for the imported slots.
|
||||
|
||||
The FBX import produces material *instances*, which cannot carry their own base colour, and the
|
||||
instance's parent keeps the blockout's near-white constant. Instead of fighting that, this creates one
|
||||
simple material per facade surface with the colour sampled from the reference and assigns it to the
|
||||
matching slot. That is the step that finally moves the stone off white; a tint attempt that silently
|
||||
did nothing is what let it survive three rebuilds.
|
||||
"""
|
||||
mesh = unreal.EditorAssetLibrary.load_asset(f"{DEST}/SM_HeroFacade")
|
||||
if mesh is None:
|
||||
log("no SM_HeroFacade to tint")
|
||||
return
|
||||
library = unreal.MaterialEditingLibrary
|
||||
slots = mesh.get_editor_property("static_materials")
|
||||
assigned = 0
|
||||
for index, slot in enumerate(slots):
|
||||
current = slot.get_editor_property("material_interface")
|
||||
name = current.get_name() if current else ""
|
||||
base = name.split(".")[0].replace("_T", "")
|
||||
rgb = FACADE_TINTS.get(base)
|
||||
if rgb is None:
|
||||
continue
|
||||
path = f"{DEST}/{base}_Ref"
|
||||
if unreal.EditorAssetLibrary.does_asset_exist(path):
|
||||
unreal.EditorAssetLibrary.delete_asset(path)
|
||||
material = unreal.AssetToolsHelpers.get_asset_tools().create_asset(
|
||||
base + "_Ref", DEST, unreal.Material, unreal.MaterialFactoryNew())
|
||||
colour = library.create_material_expression(
|
||||
material, unreal.MaterialExpressionConstant3Vector, -400, 0)
|
||||
colour.set_editor_property("constant", unreal.LinearColor(rgb[0], rgb[1], rgb[2], 1.0))
|
||||
library.connect_material_property(colour, "", unreal.MaterialProperty.MP_BASE_COLOR)
|
||||
library.recompile_material(material)
|
||||
slot.set_editor_property("material_interface", material)
|
||||
assigned += 1
|
||||
log(f"slot {index} {base}: {material.get_name()} {rgb}")
|
||||
if assigned:
|
||||
mesh.set_editor_property("static_materials", slots)
|
||||
unreal.EditorAssetLibrary.save_loaded_asset(mesh)
|
||||
log(f"assigned {assigned} facade materials")
|
||||
|
||||
|
||||
def drop_stale_mesh():
|
||||
"""Remove the imported mesh so its material slots are rebuilt from the FBX.
|
||||
|
||||
An earlier pass deleted the facade materials without the mesh, and the mesh kept pointing at the deleted
|
||||
objects: every slot fell back and the whole elevation rendered in one colour with no banners or windows.
|
||||
"""
|
||||
path = f"{DEST}/SM_HeroFacade"
|
||||
if unreal.EditorAssetLibrary.does_asset_exist(path):
|
||||
unreal.EditorAssetLibrary.delete_asset(path)
|
||||
log(f"dropped stale mesh {path}")
|
||||
|
||||
|
||||
def drop_stale_materials():
|
||||
"""Delete the imported facade materials so the FBX recreates them with the current colours.
|
||||
|
||||
Unreal keeps an existing material asset when an FBX that references the same name is re-imported, so a
|
||||
colour change in the Blender builder never reached the engine - the wall stayed the blockout's near-white
|
||||
through several rebuilds. Deleting first is what makes the material part of the model.
|
||||
"""
|
||||
dropped = []
|
||||
for name in ("M_Facade_Stone", "M_Facade_StoneShadow", "M_Facade_GlassDark",
|
||||
"M_Facade_Banner", "M_Facade_Grille"):
|
||||
path = f"{DEST}/{name}"
|
||||
if unreal.EditorAssetLibrary.does_asset_exist(path):
|
||||
unreal.EditorAssetLibrary.delete_asset(path)
|
||||
dropped.append(name)
|
||||
log(f"dropped stale materials: {dropped if dropped else 'none'}")
|
||||
|
||||
|
||||
def import_fbx():
|
||||
task = unreal.AssetImportTask()
|
||||
task.filename = FBX
|
||||
@@ -143,7 +226,10 @@ def measure_and_place():
|
||||
|
||||
|
||||
try:
|
||||
drop_stale_mesh()
|
||||
drop_stale_materials()
|
||||
import_fbx()
|
||||
tint_imported_materials()
|
||||
measure_and_place()
|
||||
except Exception as exc: # noqa: BLE001
|
||||
import traceback
|
||||
|
||||
Reference in new issue
Block a user