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

124 lines
8.9 KiB
Markdown

# Blender Integration Plan: Node Groups from Processed Assets
**Objective:** Develop a Python script (`blenderscripts/create_nodegroups.py`) to run manually inside Blender. This script will scan the output directory generated by the Asset Processor Tool, read `metadata.json` files, and create/update corresponding PBR node groups in the active Blender file, leveraging the pre-calculated metadata.
**Key Principles:**
* **Leverage Existing Tool Output:** Rely entirely on the structured output and `metadata.json` from the Asset Processor Tool. Avoid reprocessing or recalculating data already available.
* **Blender Environment:** The script is designed solely for Blender's Python environment (`bpy`).
* **Manual Execution:** Users will manually run this script from Blender's Text Editor.
* **Target Active File:** All operations modify the currently open `.blend` file.
* **Assume Templates:** The script will assume node group templates (`Template_PBRSET`, `Template_PBRTYPE`) exist in the active file. Error handling will be added if they are missing.
* **Focus on Node Groups:** The script's scope is limited to creating and updating the node groups, not materials.
**Detailed Plan:**
1. **Script Setup & Configuration:**
* Create a new Python file named `create_nodegroups.py` (intended location: `blenderscripts/`).
* Import necessary modules (`bpy`, `os`, `json`, `pathlib`).
* Define user-configurable variables at the top:
* `PROCESSED_ASSET_LIBRARY_ROOT`: Path to the root output directory of the Asset Processor Tool.
* `PARENT_TEMPLATE_NAME`: Name of the parent node group template (e.g., `"Template_PBRSET"`).
* `CHILD_TEMPLATE_NAME`: Name of the child node group template (e.g., `"Template_PBRTYPE"`).
* `ASPECT_RATIO_NODE_LABEL`: Label of the Value node in the parent template for aspect ratio correction (e.g., `"AspectRatioCorrection"`).
* `STATS_NODE_PREFIX`: Prefix for Combine XYZ nodes storing stats in the parent template (e.g., `"Histogram-"`).
* `ENABLE_MANIFEST`: Boolean flag to enable/disable the manifest system (default: `True`).
2. **Manifest Handling:**
* **Location:** Use a separate JSON file named `[ActiveBlendFileName]_manifest.json`, located in the same directory as the active `.blend` file.
* **Loading:** Implement `load_manifest(context)` that finds and reads the manifest JSON file. If not found or invalid, return an empty dictionary.
* **Saving:** Implement `save_manifest(context, manifest_data)` that writes the `manifest_data` dictionary to the manifest JSON file.
* **Checking:** Implement helper functions `is_asset_processed(manifest_data, asset_name)` and `is_map_processed(manifest_data, asset_name, map_type, resolution)` to check against the loaded manifest.
* **Updating:** Update the manifest dictionary in memory as assets/maps are processed. Save the manifest once at the end of the script.
3. **Core Logic - `process_library()` function:**
* Get Blender context.
* Load manifest data (if enabled).
* Validate that `PROCESSED_ASSET_LIBRARY_ROOT` exists.
* Validate that template node groups exist in `bpy.data.node_groups`. Exit gracefully with an error message if not found.
* Initialize counters (new groups, updated groups, etc.).
* **Scan Directory:** Use `os.walk` or `pathlib.rglob` to find all `metadata.json` files within the `PROCESSED_ASSET_LIBRARY_ROOT`.
* **Iterate Metadata:** For each `metadata.json` found:
* Parse the JSON data. Extract key information: `asset_name`, `supplier_name`, `archetype`, `maps` (dictionary of maps with resolutions, paths, stats), `aspect_ratio_change_string`.
* Check manifest if asset is already processed (if enabled). Skip if true.
* **Parent Group Handling:**
* Determine target parent group name (e.g., `f"PBRSET_{asset_name}"`).
* Find existing group or create a copy from `PARENT_TEMPLATE_NAME`.
* Mark group as asset (`asset_mark()`) if not already.
* **Apply Metadata:**
* Find the aspect ratio node using `ASPECT_RATIO_NODE_LABEL`. Calculate the correction factor based on the `aspect_ratio_change_string` from metadata (using helper function `calculate_factor_from_string`) and set the node's default value.
* For relevant map types (e.g., ROUGH, DISP), find the stats node (`STATS_NODE_PREFIX` + map type). Set the X, Y, Z inputs using the `min`, `max`, `mean` values stored in the map's metadata entry for the reference resolution.
* **Apply Asset Tags:** Use `asset_data.tags.new()` to add the `supplier_name` and `archetype` tags (checking for existence first).
* **Child Group Handling (Iterate through `maps` in metadata):**
* For each `map_type` (e.g., "COL", "NRM") and its data in the metadata:
* Determine target child group name (e.g., `f"PBRTYPE_{asset_name}_{map_type}"`).
* Find existing child group or create a copy from `CHILD_TEMPLATE_NAME`.
* Find the corresponding placeholder node in the *parent* group (by label matching `map_type`). Assign the child node group to this placeholder (`placeholder_node.node_tree = child_group`).
* Link the child group's output to the corresponding parent group's output socket. Ensure the parent output socket type is `NodeSocketColor`.
* **Image Node Handling (Iterate through resolutions for the map type):**
* For each `resolution` (e.g., "4K", "2K") and its `image_path` in the metadata:
* Check manifest if this specific map/resolution is processed (if enabled). Skip if true.
* Find the corresponding Image Texture node in the *child* group (by label matching `resolution`, e.g., "4K").
* Load the image using `bpy.data.images.load(image_path, check_existing=True)`. Handle potential file-not-found errors.
* Assign the loaded image to the `image_node.image`.
* Set the `image_node.image.colorspace_settings.name` based on the `map_type` (using a helper function `get_color_space`).
* Update manifest dictionary for this map/resolution (if enabled).
* Update manifest dictionary for the processed asset (if enabled).
* Save manifest data (if enabled and changes were made).
* Print summary (duration, groups created/updated, etc.).
4. **Helper Functions:**
* `find_nodes_by_label(node_tree, label, node_type)`: Reusable function to find nodes.
* `calculate_factor_from_string(aspect_string)`: Parses the `aspect_ratio_change_string` from metadata and returns the appropriate UV X-scaling factor.
* `get_color_space(map_type)`: Returns the appropriate Blender color space name for a given map type string.
* `add_tag_if_new(asset_data, tag_name)`: Adds a tag if it doesn't exist.
* Manifest loading/saving/checking functions.
5. **Execution Block (`if __name__ == "__main__":`)**
* Add pre-run checks (templates exist, library path valid, blend file saved if manifest enabled).
* Call the main `process_library()` function.
* Include basic timing and print statements for start/end.
**Mermaid Diagram:**
```mermaid
graph TD
A[Start Script in Blender] --> B(Load Config: Lib Path, Template Names, Node Labels);
B --> C{Check Templates Exist};
C -- Templates OK --> D(Load Manifest from adjacent .json file);
C -- Templates Missing --> X(Error & Exit);
D --> E{Scan Processed Library for metadata.json};
E --> F{For each metadata.json};
F --> G{Parse Metadata (Asset Name, Supplier, Archetype, Maps, Aspect Str, Stats)};
G --> H{Is Asset in Manifest?};
H -- Yes --> F;
H -- No --> I{Find/Create Parent Group (PBRSET_)};
I --> J(Mark as Asset & Apply Supplier + Archetype Tags);
J --> K(Find Aspect Node & Set Value from Aspect String);
K --> M{For each Map Type in Metadata};
M --> N(Find Stats Node & Set Values from Stats in Metadata);
N --> O{Find/Create Child Group (PBRTYPE_)};
O --> P(Assign Child to Parent Placeholder);
P --> Q(Link Child Output to Parent Output);
Q --> R{For each Resolution of Map};
R --> S{Is Map/Res in Manifest?};
S -- Yes --> R;
S -- No --> T(Find Image Node in Child);
T --> U(Load Processed Image);
U --> V(Assign Image to Node);
V --> W(Set Image Color Space);
W --> W1(Update Manifest Dict for Map/Res);
W1 --> R;
R -- All Resolutions Done --> M;
M -- All Map Types Done --> X1(Update Manifest Dict for Asset);
X1 --> F;
F -- All metadata.json Processed --> Y(Save Manifest Dict to adjacent .json file);
Y --> Z(Print Summary & Finish);
subgraph "Manifest Operations (External File)"
D; H; S; W1; X1; Y;
end
subgraph "Node/Asset Operations"
I; J; K; N; O; P; Q; T; U; V; W;
end