Merge branch 'ahujasid:main' into rodin-intergration
This commit is contained in:
commit
648ed244fb
33
README.md
33
README.md
@ -5,6 +5,7 @@ BlenderMCP connects Blender to Claude AI through the Model Context Protocol (MCP
|
||||
## Release notes (1.1.0)
|
||||
|
||||
- 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
|
||||
- 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!
|
||||
@ -33,13 +34,17 @@ The system consists of two main components:
|
||||
- Python 3.10 or newer
|
||||
- uv package manager:
|
||||
|
||||
If you're on Mac, please install uv as
|
||||
**If you're on Mac, please install uv as**
|
||||
```bash
|
||||
brew install uv
|
||||
```
|
||||
On Windows
|
||||
**On Windows**
|
||||
```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/)
|
||||
@ -104,22 +109,15 @@ Once the config file has been set on Claude, and the addon is running on Blender
|
||||
|
||||

|
||||
|
||||
#### Tools
|
||||
#### Capabilities
|
||||
|
||||
- `get_scene_info` - Gets scene information
|
||||
- `get_object_info` - Gets detailed information for a specific object in the scene
|
||||
- `create_primitive` - Create basic primitive objects with optional color
|
||||
- `set_object_property` - Set a single property of an object
|
||||
- `create_object` - Create a new object with detailed parameters
|
||||
- `modify_object` - Modify an existing object's properties
|
||||
- `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
|
||||
- Get scene and object information
|
||||
- Create, delete and modify shapes
|
||||
- Apply or create materials for objects
|
||||
- Execute any Python code in Blender
|
||||
- Download the right models, assets and HDRIs through [Poly Haven](https://polyhaven.com/)
|
||||
- AI generated 3D models through [Hyper3D Rodin](https://hyper3d.ai/)
|
||||
|
||||
To see everything in Poly Haven, [see here](https://polyhaven.com/)
|
||||
|
||||
### 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 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)
|
||||
- "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)
|
||||
- "Make this car red and metallic"
|
||||
- "Create a sphere and place it above the cube"
|
||||
|
||||
120
addon.py
120
addon.py
@ -268,63 +268,81 @@ class BlenderMCPServer:
|
||||
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):
|
||||
"""Create a new object in the scene"""
|
||||
# Deselect all objects
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
try:
|
||||
# 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)
|
||||
elif type == "SPHERE":
|
||||
bpy.ops.mesh.primitive_uv_sphere_add(location=location, rotation=rotation, scale=scale)
|
||||
elif type == "CYLINDER":
|
||||
bpy.ops.mesh.primitive_cylinder_add(location=location, rotation=rotation, scale=scale)
|
||||
elif type == "PLANE":
|
||||
bpy.ops.mesh.primitive_plane_add(location=location, rotation=rotation, scale=scale)
|
||||
elif type == "CONE":
|
||||
bpy.ops.mesh.primitive_cone_add(location=location, rotation=rotation, scale=scale)
|
||||
elif type == "TORUS":
|
||||
bpy.ops.mesh.primitive_torus_add(
|
||||
align=align,
|
||||
location=location,
|
||||
rotation=rotation,
|
||||
major_segments=major_segments,
|
||||
minor_segments=minor_segments,
|
||||
mode=mode,
|
||||
major_radius=major_radius,
|
||||
minor_radius=minor_radius,
|
||||
abso_major_rad=abso_major_rad,
|
||||
abso_minor_rad=abso_minor_rad,
|
||||
generate_uvs=generate_uvs
|
||||
)
|
||||
elif type == "EMPTY":
|
||||
bpy.ops.object.empty_add(location=location, rotation=rotation, scale=scale)
|
||||
elif type == "CAMERA":
|
||||
bpy.ops.object.camera_add(location=location, rotation=rotation)
|
||||
elif type == "LIGHT":
|
||||
bpy.ops.object.light_add(type='POINT', location=location, rotation=rotation, scale=scale)
|
||||
else:
|
||||
raise ValueError(f"Unsupported object type: {type}")
|
||||
# Create the object based on type
|
||||
if type == "CUBE":
|
||||
bpy.ops.mesh.primitive_cube_add(location=location, rotation=rotation, scale=scale)
|
||||
elif type == "SPHERE":
|
||||
bpy.ops.mesh.primitive_uv_sphere_add(location=location, rotation=rotation, scale=scale)
|
||||
elif type == "CYLINDER":
|
||||
bpy.ops.mesh.primitive_cylinder_add(location=location, rotation=rotation, scale=scale)
|
||||
elif type == "PLANE":
|
||||
bpy.ops.mesh.primitive_plane_add(location=location, rotation=rotation, scale=scale)
|
||||
elif type == "CONE":
|
||||
bpy.ops.mesh.primitive_cone_add(location=location, rotation=rotation, scale=scale)
|
||||
elif type == "TORUS":
|
||||
bpy.ops.mesh.primitive_torus_add(
|
||||
align=align,
|
||||
location=location,
|
||||
rotation=rotation,
|
||||
major_segments=major_segments,
|
||||
minor_segments=minor_segments,
|
||||
mode=mode,
|
||||
major_radius=major_radius,
|
||||
minor_radius=minor_radius,
|
||||
abso_major_rad=abso_major_rad,
|
||||
abso_minor_rad=abso_minor_rad,
|
||||
generate_uvs=generate_uvs
|
||||
)
|
||||
elif type == "EMPTY":
|
||||
bpy.ops.object.empty_add(location=location, rotation=rotation, scale=scale)
|
||||
elif type == "CAMERA":
|
||||
bpy.ops.object.camera_add(location=location, rotation=rotation)
|
||||
elif type == "LIGHT":
|
||||
bpy.ops.object.light_add(type='POINT', location=location, rotation=rotation, scale=scale)
|
||||
else:
|
||||
raise ValueError(f"Unsupported object type: {type}")
|
||||
|
||||
# Get the created object
|
||||
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:
|
||||
obj.name = name
|
||||
# Get the active object (which should be our newly created object)
|
||||
obj = bpy.context.view_layer.objects.active
|
||||
|
||||
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 we don't have an active object, something went wrong
|
||||
if obj is None:
|
||||
raise RuntimeError("Failed to create object - no active object")
|
||||
|
||||
if obj.type == "MESH":
|
||||
bounding_box = self._get_aabb(obj)
|
||||
result["world_bounding_box"] = bounding_box
|
||||
# Make sure it's selected
|
||||
obj.select_set(True)
|
||||
|
||||
return result
|
||||
# Rename if name is provided
|
||||
if name:
|
||||
obj.name = name
|
||||
if obj.data:
|
||||
obj.data.name = name
|
||||
|
||||
# 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):
|
||||
"""Modify an existing object in the scene"""
|
||||
|
||||
Loading…
Reference in New Issue
Block a user