feat: add tool to get screenshot of viewport

This commit is contained in:
Stephen Crowe 2025-06-07 09:58:07 -05:00
parent 972096e9dc
commit 093c756a9f
2 changed files with 100 additions and 0 deletions

View File

@ -197,6 +197,7 @@ class BlenderMCPServer:
handlers = {
"get_scene_info": self.get_scene_info,
"get_object_info": self.get_object_info,
"get_viewport_screenshot": self.get_viewport_screenshot,
"execute_code": self.execute_code,
"get_polyhaven_status": self.get_polyhaven_status,
"get_hyper3d_status": self.get_hyper3d_status,
@ -330,6 +331,63 @@ class BlenderMCPServer:
return obj_info
def get_viewport_screenshot(self, max_size=800, filepath=None, format="png"):
"""
Capture a screenshot of the current 3D viewport and save it to the specified path.
Parameters:
- max_size: Maximum size in pixels for the largest dimension of the image
- filepath: Path where to save the screenshot file
- format: Image format (png, jpg, etc.)
Returns success/error status
"""
try:
if not filepath:
return {"error": "No filepath provided"}
# Find the active 3D viewport
area = None
for a in bpy.context.screen.areas:
if a.type == 'VIEW_3D':
area = a
break
if not area:
return {"error": "No 3D viewport found"}
# Take screenshot with proper context override
with bpy.context.temp_override(area=area):
bpy.ops.screen.screenshot_area(filepath=filepath)
# Load and resize if needed
img = bpy.data.images.load(filepath)
width, height = img.size
if max(width, height) > max_size:
scale = max_size / max(width, height)
new_width = int(width * scale)
new_height = int(height * scale)
img.scale(new_width, new_height)
# Set format and save
img.file_format = format.upper()
img.save()
width, height = new_width, new_height
# Cleanup Blender image data
bpy.data.images.remove(img)
return {
"success": True,
"width": width,
"height": height,
"filepath": filepath
}
except Exception as e:
return {"error": str(e)}
def execute_code(self, code):
"""Execute arbitrary Blender Python code"""
# This is powerful but potentially dangerous - use with caution

View File

@ -4,6 +4,7 @@ import socket
import json
import asyncio
import logging
import tempfile
from dataclasses import dataclass
from contextlib import asynccontextmanager
from typing import AsyncIterator, Dict, Any, List
@ -266,6 +267,47 @@ def get_object_info(ctx: Context, object_name: str) -> str:
logger.error(f"Error getting object info from Blender: {str(e)}")
return f"Error getting object info: {str(e)}"
@mcp.tool()
def get_viewport_screenshot(ctx: Context, max_size: int = 800) -> Image:
"""
Capture a screenshot of the current Blender 3D viewport.
Parameters:
- max_size: Maximum size in pixels for the largest dimension (default: 800)
Returns the screenshot as an Image.
"""
try:
blender = get_blender_connection()
# Create temp file path
temp_dir = tempfile.gettempdir()
temp_path = os.path.join(temp_dir, f"blender_screenshot_{os.getpid()}.png")
result = blender.send_command("get_viewport_screenshot", {
"max_size": max_size,
"filepath": temp_path,
"format": "png"
})
if "error" in result:
raise Exception(result["error"])
if not os.path.exists(temp_path):
raise Exception("Screenshot file was not created")
# Read the file
with open(temp_path, 'rb') as f:
image_bytes = f.read()
# Delete the temp file
os.remove(temp_path)
return Image(data=image_bytes, format="png")
except Exception as e:
logger.error(f"Error capturing screenshot: {str(e)}")
raise Exception(f"Screenshot failed: {str(e)}")
@mcp.tool()