ralpha-assets/RalphaData/scripts/generate_catalogue.py

187 lines
6.4 KiB
Python

#!/usr/bin/env python3
"""
Generate catalogue.json from Unreal Engine assets.
This script can be run:
1. Standalone with Python (parses .uasset files for metadata)
2. As a wrapper for UE commandlet (for full asset inspection)
Usage:
python generate_catalogue.py --project /path/to/Ralpha.uproject
python generate_catalogue.py --scan-only # Just scan folders
"""
import argparse
import json
import os
import subprocess
import hashlib
from datetime import datetime
from pathlib import Path
from typing import Any
# Asset categories based on folder structure
CATEGORY_MAP = {
"Furniture/Seating": ["sofa", "chair", "stool", "bench", "armchair"],
"Furniture/Tables": ["table", "desk", "counter"],
"Furniture/Storage": ["shelf", "cabinet", "drawer", "wardrobe"],
"Furniture/Bedroom": ["bed", "nightstand", "mattress"],
"Electronics/Computing": ["laptop", "monitor", "keyboard", "mouse", "computer"],
"Electronics/Entertainment": ["tv", "speaker", "console", "headphone"],
"Electronics/Appliances": ["refrigerator", "microwave", "oven", "washer"],
"Food/Packaged": ["can", "bottle", "box", "bag", "jar"],
"Food/Fresh": ["fruit", "vegetable", "bread", "meat"],
"Food/Prepared": ["pizza", "burger", "sandwich", "cake"],
"Food/Beverages": ["coffee", "tea", "juice", "wine", "beer"],
"Vehicles/Cars": ["sedan", "suv", "truck", "van", "sports"],
"Vehicles/Motorcycles": ["motorcycle", "scooter", "bike"],
"Nature/Trees": ["tree", "pine", "oak", "palm"],
"Nature/Plants": ["plant", "flower", "bush", "grass"],
"Nature/Rocks": ["rock", "boulder", "stone", "cliff"],
}
def scan_asset_folder(content_path: Path) -> list[dict[str, Any]]:
"""Scan Content/Ralpha/Assets for .uasset files."""
assets = []
asset_root = content_path / "Ralpha" / "Assets"
if not asset_root.exists():
print(f"Warning: Asset folder not found: {asset_root}")
return assets
for uasset_path in asset_root.rglob("*.uasset"):
# Skip generated files
if "Intermediate" in str(uasset_path) or "Saved" in str(uasset_path):
continue
rel_path = uasset_path.relative_to(content_path)
category = str(rel_path.parent).replace("Ralpha/Assets/", "").replace("/", "/")
# Generate stable ID from path
asset_name = uasset_path.stem
asset_id = asset_name.lower().replace(" ", "_").replace("-", "_")
# Build Unreal path
unreal_path = f"/Game/{str(rel_path.with_suffix('')).replace(os.sep, '/')}"
# Infer tags from name
tags = [word.lower() for word in asset_name.replace("_", " ").split()]
asset = {
"id": asset_id,
"name": asset_name.replace("_", " ").title(),
"category": category,
"unreal_path": unreal_path,
"tags": tags,
"thumbnail": f"thumbnails/{asset_id}.png",
"pivot": "bottom_center",
"placement_modes": ["floor"],
"collision": True,
"license": {
"type": "Unknown",
"source": "TBD"
},
"added_at": datetime.utcnow().isoformat() + "Z"
}
assets.append(asset)
return assets
def run_ue_commandlet(project_path: Path, ue_path: Path | None = None) -> list[dict[str, Any]]:
"""Run UE commandlet to extract full asset metadata."""
# This would invoke the UE editor in commandlet mode
# For now, just use scan method
print("Note: Full UE commandlet integration not implemented yet")
print("Using folder scan method instead")
content_path = project_path.parent / "Content"
return scan_asset_folder(content_path)
def generate_thumbnails(assets: list[dict], project_path: Path) -> None:
"""Generate thumbnail images for assets (placeholder)."""
thumbnail_dir = project_path.parent / "RalphaData" / "thumbnails"
thumbnail_dir.mkdir(parents=True, exist_ok=True)
print(f"Thumbnail directory: {thumbnail_dir}")
print("Note: Actual thumbnail generation requires UE Editor Utility")
# Create placeholder file
placeholder = thumbnail_dir / ".gitkeep"
placeholder.touch()
def compute_catalogue_hash(assets: list[dict]) -> str:
"""Compute hash of catalogue for version tracking."""
content = json.dumps(sorted([a["id"] for a in assets]))
return hashlib.sha256(content.encode()).hexdigest()[:12]
def main():
parser = argparse.ArgumentParser(description="Generate Ralpha asset catalogue")
parser.add_argument("--project", type=Path, help="Path to .uproject file")
parser.add_argument("--scan-only", action="store_true", help="Only scan folders, no UE")
parser.add_argument("--output", type=Path, help="Output catalogue path")
parser.add_argument("--ue-path", type=Path, help="Path to UE Editor executable")
args = parser.parse_args()
# Find project
if args.project:
project_path = args.project
else:
# Default to current directory
project_files = list(Path.cwd().glob("*.uproject"))
if project_files:
project_path = project_files[0]
else:
print("Error: No .uproject file found. Use --project to specify.")
return 1
print(f"Project: {project_path}")
# Scan or run commandlet
content_path = project_path.parent / "Content"
if args.scan_only or not args.ue_path:
assets = scan_asset_folder(content_path)
else:
assets = run_ue_commandlet(project_path, args.ue_path)
print(f"Found {len(assets)} assets")
# Build catalogue
catalogue = {
"$schema": "./catalogue.schema.json",
"version": "1.0.0",
"generated_at": datetime.utcnow().isoformat() + "Z",
"asset_count": len(assets),
"catalogue_hash": compute_catalogue_hash(assets),
"categories": sorted(list(set(a["category"] for a in assets))),
"assets": sorted(assets, key=lambda a: (a["category"], a["id"]))
}
# Output
if args.output:
output_path = args.output
else:
output_path = project_path.parent / "RalphaData" / "catalogue" / "catalogue.json"
output_path.parent.mkdir(parents=True, exist_ok=True)
with open(output_path, "w") as f:
json.dump(catalogue, f, indent=2)
print(f"Catalogue written to: {output_path}")
print(f"Hash: {catalogue['catalogue_hash']}")
# Generate thumbnails placeholder
generate_thumbnails(assets, project_path)
return 0
if __name__ == "__main__":
exit(main())