feat(hero): generate the stone albedo with the new texture tool, import it, wire it into the material
Used the workflow integration from the previous step for real: generated a warm limestone ashlar albedo with two profiles compared (AlbedoBase 17.04 vs Krea-2 13.90, AlbedoBase won), saved it as Art/textures/generated/T_FacadeAshlar_AI.png beside its provenance sidecar, and imported it to /Game/Hollywood/Textures. ue_hero_facade_import.py now imports that albedo and, for the two stone slots, builds a material with TextureSampleParameter2D -> Multiply(tint) -> Base Color, tiling the UVs 6x, so the map carries the coursing and the tint keeps the value inside the reference's range. Two real bugs fixed on the way: - the albedo path was resolved against unreal.Paths.project_dir() (Game/GigLine), not the repository root, so the import silently skipped; - the authored materials were never saved - only the mesh was - so after a reload the slots pointed at nothing and the elevation fell back to one default colour. Honest status: the nodes are verified present and saved (Constant3Vector, TextureSampleParameter2D, TextureCoordinate, Multiply) and the source mesh has UVs, but the render still shows the flat tint. Recorded in TODO.md with the next probe: read the compiled Base Color pin instead of the node list. THIRD_PARTY_NOTICES.md gains the AlbedoBase row (commercial use, credit required).
This commit is contained in:
1 parent
70a7e19841
commit
f681d83736
126 files changed
+445
-139
No files matched your search
@@ -41,6 +41,12 @@ def log(msg):
|
||||
lines.append(str(msg))
|
||||
|
||||
|
||||
# Generated with Tools/comfyui_generate.py texture (AlbedoBase XL, see the sidecar beside the PNG). The
|
||||
# reference facade is coursed rubble-ashlar, not a flat tint, and a texture is what carries that at 45 m.
|
||||
FACADE_ALBEDO = "Art/textures/generated/T_FacadeAshlar_AI.png"
|
||||
FACADE_ALBEDO_ASSET = "T_FacadeAshlar_AI"
|
||||
TEXTURE_DIR = "/Game/Hollywood/Textures"
|
||||
|
||||
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
|
||||
@@ -53,6 +59,28 @@ FACADE_TINTS = {
|
||||
}
|
||||
|
||||
|
||||
def import_albedo():
|
||||
"""Import the generated ashlar albedo, if it is on disk. Returns the asset or None."""
|
||||
# unreal.Paths.project_dir() is Game/GigLine, not the repository root: the textures live beside Art/,
|
||||
# two levels above this file.
|
||||
repo_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
path = os.path.join(repo_root, FACADE_ALBEDO)
|
||||
if not os.path.exists(path):
|
||||
log(f"albedo missing: {path}")
|
||||
return None
|
||||
task = unreal.AssetImportTask()
|
||||
task.filename = path
|
||||
task.destination_path = TEXTURE_DIR
|
||||
task.destination_name = FACADE_ALBEDO_ASSET
|
||||
task.automated = True
|
||||
task.save = True
|
||||
task.replace_existing = True
|
||||
unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks([task])
|
||||
asset = unreal.EditorAssetLibrary.load_asset(f"{TEXTURE_DIR}/{FACADE_ALBEDO_ASSET}")
|
||||
log(f"albedo imported: {asset.get_path_name() if asset else 'FAILED'}")
|
||||
return asset
|
||||
|
||||
|
||||
def tint_imported_materials():
|
||||
"""Author plain facade materials for the imported slots.
|
||||
|
||||
@@ -67,6 +95,7 @@ def tint_imported_materials():
|
||||
log("no SM_HeroFacade to tint")
|
||||
return
|
||||
library = unreal.MaterialEditingLibrary
|
||||
albedo = import_albedo()
|
||||
slots = mesh.get_editor_property("static_materials")
|
||||
assigned = 0
|
||||
for index, slot in enumerate(slots):
|
||||
@@ -84,8 +113,31 @@ def tint_imported_materials():
|
||||
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)
|
||||
source = colour
|
||||
if albedo is not None and base in ("M_Facade_Stone", "M_Facade_StoneShadow"):
|
||||
# Texture sample multiplied by the tint: the map carries the coursing and grain, the tint keeps
|
||||
# the value in the reference's range instead of the generator's brighter one.
|
||||
sample = library.create_material_expression(
|
||||
material, unreal.MaterialExpressionTextureSampleParameter2D, -700, 0)
|
||||
sample.set_editor_property("parameter_name", f"Albedo_{base}")
|
||||
sample.set_editor_property("texture", albedo)
|
||||
if hasattr(unreal, "MaterialExpressionTextureCoordinate"):
|
||||
uvs = library.create_material_expression(
|
||||
material, unreal.MaterialExpressionTextureCoordinate, -950, 0)
|
||||
uvs.set_editor_property("u_tiling", 6.0)
|
||||
uvs.set_editor_property("v_tiling", 6.0)
|
||||
library.connect_material_expressions(uvs, "", sample, "UVs")
|
||||
multiply = library.create_material_expression(
|
||||
material, unreal.MaterialExpressionMultiply, -250, 0)
|
||||
library.connect_material_expressions(sample, "RGB", multiply, "A")
|
||||
library.connect_material_expressions(colour, "", multiply, "B")
|
||||
source = multiply
|
||||
library.connect_material_property(source, "", unreal.MaterialProperty.MP_BASE_COLOR)
|
||||
library.recompile_material(material)
|
||||
# Save the material itself. Saving only the mesh left these assets unsaved, so after a reload the
|
||||
# slots pointed at nothing and the whole elevation rendered in one default colour.
|
||||
saved = unreal.EditorAssetLibrary.save_loaded_asset(material)
|
||||
log(f"{base}_Ref saved: {saved}")
|
||||
slot.set_editor_property("material_interface", material)
|
||||
assigned += 1
|
||||
log(f"slot {index} {base}: {material.get_name()} {rgb}")
|
||||
|
||||
Reference in new issue
Block a user