8.9 KiB
8.9 KiB
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.jsonfrom 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
.blendfile. - 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:
-
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).
- Create a new Python file named
-
Manifest Handling:
- Location: Use a separate JSON file named
[ActiveBlendFileName]_manifest.json, located in the same directory as the active.blendfile. - 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 themanifest_datadictionary to the manifest JSON file. - Checking: Implement helper functions
is_asset_processed(manifest_data, asset_name)andis_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.
- Location: Use a separate JSON file named
-
Core Logic -
process_library()function:- Get Blender context.
- Load manifest data (if enabled).
- Validate that
PROCESSED_ASSET_LIBRARY_ROOTexists. - 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.walkorpathlib.rglobto find allmetadata.jsonfiles within thePROCESSED_ASSET_LIBRARY_ROOT. - Iterate Metadata: For each
metadata.jsonfound:- 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 theaspect_ratio_change_stringfrom metadata (using helper functioncalculate_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 themin,max,meanvalues stored in the map's metadata entry for the reference resolution.
- Find the aspect ratio node using
- Apply Asset Tags: Use
asset_data.tags.new()to add thesupplier_nameandarchetypetags (checking for existence first).
- Determine target parent group name (e.g.,
- Child Group Handling (Iterate through
mapsin 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 itsimage_pathin 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.namebased on themap_type(using a helper functionget_color_space). - Update manifest dictionary for this map/resolution (if enabled).
- For each
- Determine target child group name (e.g.,
- For each
- Update manifest dictionary for the processed asset (if enabled).
- Parse the JSON data. Extract key information:
- Save manifest data (if enabled and changes were made).
- Print summary (duration, groups created/updated, etc.).
-
Helper Functions:
find_nodes_by_label(node_tree, label, node_type): Reusable function to find nodes.calculate_factor_from_string(aspect_string): Parses theaspect_ratio_change_stringfrom 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.
-
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:
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