feat(K): Blender Asset Library + production exports (GLB + .blend)
- feat(migration): 045_asset_libraries — new asset_libraries table (blend_file_path, catalog JSONB) - feat(model): AssetLibrary SQLAlchemy model in domains/materials/models.py - feat(api): POST/GET/PATCH/DELETE /api/asset-libraries + /upload-blend + /refresh-catalog endpoints - feat(celery): refresh_asset_library_catalog task on thumbnail_rendering queue — runs Blender headless - feat(blender): catalog_assets.py — extracts asset-marked materials + node_groups from .blend - feat(blender): asset_library.py — apply_asset_library_materials + apply_asset_library_node_groups helpers - feat(blender): export_gltf.py — STEP→STL→GLB production export with optional asset library - feat(blender): export_blend.py — STEP→STL→.blend production export with pack_all() - feat(frontend): api/assetLibraries.ts — full CRUD API client - feat(frontend): AssetLibraryPanel in Admin.tsx — upload, refresh, expand catalog view - docs: Blender asset_data marking requirement learning in LEARNINGS.md Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
"""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) -> None:
|
||||
"""Link 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"}
|
||||
"""
|
||||
import bpy # type: ignore[import]
|
||||
|
||||
if not material_map:
|
||||
return
|
||||
|
||||
target_names = set(material_map.values())
|
||||
|
||||
# Link materials from the library
|
||||
with bpy.data.libraries.load(blend_path, link=True, assets_only=True) as (src, dst):
|
||||
dst.materials = [name for name in src.materials if name in target_names]
|
||||
|
||||
linked = {m.name for m in dst.materials if m is not None}
|
||||
logger.info("Linked %d materials from %s", len(linked), 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)
|
||||
Reference in New Issue
Block a user