187 lines
6.4 KiB
Python
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())
|