# Blender Material Creation Script Plan This document outlines the plan for creating a new Blender script (`create_materials.py`) in the `blenderscripts/` directory. This script will scan the processed asset library output by the Asset Processor Tool, read the `metadata.json` files, and create or update Blender materials that link to the corresponding PBRSET node groups found in a specified Blender Asset Library. The script will also set the material's viewport properties using pre-calculated statistics from the metadata. The script will skip processing an asset if the corresponding material already exists in the current Blender file. ## 1. Script Location and Naming * Create a new file: `blenderscripts/create_materials.py`. ## 2. Script Structure The script will follow a similar structure to `blenderscripts/create_nodegroups.py`, including: * Import statements (`bpy`, `os`, `json`, `pathlib`, `time`, `base64`). * A `--- USER CONFIGURATION ---` section at the top. * Helper functions. * A main processing function (e.g., `process_library_for_materials`). * An execution block (`if __name__ == "__main__":`) to run the main function. ## 3. Configuration Variables The script will include the following configuration variables in the `--- USER CONFIGURATION ---` section: * `PROCESSED_ASSET_LIBRARY_ROOT`: Path to the root output directory of the Asset Processor Tool (same as in `create_nodegroups.py`). This is used to find the `metadata.json` files and reference images for previews. * `PBRSET_ASSET_LIBRARY_NAME`: The name of the Blender Asset Library (configured in Blender Preferences) that contains the PBRSET node groups created by `create_nodegroups.py`. * `TEMPLATE_MATERIAL_NAME`: Name of the required template material in the Blender file (e.g., "Template_PBRMaterial"). * `PLACEHOLDER_NODE_LABEL`: Label of the placeholder Group node within the template material's node tree where the PBRSET node group will be linked (e.g., "PBRSET_PLACEHOLDER"). * `MATERIAL_NAME_PREFIX`: Prefix for the created materials (e.g., "Mat_"). * `PBRSET_GROUP_PREFIX`: Prefix used for the PBRSET node groups created by `create_nodegroups.py` (e.g., "PBRSET_"). * `REFERENCE_MAP_TYPES`: List of map types to look for to find a reference image for the material preview (e.g., `["COL", "COL-1"]`). * `REFERENCE_RESOLUTION_ORDER`: Preferred resolution order for the reference image (e.g., `["1K", "512", "2K", "4K"]`). * `IMAGE_FILENAME_PATTERN`: Assumed filename pattern for processed images (same as in `create_nodegroups.py`). * `FALLBACK_IMAGE_EXTENSIONS`: Fallback extensions for finding image files (same as in `create_nodegroups.py`). * `VIEWPORT_COLOR_MAP_TYPES`: List of map types to check in metadata's `image_stats_1k` for viewport diffuse color. * `VIEWPORT_ROUGHNESS_MAP_TYPES`: List of map types to check in metadata's `image_stats_1k` for viewport roughness. * `VIEWPORT_METALLIC_MAP_TYPES`: List of map types to check in metadata's `image_stats_1k` for viewport metallic. ## 4. Helper Functions The script will include the following helper functions: * `find_nodes_by_label(node_tree, label, node_type=None)`: Reusable from `create_nodegroups.py` to find nodes in a node tree. * `add_tag_if_new(asset_data, tag_name)`: Reusable from `create_nodegroups.py` to add asset tags. * `reconstruct_image_path_with_fallback(...)`: Reusable from `create_nodegroups.py` to find image paths (needed for setting the custom preview). * `get_stat_value(stats_dict, map_type_list, stat_key)`: A helper function to safely retrieve a specific statistic from the `image_stats_1k` dictionary. ## 5. Main Processing Logic (`process_library_for_materials`) The main function will perform the following steps: * **Pre-run Checks:** * Verify `PROCESSED_ASSET_LIBRARY_ROOT` exists and is a directory. * Verify the `PBRSET_ASSET_LIBRARY_NAME` exists in Blender's user preferences (`bpy.context.preferences.filepaths.asset_libraries`). * Verify the `TEMPLATE_MATERIAL_NAME` material exists and uses nodes. * Verify the `PLACEHOLDER_NODE_LABEL` Group node exists in the template material's node tree. * **Scan for Metadata:** * Iterate through supplier directories within `PROCESSED_ASSET_LIBRARY_ROOT`. * Iterate through asset directories within each supplier directory. * Identify `metadata.json` files. * **Process Each Metadata File:** * Load the `metadata.json` file. * Extract `asset_name`, `supplier_name`, `archetype`, `processed_map_resolutions`, `merged_map_resolutions`, `map_details`, and `image_stats_1k`. * Determine the expected PBRSET node group name: `f"{PBRSET_GROUP_PREFIX}{asset_name}"`. * Determine the target material name: `f"{MATERIAL_NAME_PREFIX}{asset_name}"`. * **Find or Create Material:** * Check if a material with the `target_material_name` already exists in `bpy.data.materials`. * If it exists, log a message indicating the asset is being skipped and move to the next metadata file. * If it doesn't exist, copy the `TEMPLATE_MATERIAL_NAME` material and rename the copy to `target_material_name` (create mode). Handle potential copy failures. * **Find Placeholder Node:** * Find the node with `PLACEHOLDER_NODE_LABEL` in the target material's node tree using `find_nodes_by_label`. Handle cases where the node is not found or is not a Group node. * **Find and Link PBRSET Node Group from Asset Library:** * Get the path to the `.blend` file associated with the `PBRSET_ASSET_LIBRARY_NAME` from user preferences. * Use `bpy.data.libraries.load(filepath, link=True)` to link the node group with `target_pbrset_group_name` from the external `.blend` file into the current file. Handle cases where the library or the node group is not found. * Once linked, get the reference to the newly linked node group in `bpy.data.node_groups`. * **Link Linked Node Group to Placeholder:** * If both the placeholder node and the *newly linked* PBRSET node group are found, assign the linked node group to the `node_tree` property of the placeholder node. * **Mark Material as Asset:** * If the material is new or not already marked, call `material.asset_mark()`. * **Copy Asset Tags:** * If both the material and the *linked* PBRSET node group have asset data, copy tags (supplier, archetype) from the node group to the material using `add_tag_if_new`. * **Set Custom Preview:** * Find a suitable reference image path (e.g., lowest resolution COL map) using `reconstruct_image_path_with_fallback` and the `REFERENCE_MAP_TYPES` and `REFERENCE_RESOLUTION_ORDER` configurations. * If a reference image path is found, use `bpy.ops.ed.lib_id_load_custom_preview` to set the custom preview for the material. This operation requires overriding the context. * **Set Viewport Properties (using metadata stats):** * Check if `image_stats_1k` is present and valid in the metadata. * **Diffuse Color:** Use the `get_stat_value` helper to get the 'mean' stat for map types in `VIEWPORT_COLOR_MAP_TYPES`. If found and is a valid color list `[R, G, B]`, set `material.diffuse_color` to this value. * **Roughness:** Use the `get_stat_value` helper to get the 'mean' stat for map types in `VIEWPORT_ROUGHNESS_MAP_TYPES`. If found, get the first value (for grayscale). Check if stats for `VIEWPORT_METALLIC_MAP_TYPES` exist. If metallic stats are *not* found, invert the roughness value (`1.0 - value`) before assigning it to `material.roughness`. Clamp the final value between 0.0 and 1.0. * **Metallic:** Use the `get_stat_value` helper to get the 'mean' stat for map types in `VIEWPORT_METALLIC_MAP_TYPES`. If found, get the first value (for grayscale) and assign it to `material.metallic`. If metallic stats are *not* found, set `material.metallic` to 0.0. * **Error Handling and Reporting:** * Include `try...except` blocks to catch errors during file reading, JSON parsing, Blender operations, linking, etc. * Print informative messages about progress, creation/update status, and errors. * **Summary Report:** * Print a summary of how many metadata files were processed, materials created/updated, node groups linked, errors encountered, and assets skipped. ## 6. Process Flow Diagram (Updated) ```mermaid graph TD A[Start Script] --> B{Pre-run Checks Pass?}; B -- No --> C[Abort Script]; B -- Yes --> D[Scan Processed Asset Root]; D --> E{Found metadata.json?}; E -- No --> F[Finish Script (No Assets)]; E -- Yes --> G[Loop through metadata.json files]; G --> H[Read metadata.json]; H --> I{Metadata Valid?}; I -- No --> J[Log Error, Skip Asset]; I -- Yes --> K[Extract Asset Info & Stats]; K --> L[Find or Create Material]; L --> M{Material Exists?}; M -- Yes --> N[Log Skip, Continue Loop]; M -- No --> O[Find Placeholder Node in Material]; O --> P[Find PBRSET NG in Library & Link]; P --> Q{Linking Successful?}; Q -- No --> R[Log Error, Skip Asset]; Q -- Yes --> S[Link Linked NG to Placeholder]; S --> T[Mark Material as Asset]; T --> U[Copy Asset Tags]; U --> V[Find Reference Image Path for Preview]; V --> W{Reference Image Found?}; W -- Yes --> X[Set Custom Material Preview]; W -- No --> Y[Log Warning (No Preview)]; X --> Z[Set Viewport Properties from Stats]; Y --> Z; Z --> AA[Increment Counters]; AA --> G; G --> AB[Print Summary Report]; AB --> AC[End Script]; J --> G; N --> G; R --> G;