Merge branch 'ahujasid:main' into rodin-intergration

This commit is contained in:
ElgoogUdiab 2025-03-17 17:21:31 +08:00 committed by GitHub
commit 648ed244fb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 91 additions and 74 deletions

View File

@ -5,6 +5,7 @@ BlenderMCP connects Blender to Claude AI through the Model Context Protocol (MCP
## Release notes (1.1.0) ## Release notes (1.1.0)
- Added support for Poly Haven assets through their API - Added support for Poly Haven assets through their API
- Added support to prompt 3D models using Hyper3D Rodin
- For newcomers, you can go straight to Installation. For existing users, see the points below - For newcomers, you can go straight to Installation. For existing users, see the points below
- Download the latest addon.py file and replace the older one, then add it to Blender - Download the latest addon.py file and replace the older one, then add it to Blender
- Delete the MCP server from Claude and add it back again, and you should be good to go! - Delete the MCP server from Claude and add it back again, and you should be good to go!
@ -33,13 +34,17 @@ The system consists of two main components:
- Python 3.10 or newer - Python 3.10 or newer
- uv package manager: - uv package manager:
If you're on Mac, please install uv as **If you're on Mac, please install uv as**
```bash ```bash
brew install uv brew install uv
``` ```
On Windows **On Windows**
```bash ```bash
pip install uv powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
```
and then
```bash
set Path=C:\Users\nntra\.local\bin;%Path%
``` ```
Otherwise installation instructions are on their website: [Install uv](https://docs.astral.sh/uv/getting-started/installation/) Otherwise installation instructions are on their website: [Install uv](https://docs.astral.sh/uv/getting-started/installation/)
@ -104,22 +109,15 @@ Once the config file has been set on Claude, and the addon is running on Blender
![BlenderMCP in the sidebar](assets/hammer-icon.png) ![BlenderMCP in the sidebar](assets/hammer-icon.png)
#### Tools #### Capabilities
- `get_scene_info` - Gets scene information - Get scene and object information
- `get_object_info` - Gets detailed information for a specific object in the scene - Create, delete and modify shapes
- `create_primitive` - Create basic primitive objects with optional color - Apply or create materials for objects
- `set_object_property` - Set a single property of an object - Execute any Python code in Blender
- `create_object` - Create a new object with detailed parameters - Download the right models, assets and HDRIs through [Poly Haven](https://polyhaven.com/)
- `modify_object` - Modify an existing object's properties - AI generated 3D models through [Hyper3D Rodin](https://hyper3d.ai/)
- `delete_object` - Remove an object from the scene
- `set_material` - Apply or create materials for objects
- `execute_blender_code` - Run any Python code in Blender
- `get_polyhaven_categories` - Get a list of categories for PolyHaven assets (HDRIs, textures, models)
- `search_polyhaven_assets` - Search for assets on PolyHaven with optional category filtering
- `download_polyhaven_asset` - Download and import a PolyHaven asset into Blender
To see everything in Poly Haven, [see here](https://polyhaven.com/)
### Example Commands ### Example Commands
@ -128,6 +126,7 @@ Here are some examples of what you can ask Claude to do:
- "Create a low poly scene in a dungeon, with a dragon guarding a pot of gold" [Demo](https://www.youtube.com/watch?v=DqgKuLYUv00) - "Create a low poly scene in a dungeon, with a dragon guarding a pot of gold" [Demo](https://www.youtube.com/watch?v=DqgKuLYUv00)
- "Create a beach vibe using HDRIs, textures, and models like rocks and vegetation from Poly Haven" [Demo](https://www.youtube.com/watch?v=I29rn92gkC4) - "Create a beach vibe using HDRIs, textures, and models like rocks and vegetation from Poly Haven" [Demo](https://www.youtube.com/watch?v=I29rn92gkC4)
- Give a reference image, and create a Blender scene out of it [Demo](https://www.youtube.com/watch?v=FDRb03XPiRo) - Give a reference image, and create a Blender scene out of it [Demo](https://www.youtube.com/watch?v=FDRb03XPiRo)
- "Generate a 3D model of a garden gnome through Hyper3D"
- "Get information about the current scene, and make a threejs sketch from it" [Demo](https://www.youtube.com/watch?v=jxbNI5L7AH8) - "Get information about the current scene, and make a threejs sketch from it" [Demo](https://www.youtube.com/watch?v=jxbNI5L7AH8)
- "Make this car red and metallic" - "Make this car red and metallic"
- "Create a sphere and place it above the cube" - "Create a sphere and place it above the cube"

132
addon.py
View File

@ -268,63 +268,81 @@ class BlenderMCPServer:
align="WORLD", major_segments=48, minor_segments=12, mode="MAJOR_MINOR", align="WORLD", major_segments=48, minor_segments=12, mode="MAJOR_MINOR",
major_radius=1.0, minor_radius=0.25, abso_major_rad=1.25, abso_minor_rad=0.75, generate_uvs=True): major_radius=1.0, minor_radius=0.25, abso_major_rad=1.25, abso_minor_rad=0.75, generate_uvs=True):
"""Create a new object in the scene""" """Create a new object in the scene"""
# Deselect all objects try:
bpy.ops.object.select_all(action='DESELECT') # Deselect all objects first
bpy.ops.object.select_all(action='DESELECT')
if type == "CUBE":
bpy.ops.mesh.primitive_cube_add(location=location, rotation=rotation, scale=scale) # Create the object based on type
elif type == "SPHERE": if type == "CUBE":
bpy.ops.mesh.primitive_uv_sphere_add(location=location, rotation=rotation, scale=scale) bpy.ops.mesh.primitive_cube_add(location=location, rotation=rotation, scale=scale)
elif type == "CYLINDER": elif type == "SPHERE":
bpy.ops.mesh.primitive_cylinder_add(location=location, rotation=rotation, scale=scale) bpy.ops.mesh.primitive_uv_sphere_add(location=location, rotation=rotation, scale=scale)
elif type == "PLANE": elif type == "CYLINDER":
bpy.ops.mesh.primitive_plane_add(location=location, rotation=rotation, scale=scale) bpy.ops.mesh.primitive_cylinder_add(location=location, rotation=rotation, scale=scale)
elif type == "CONE": elif type == "PLANE":
bpy.ops.mesh.primitive_cone_add(location=location, rotation=rotation, scale=scale) bpy.ops.mesh.primitive_plane_add(location=location, rotation=rotation, scale=scale)
elif type == "TORUS": elif type == "CONE":
bpy.ops.mesh.primitive_torus_add( bpy.ops.mesh.primitive_cone_add(location=location, rotation=rotation, scale=scale)
align=align, elif type == "TORUS":
location=location, bpy.ops.mesh.primitive_torus_add(
rotation=rotation, align=align,
major_segments=major_segments, location=location,
minor_segments=minor_segments, rotation=rotation,
mode=mode, major_segments=major_segments,
major_radius=major_radius, minor_segments=minor_segments,
minor_radius=minor_radius, mode=mode,
abso_major_rad=abso_major_rad, major_radius=major_radius,
abso_minor_rad=abso_minor_rad, minor_radius=minor_radius,
generate_uvs=generate_uvs abso_major_rad=abso_major_rad,
) abso_minor_rad=abso_minor_rad,
elif type == "EMPTY": generate_uvs=generate_uvs
bpy.ops.object.empty_add(location=location, rotation=rotation, scale=scale) )
elif type == "CAMERA": elif type == "EMPTY":
bpy.ops.object.camera_add(location=location, rotation=rotation) bpy.ops.object.empty_add(location=location, rotation=rotation, scale=scale)
elif type == "LIGHT": elif type == "CAMERA":
bpy.ops.object.light_add(type='POINT', location=location, rotation=rotation, scale=scale) bpy.ops.object.camera_add(location=location, rotation=rotation)
else: elif type == "LIGHT":
raise ValueError(f"Unsupported object type: {type}") bpy.ops.object.light_add(type='POINT', location=location, rotation=rotation, scale=scale)
else:
# Get the created object raise ValueError(f"Unsupported object type: {type}")
bpy.context.view_layer.update()
obj = bpy.context.view_layer.objects.active # Force update the view layer
bpy.context.view_layer.update()
# Rename the object if a name is provided
if name: # Get the active object (which should be our newly created object)
obj.name = name obj = bpy.context.view_layer.objects.active
result = { # If we don't have an active object, something went wrong
"name": obj.name, if obj is None:
"type": obj.type, raise RuntimeError("Failed to create object - no active object")
"location": [obj.location.x, obj.location.y, obj.location.z],
"rotation": [obj.rotation_euler.x, obj.rotation_euler.y, obj.rotation_euler.z], # Make sure it's selected
"scale": [obj.scale.x, obj.scale.y, obj.scale.z], obj.select_set(True)
}
# Rename if name is provided
if obj.type == "MESH": if name:
bounding_box = self._get_aabb(obj) obj.name = name
result["world_bounding_box"] = bounding_box if obj.data:
obj.data.name = name
return result
# Return the object info
result = {
"name": obj.name,
"type": obj.type,
"location": [obj.location.x, obj.location.y, obj.location.z],
"rotation": [obj.rotation_euler.x, obj.rotation_euler.y, obj.rotation_euler.z],
"scale": [obj.scale.x, obj.scale.y, obj.scale.z],
}
if obj.type == "MESH":
bounding_box = self._get_aabb(obj)
result["world_bounding_box"] = bounding_box
return result
except Exception as e:
print(f"Error in create_object: {str(e)}")
traceback.print_exc()
return {"error": str(e)}
def modify_object(self, name, location=None, rotation=None, scale=None, visible=None): def modify_object(self, name, location=None, rotation=None, scale=None, visible=None):
"""Modify an existing object in the scene""" """Modify an existing object in the scene"""