Follows the character-artist clothing workflow literally: duplicate the body's own faces for the garment base, solidify for cloth thickness, and keep each piece a separate mesh. Attached as leader-pose components beside the hair, so no second rig. - Tools/character_clothing_build.py builds three garments from the body mesh, trimmed geometrically (height band plus a width limit) instead of by dominant bone: the bone filter left holes wherever a face's strongest bone was outside the allowed list. - Tools/ue_character_clothing_import.py imports them as CharacterTee / CharacterJeans / CharacterShoes, each arriving with its own single material slot. - The pawn attaches all three, hidden with the body in first person. Three traps recorded in MEMORY.md, each of which cost a build: MPFB weights its helper geometry to groups that dominate every vertex, Blender refuses to apply a modifier to a mesh with shape keys, and object-mode polygon selection is discarded on entering edit mode - the delete wiped the whole garment. Known defects, in TODO.md: the garments have coverage holes at shoulders, hips and knees, and the hair reads light and wispy since its material was repointed to the masked strand sheet.
72 lines
2.5 KiB
Python
72 lines
2.5 KiB
Python
# Import the garment meshes as separate skeletal meshes (0.2.2).
|
|
#
|
|
# blender -b Art/blender/character/character-2026-11-07.blend \
|
|
# --python Tools/character_clothing_build.py -- --out /tmp/gigline/clothing
|
|
# <engine>/Engine/Binaries/Linux/UnrealEditor-Cmd <uproject> -run=pythonscript \
|
|
# -script=<repo>/Tools/ue_character_clothing_import.py -nullrhi -unattended -nopause -stdout
|
|
#
|
|
# Each garment is its own asset, per the character-artist skill's rule that clothing
|
|
# stays separate so it can be swapped or re-materialled. The pawn attaches each one as
|
|
# a component using the body as the leader pose.
|
|
#
|
|
# Result file: /tmp/gigline/character-clothing-import.txt
|
|
|
|
import unreal
|
|
|
|
DEST = "/Game/Test/Character"
|
|
SOURCE = "/tmp/gigline/clothing"
|
|
GARMENTS = (("tee", "CharacterTee"), ("jeans", "CharacterJeans"), ("shoes", "CharacterShoes"))
|
|
RESULT = "/tmp/gigline/character-clothing-import.txt"
|
|
lines = []
|
|
|
|
|
|
def log(msg):
|
|
lines.append(str(msg))
|
|
unreal.log(str(msg))
|
|
|
|
|
|
def main():
|
|
for source, name in GARMENTS:
|
|
task = unreal.AssetImportTask()
|
|
task.filename = f"{SOURCE}/{source}.fbx"
|
|
task.destination_path = DEST
|
|
task.destination_name = name
|
|
task.automated = True
|
|
task.save = True
|
|
task.replace_existing = True
|
|
|
|
ui = unreal.FbxImportUI()
|
|
ui.import_mesh = True
|
|
ui.import_as_skeletal = True
|
|
ui.import_animations = False
|
|
ui.import_materials = True
|
|
ui.import_textures = False
|
|
ui.skeletal_mesh_import_data.import_uniform_scale = 1.0
|
|
task.options = ui
|
|
unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks([task])
|
|
|
|
mesh = unreal.EditorAssetLibrary.load_asset(f"{DEST}/{name}")
|
|
if mesh is None:
|
|
log(f"{name}: MISSING after import")
|
|
lines.append("VERDICT: FAIL")
|
|
return
|
|
mats = mesh.get_editor_property("materials")
|
|
log(f"{name}: imported, {len(mats)} slot(s): "
|
|
+ ", ".join((m.get_editor_property("material_interface").get_name()
|
|
if m.get_editor_property("material_interface") else "None")
|
|
for m in mats))
|
|
lines.append("VERDICT: PASS")
|
|
|
|
|
|
try:
|
|
main()
|
|
except Exception as exc: # noqa: BLE001
|
|
import traceback
|
|
log(f"FAILED: {type(exc).__name__}: {exc}")
|
|
log(traceback.format_exc())
|
|
lines.append("VERDICT: FAIL")
|
|
|
|
unreal.EditorLoadingAndSavingUtils.save_dirty_packages(True, True)
|
|
with open(RESULT, "w") as f:
|
|
f.write("\n".join(lines) + "\n")
|