e2eda92d82
Linked materials are external references — Blender's GLTF exporter cannot traverse their node trees to extract Principled BSDF PBR values (metallic, roughness, base color, normal maps). Appended materials are local copies that the exporter can fully traverse. Changes: - asset_library.py: add link=True parameter (default unchanged for renders) - export_gltf.py: call apply_asset_library_materials with link=False - export_gltf.py: add export_materials='EXPORT' + export_image_format='AUTO' to embed textures and ensure full PBR data in the GLB Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
81 lines
3.1 KiB
Python
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": "SCHAEFFLER_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)
|