Spaces:
Runtime error
Runtime error
| """Main asset generation pipeline orchestration.""" | |
| import time | |
| from pathlib import Path | |
| from typing import Optional | |
| from core.config import QUALITY_PRESETS | |
| from core.types import GenerationResult, AssetMetadata | |
| from generators import FluxGenerator, HunyuanGenerator | |
| from processors import BlenderProcessor, AssetValidator | |
| from utils import CacheManager, SecurityManager | |
| class AssetPipeline: | |
| """Orchestrates the complete asset generation pipeline.""" | |
| def __init__(self): | |
| self.flux = FluxGenerator() | |
| self.hunyuan = HunyuanGenerator() # API client with better error handling | |
| self.blender = BlenderProcessor() | |
| self.validator = AssetValidator() | |
| self.cache = CacheManager() | |
| self.security = SecurityManager() | |
| self.output_dir = Path("outputs") | |
| self.temp_dir = Path("temp") | |
| self.script_dir = Path("scripts") | |
| # Create directories | |
| self.output_dir.mkdir(exist_ok=True) | |
| self.temp_dir.mkdir(exist_ok=True) | |
| def generate( | |
| self, | |
| prompt: str, | |
| quality: str = "High", | |
| progress_callback: Optional[callable] = None | |
| ) -> GenerationResult: | |
| """ | |
| Generate 3D asset from text prompt. | |
| Args: | |
| prompt: Text description of asset | |
| quality: Quality preset (Fast/Balanced/High/Ultra) | |
| progress_callback: Optional callback for progress updates | |
| Returns: | |
| GenerationResult with GLB path and metadata | |
| """ | |
| start_time = time.time() | |
| def update_progress(value: float, desc: str): | |
| if progress_callback: | |
| progress_callback(value, desc=desc) | |
| print(f"[Pipeline] {desc} ({value*100:.0f}%)") | |
| try: | |
| # Step 1: Security checks | |
| update_progress(0.0, "Validating input...") | |
| prompt = self.security.sanitize_prompt(prompt) | |
| self.security.check_rate_limit() | |
| # Step 2: Check cache | |
| update_progress(0.05, "Checking cache...") | |
| if cached_path := self.cache.get_cached_result(prompt, quality): | |
| elapsed = time.time() - start_time | |
| preset = QUALITY_PRESETS[quality] | |
| metadata = AssetMetadata( | |
| prompt=prompt, | |
| quality=quality, | |
| flux_steps=preset.flux_steps, | |
| hunyuan_steps=preset.hunyuan_steps, | |
| file_size_mb=cached_path.stat().st_size / 1e6, | |
| generation_time_s=elapsed, | |
| optimized=True, | |
| rigged=False | |
| ) | |
| return GenerationResult( | |
| glb_path=cached_path, | |
| status_message="✨ Loaded from cache (saved GPU quota!)", | |
| metadata=metadata, | |
| cached=True | |
| ) | |
| # Step 3: Get quality preset | |
| preset = QUALITY_PRESETS.get(quality, QUALITY_PRESETS["High"]) | |
| # Step 4: Generate 2D image (FLUX) | |
| update_progress(0.1, f"Generating 2D image (FLUX {preset.flux_steps} steps)...") | |
| image_path = self.flux.generate(prompt, preset, self.temp_dir) | |
| # Step 5: Generate 3D model (Hunyuan3D) | |
| update_progress(0.5, f"Converting to 3D (Hunyuan3D {preset.hunyuan_steps} steps)...") | |
| glb_path = self.hunyuan.generate(image_path, preset, self.temp_dir) | |
| # Step 6: Validate GLB | |
| update_progress(0.8, "Validating 3D model...") | |
| is_valid, validation_msg = self.validator.validate_glb(glb_path) | |
| if not is_valid: | |
| raise ValueError(f"GLB validation failed: {validation_msg}") | |
| # Step 7: Blender optimization (if available) | |
| update_progress(0.85, "Optimizing for game engine...") | |
| raw_path = self.output_dir / f"asset_raw_{int(time.time())}.glb" | |
| import shutil | |
| shutil.copy(glb_path, raw_path) | |
| optimized = False | |
| optimization_msg = "" | |
| if self.blender.is_available(): | |
| optimized_path = self.output_dir / f"asset_optimized_{int(time.time())}.glb" | |
| script_path = self.script_dir / "blender_optimize.py" | |
| success, message = self.blender.optimize(raw_path, optimized_path, script_path) | |
| if success: | |
| final_path = optimized_path | |
| optimized = True | |
| optimization_msg = f"\n✅ {message}" | |
| else: | |
| final_path = raw_path | |
| optimization_msg = f"\n⚠️ Optimization skipped: {message}" | |
| else: | |
| final_path = raw_path | |
| optimization_msg = "\n⚠️ Blender not available, using raw output" | |
| # Step 8: Save to cache | |
| update_progress(0.95, "Saving to cache...") | |
| self.cache.save_to_cache(prompt, quality, final_path) | |
| # Step 9: Cleanup temp files | |
| if image_path.exists(): | |
| image_path.unlink() | |
| # Step 10: Create result | |
| elapsed = time.time() - start_time | |
| metadata = AssetMetadata( | |
| prompt=prompt, | |
| quality=quality, | |
| flux_steps=preset.flux_steps, | |
| hunyuan_steps=preset.hunyuan_steps, | |
| file_size_mb=final_path.stat().st_size / 1e6, | |
| generation_time_s=elapsed, | |
| optimized=optimized, | |
| rigged=False | |
| ) | |
| status_msg = f"✨ Generated in {elapsed:.1f}s" | |
| status_msg += f"\n📊 {preset.flux_steps} FLUX steps, {preset.hunyuan_steps} Hunyuan3D steps" | |
| status_msg += optimization_msg | |
| update_progress(1.0, "Complete!") | |
| return GenerationResult( | |
| glb_path=final_path, | |
| status_message=status_msg, | |
| metadata=metadata, | |
| cached=False | |
| ) | |
| except Exception as e: | |
| print(f"[Pipeline] Error: {e}") | |
| raise | |