Files
HartOMat/render-worker/scripts/asset_library.py
T

81 lines
3.1 KiB
Python

"""Asset library helpers for Blender render scripts.
Provides functions to link materials and node groups from a .blend asset library
into the current scene, and apply them to mesh objects.
These functions are intended to be imported by still_render.py / turntable_render.py
when a RenderTemplate has an asset library associated.
"""
from __future__ import annotations
import logging
logger = logging.getLogger(__name__)
def apply_asset_library_materials(blend_path: str, material_map: dict, link: bool = True) -> None:
"""Load materials from an asset library .blend and apply them to mesh slots.
Args:
blend_path: Absolute path to the .blend library file.
material_map: Mapping of current slot material name -> library material name.
E.g. {"Steel--Stahl": "HARTOMAT_010101_Steel-Bare"}
link: If True (default), link materials (external reference, good for rendering).
If False, append materials (local copy — required for GLB/GLTF export so
that the exporter can traverse Principled BSDF node trees for PBR values).
"""
import bpy # type: ignore[import]
if not material_map:
return
target_names = set(material_map.values())
# Link or append materials from the library
with bpy.data.libraries.load(blend_path, link=link, assets_only=True) as (src, dst):
dst.materials = [name for name in src.materials if name in target_names]
loaded = {m.name for m in dst.materials if m is not None}
logger.info("%s %d materials from %s", "Linked" if link else "Appended", len(loaded), blend_path)
# Apply to all mesh object material slots
for obj in bpy.data.objects:
if obj.type != "MESH":
continue
for slot in obj.material_slots:
if slot.material is None:
continue
resolved = material_map.get(slot.material.name)
if resolved and resolved in bpy.data.materials:
slot.material = bpy.data.materials[resolved]
def apply_asset_library_node_groups(blend_path: str, modifier_map: dict) -> None:
"""Link geometry node groups from an asset library and apply as modifiers.
Args:
blend_path: Absolute path to the .blend library file.
modifier_map: Mapping of object name substring -> node group name.
E.g. {"ring": "WearPattern_GeoNodes"}
"""
import bpy # type: ignore[import]
if not modifier_map:
return
target_names = set(modifier_map.values())
with bpy.data.libraries.load(blend_path, link=True, assets_only=True) as (src, dst):
dst.node_groups = [name for name in src.node_groups if name in target_names]
for obj in bpy.data.objects:
if obj.type != "MESH":
continue
for part_substr, ng_name in modifier_map.items():
if part_substr.lower() in obj.name.lower():
ng = bpy.data.node_groups.get(ng_name)
if ng:
mod = obj.modifiers.new(name=ng_name, type="NODES")
mod.node_group = ng
logger.info("Applied node group %s to %s", ng_name, obj.name)