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