Asset-Frameworker/Project Notes/BLENDER_MATERIAL_CREATION_PLAN.md
2025-04-29 18:26:13 +02:00

131 lines
9.5 KiB
Markdown

# 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;