-
Notifications
You must be signed in to change notification settings - Fork 1.4k
It seems feasible to bind 3D animation skeletons in Blender. #117
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
It seems feasible to bind 3D animation skeletons in Blender. #117
Conversation
This analysis concludes that the BlenderMCP project is sufficiently complete to enable 3D animation skeleton binding in Blender when accessed via a compatible interface like Cursor. The core mechanism relies on the ability to execute Python scripts within Blender's environment. Given Blender's comprehensive Python API (`bpy`) for armature creation, mesh parenting, weight painting, and other rigging operations, any skeleton binding task that can be scripted in Blender can be triggered and performed. The analysis involved: 1. Reviewing the BlenderMCP codebase (`server.py`, `README.md`) to understand its capabilities. 2. Confirming through Blender's API documentation that `bpy` supports all necessary operations for skeleton binding. 3. Developing an example Python script that performs a basic skeleton binding (creating a mesh and armature, parenting with automatic weights, and posing a bone). 4. Detailing how this script would be executed to achieve the binding in Blender. The findings confirm the project's suitability for the described task, with the understanding that the specific binding logic must be provided in the Python scripts being executed.
WalkthroughA new Python script for Blender automates the setup of a simple 3D scene. It clears the current scene, creates a cylinder mesh, constructs a three-bone armature, binds the mesh to the armature with automatic weights, and rotates the middle bone. The script is designed for direct execution within Blender. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Blender
User->>Blender: Run skeleton_binding_script.py
Blender->>Blender: clear_scene()
Blender->>Blender: create_cylinder()
Blender->>Blender: create_armature()
Blender->>Blender: Parent cylinder to armature (auto weights)
Blender->>Blender: Switch to Pose Mode, select middle bone
Blender->>Blender: Rotate middle bone
Blender-->>User: Print completion message
Poem
✨ Finishing Touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
PR Reviewer Guide 🔍Here are some key observations to aid the review process:
|
PR Code Suggestions ✨Explore these optional code suggestions:
|
|||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (6)
skeleton_binding_script.py (6)
1-1: Add module docstring and address import considerations.The script is missing a module-level docstring to describe its purpose and usage. Also, note that the
bpyimport will only work within Blender's Python environment.+""" +Blender script for automated 3D skeleton binding demonstration. + +This script creates a cylinder mesh, constructs a three-bone armature, +binds the mesh to the armature with automatic weights, and demonstrates +bone rotation. Designed for execution within Blender's Python environment. + +Usage: + - Run from Blender's Text Editor, or + - Execute via command line: blender --background --python skeleton_binding_script.py +""" import bpy🧰 Tools
🪛 Pylint (3.3.7)
[convention] 1-1: Missing module docstring
(C0114)
[error] 1-1: Unable to import 'bpy'
(E0401)
3-29: Refactor repetitive cleanup code for better maintainability.The
clear_scene()function contains repetitive code for removing orphaned data blocks. This violates the DRY principle and triggers the static analysis warning about too many branches.def clear_scene(): """Clears all objects from the current scene.""" bpy.ops.object.select_all(action='SELECT') bpy.ops.object.delete(use_global=False) - # Also remove any orphaned data blocks - for block in bpy.data.meshes: - if block.users == 0: - bpy.data.meshes.remove(block) - for block in bpy.data.armatures: - if block.users == 0: - bpy.data.armatures.remove(block) - for block in bpy.data.materials: - if block.users == 0: - bpy.data.materials.remove(block) - for block in bpy.data.textures: - if block.users == 0: - bpy.data.textures.remove(block) - for block in bpy.data.images: - if block.users == 0: - bpy.data.images.remove(block) - for block in bpy.data.lights: - if block.users == 0: - bpy.data.lights.remove(block) - for block in bpy.data.cameras: - if block.users == 0: - bpy.data.cameras.remove(block) + # Remove orphaned data blocks + data_collections = [ + bpy.data.meshes, bpy.data.armatures, bpy.data.materials, + bpy.data.textures, bpy.data.images, bpy.data.lights, bpy.data.cameras + ] + + for collection in data_collections: + for block in collection: + if block.users == 0: + collection.remove(block)🧰 Tools
🪛 Pylint (3.3.7)
[refactor] 3-3: Too many branches (14/12)
(R0912)
47-52: Remove unnecessary bone cleanup logic.The bone name cleanup logic is redundant since
clear_scene()already removes all objects and orphaned data blocks, ensuring no pre-existing bones with these names exist.- # Ensure unique bone names by removing any existing bones with the same names - # (though clear_scene should prevent this) - bone_names = ["Bone.001", "Bone.002", "Bone.003"] - for name in bone_names: - if name in armature.edit_bones: - armature.edit_bones.remove(armature.edit_bones[name]) -
90-90: Fix line length violation.Line 90 exceeds the 100-character limit flagged by static analysis.
- bpy.context.view_layer.objects.active = armature_obj # Armature should be the active object for parenting + # Armature should be the active object for parenting + bpy.context.view_layer.objects.active = armature_obj🧰 Tools
🪛 Pylint (3.3.7)
[convention] 90-90: Line too long (109/100)
(C0301)
109-109: Use a named constant for rotation angle.The magic number
0.785398should be replaced with a named constant or calculated value for better readability and maintainability.+import math + +# Constants +ROTATION_ANGLE_45_DEG = math.pi / 4 # 45 degrees in radiansThen update the rotation call:
- bpy.ops.transform.rotate(value=0.785398, # 45 degrees in radians + bpy.ops.transform.rotate(value=ROTATION_ANGLE_45_DEG,
118-135: Clean up commented verification code.The extensive commented-out code at the end should be removed or properly implemented as optional functionality.
if __name__ == "__main__": main() - # To make the script runnable from Blender's text editor: - # If you want to run this script directly from Blender's Text Editor, - # you might need to register it if it contains operators or panels. - # For this simple script, direct execution should work fine. - # To ensure it runs when pasted, you can also just call main() directly. - # For example: - # main() - # However, the if __name__ == "__main__": block is standard practice. - # If running via command line: blender --background --python your_script_name.py - # The print statement at the end will show up in the console. - - # For verification, let's also set the active object to the cylinder - # and the mode to OBJECT to make it easier to see the result if run in Blender UI. - # bpy.ops.object.mode_set(mode='OBJECT') - # bpy.context.view_layer.objects.active = bpy.data.objects.get("Cylinder") - # if bpy.context.view_layer.objects.active: - # bpy.context.view_layer.objects.active.select_set(True)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
skeleton_binding_script.py(1 hunks)
🧰 Additional context used
🪛 Pylint (3.3.7)
skeleton_binding_script.py
[convention] 90-90: Line too long (109/100)
(C0301)
[convention] 1-1: Missing module docstring
(C0114)
[error] 1-1: Unable to import 'bpy'
(E0401)
[refactor] 3-3: Too many branches (14/12)
(R0912)
🔇 Additional comments (1)
skeleton_binding_script.py (1)
74-114: Well-structured workflow with good documentation.The main function follows a logical step-by-step approach and includes helpful comments explaining each phase of the skeleton binding process. The workflow correctly demonstrates the key concepts mentioned in the PR objectives.
🧰 Tools
🪛 Pylint (3.3.7)
[convention] 90-90: Line too long (109/100)
(C0301)
| armature_obj.pose.bones["Bone.002"].bone.select = True | ||
| bpy.context.object.data.bones.active = armature_obj.data.bones["Bone.002"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add error handling for bone selection.
The bone selection assumes "Bone.002" exists but doesn't handle the case where it might not be found, which could cause a KeyError.
- armature_obj.pose.bones["Bone.002"].bone.select = True
- bpy.context.object.data.bones.active = armature_obj.data.bones["Bone.002"]
+ try:
+ target_bone = armature_obj.pose.bones["Bone.002"]
+ target_bone.bone.select = True
+ bpy.context.object.data.bones.active = armature_obj.data.bones["Bone.002"]
+ except KeyError:
+ print("Error: Bone.002 not found in armature")
+ return📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| armature_obj.pose.bones["Bone.002"].bone.select = True | |
| bpy.context.object.data.bones.active = armature_obj.data.bones["Bone.002"] | |
| try: | |
| target_bone = armature_obj.pose.bones["Bone.002"] | |
| target_bone.bone.select = True | |
| bpy.context.object.data.bones.active = armature_obj.data.bones["Bone.002"] | |
| except KeyError: | |
| print("Error: Bone.002 not found in armature") | |
| return |
🤖 Prompt for AI Agents
In skeleton_binding_script.py around lines 103 to 104, the code assumes the bone
"Bone.002" exists without checking, which can cause a KeyError if it is missing.
Add error handling by verifying if "Bone.002" is present in
armature_obj.pose.bones and armature_obj.data.bones before attempting to select
or activate it. If the bone is not found, handle the situation gracefully, such
as by logging an error or skipping the selection step.
| # 4. Parent the cylinder to the armature with automatic weights | ||
| # Ensure cylinder is active and armature is selected | ||
| bpy.ops.object.select_all(action='DESELECT') | ||
| cylinder_obj.select_set(True) | ||
| armature_obj.select_set(True) | ||
| bpy.context.view_layer.objects.active = armature_obj # Armature should be the active object for parenting | ||
|
|
||
| bpy.ops.object.parent_set(type='ARMATURE_AUTO') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add error handling for critical parenting operation.
The automatic parenting operation is critical for skeleton binding but lacks error handling. If this operation fails, the subsequent pose manipulation will not work as expected.
# 4. Parent the cylinder to the armature with automatic weights
# Ensure cylinder is active and armature is selected
bpy.ops.object.select_all(action='DESELECT')
cylinder_obj.select_set(True)
armature_obj.select_set(True)
bpy.context.view_layer.objects.active = armature_obj # Armature should be the active object for parenting
- bpy.ops.object.parent_set(type='ARMATURE_AUTO')
+ try:
+ bpy.ops.object.parent_set(type='ARMATURE_AUTO')
+ print("Successfully parented cylinder to armature with automatic weights")
+ except Exception as e:
+ print(f"Failed to parent cylinder to armature: {e}")
+ return📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| # 4. Parent the cylinder to the armature with automatic weights | |
| # Ensure cylinder is active and armature is selected | |
| bpy.ops.object.select_all(action='DESELECT') | |
| cylinder_obj.select_set(True) | |
| armature_obj.select_set(True) | |
| bpy.context.view_layer.objects.active = armature_obj # Armature should be the active object for parenting | |
| bpy.ops.object.parent_set(type='ARMATURE_AUTO') | |
| # 4. Parent the cylinder to the armature with automatic weights | |
| # Ensure cylinder is active and armature is selected | |
| bpy.ops.object.select_all(action='DESELECT') | |
| cylinder_obj.select_set(True) | |
| armature_obj.select_set(True) | |
| bpy.context.view_layer.objects.active = armature_obj # Armature should be the active object for parenting | |
| try: | |
| bpy.ops.object.parent_set(type='ARMATURE_AUTO') | |
| print("Successfully parented cylinder to armature with automatic weights") | |
| except Exception as e: | |
| print(f"Failed to parent cylinder to armature: {e}") | |
| return |
🧰 Tools
🪛 Pylint (3.3.7)
[convention] 90-90: Line too long (109/100)
(C0301)
🤖 Prompt for AI Agents
In skeleton_binding_script.py around lines 85 to 92, the automatic parenting
operation using bpy.ops.object.parent_set(type='ARMATURE_AUTO') lacks error
handling. Wrap this operation in a try-except block to catch any exceptions, log
or print an error message if the parenting fails, and handle the failure
gracefully to prevent subsequent pose manipulation errors.
|
Hi, could you provide an extensible way of doing this? Looks promising |
User description
This analysis concludes that the BlenderMCP project is sufficiently complete to enable 3D animation skeleton binding in Blender when accessed via a compatible interface like Cursor.
The core mechanism relies on the ability to execute Python scripts within Blender's environment. Given Blender's comprehensive Python API (
bpy) for armature creation, mesh parenting, weight painting, and other rigging operations, any skeleton binding task that can be scripted in Blender can be triggered and performed.The analysis involved:
server.py,README.md) to understand its capabilities.bpysupports all necessary operations for skeleton binding.The findings confirm the project's suitability for the described task, with the understanding that the specific binding logic must be provided in the Python scripts being executed.
PR Type
enhancement, documentation
Description
Add example Python script for skeleton binding in Blender
Script demonstrates mesh creation, armature setup, and bone posing
Provides clear, step-by-step comments for educational use
Changes walkthrough 📝
skeleton_binding_script.py
Add comprehensive Blender script for skeleton binding demonstrationskeleton_binding_script.py
armature setup
mode
Summary by CodeRabbit