import json import re import anthropic import gradio as gr import modal import base64 import os import tempfile # Modal function to run the 3D generation pipeline text_to_3d = modal.Function.from_name("flux-trellis-gguf-3d-pipeline", "text_to_3d") def generate_3d_prompts(player_bio: str, num_assets: int = 10) -> list: """ Analyze player bio using Claude Sonnet to generate 3D asset prompts. Args: player_bio (str): The player's biographical information num_assets (int): Number of assets to generate (default: 10) Returns: list: List of 3D generation prompts """ client = anthropic.Anthropic() system_prompt = f"""You are an expert 3D scene designer for video games. Your task is to analyze a player's biographical information and identify specific 3D assets that would create a comfortable, personalized environment for them. Based on the player's bio, generate a list of exactly {num_assets} specific 3D asset prompts that would compose a scene tailored to their interests, personality, and background. Objects should be deeply personal and couldn't exist in any generic asset library. You should combine multiple elements into a single prompt if they are closely related. For instance, if the player loves both coffee and gaming, you might create a prompt for a "gaming desk with a custom coffee station." Prefer 3d assets, which combine multiple elements instead of single objects like a piece of furniture or a decoration. Each prompt should include: 3d isomorphic, white background. This is for 3D game asset generation. Format your response as a simple JSON array of strings, where each string is a detailed prompt for 3D generation. Focus on objects like furniture, decorations, equipment, or environmental elements. Follow this format: """ response = client.messages.create( model="claude-sonnet-4-20250514", max_tokens=1024, system=system_prompt, messages=[{"role": "user", "content": f"Player bio: {player_bio}"}], ) prompts_text = response.content[0].text # Extract JSON from the response text using regex try: # Find JSON array between ```json ``` markers json_pattern = r"```json\s*(\[.*?\])\s*```" match = re.search(json_pattern, prompts_text, re.DOTALL) if match: json_str = match.group(1) return json.loads(json_str) else: # Fallback: try to find any JSON array array_pattern = r"(\[.*?\])" match = re.search(array_pattern, prompts_text, re.DOTALL) if match: return json.loads(match.group(1)) else: return [prompts_text] except (json.JSONDecodeError, ValueError): # Fallback: return the raw text if JSON parsing fails return [prompts_text] def generate_3d_assets(player_bio: str, num_assets: int = 10) -> str: """ Generate 3D assets based on a player's bio for video game scene composition. Args: player_bio (str): The player's biographical information num_assets (int): Number of assets to generate Returns: str: JSON string containing prompts and GLB file data """ try: # Analyze bio with Claude to get 3D prompts prompts = generate_3d_prompts(player_bio, num_assets) # Generate 3D assets using Modal function modal_results = text_to_3d.map(prompts) # Process results and prepare GLB data for API/MCP consumption generated_assets = [] for i, result in enumerate(modal_results): if "glb_file" in result: generated_assets.append( { "prompt": prompts[i], "glb_data": base64.b64encode(result["glb_file"]).decode( "utf-8" ), "size_bytes": len(result["glb_file"]), "asset_id": f"asset_{i+1}", } ) result_dict = { "assets": generated_assets, "total_assets": len(generated_assets), } return json.dumps(result_dict) except Exception as e: error_dict = {"error": str(e), "prompts": [], "assets": [], "total_assets": 0} return json.dumps(error_dict) def generate_3d_assets_with_display(player_bio: str, num_assets: int = 10) -> tuple: """ Generate 3D assets and prepare them for both JSON output and 3D display. Returns: tuple: (json_output, model_paths_dict, num_assets) """ result_json = generate_3d_assets(player_bio, num_assets) result = json.loads(result_json) if "error" in result: return result_json, {}, num_assets # Save GLB files to temporary directory for display temp_dir = tempfile.mkdtemp() model_paths = {} for i, asset in enumerate(result.get("assets", [])): if "glb_data" in asset: # Decode base64 back to bytes and save to file glb_bytes = base64.b64decode(asset["glb_data"]) model_path = os.path.join(temp_dir, f"model_{i+1}.glb") with open(model_path, "wb") as f: f.write(glb_bytes) model_paths[f"model_{i+1}"] = model_path return result_json, model_paths, num_assets # Create a clean Gradio interface using Blocks with gr.Blocks(theme=gr.themes.Monochrome(), title="3D Scene Asset Generator") as demo: gr.Markdown("# đŽ 3D Game Environment Builder đī¸") with gr.Row(): with gr.Column(scale=2): gr.Markdown( """ Transform player personalities into immersive 3D game environments! Create fast personalized 3D environments to a player, by using information from their bio. This tool will craft personalized 3D assets that reflect each player's unique interests, hobbies, and lifestyle, allowing you to build unique scenes for video games. This space is used as a MCP Server, example usage: ```bash mcptools call generate_3d_assets --params '{"player_bio":"Elena is a music producer who [...]"}' https://{gradio-url}:7860/gradio_api/mcp/sse ``` This returns a JSON with the generated 3D assets in GLB format, along with their description. """ ) with gr.Column(scale=1): gr.HTML( """ """ ) gr.Image("images/pipeline.png", label="đ¯ Pipeline Overview", show_label=True, height=200) gr.Markdown( """
Note: This space cannot be directly tested as we didn't want users to enter their own API keys for the various providers used in the pipeline.
However, we provide all the source code and the instructions to build your own and test it at: đ GitHub Repository