Create 3D objects using the Beaver3D model service in Blender#102
Create 3D objects using the Beaver3D model service in Blender#102cuericlee wants to merge 2 commits into
Conversation
WalkthroughThis update introduces full integration with the Beaver3D 3D model generation service into the BlenderMCP addon and its server. The addon now supports generating 3D models from text prompts or image URLs via the Beaver3D API, with task caching to avoid redundant requests. The integration includes synchronous task monitoring, result downloading, and importing models into Blender in GLB or USD formats, with robust file handling. The Blender UI and scene properties are updated to enable Beaver3D and input API keys. On the server side, a new tool is added to invoke this feature, handling responses and errors appropriately. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant BlenderMCP Server
participant Blender Addon
participant Beaver3D API
User->>BlenderMCP Server: Request generate_beaver3d_from_text_or_image (text/image)
BlenderMCP Server->>Blender Addon: Send command with params
Blender Addon->>Beaver3D API: Submit generation request (text/image)
Beaver3D API-->>Blender Addon: Return task ID
loop Poll until complete
Blender Addon->>Beaver3D API: Check task status
Beaver3D API-->>Blender Addon: Status (pending/completed)
end
Blender Addon->>Beaver3D API: Download result files
Blender Addon->>Blender Addon: Import model (GLB/USD)
Blender Addon-->>BlenderMCP Server: Return success with task ID
BlenderMCP Server-->>User: Return result/status
Possibly related PRs
Suggested labels
Poem
Tip ⚡💬 Agentic Chat (Pro Plan, General Availability)
✨ Finishing Touches
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
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.
Actionable comments posted: 2
🧹 Nitpick comments (5)
addon.py (5)
1186-1329: Apply position & scale to the imported object.
You acceptpositionandscalebut never apply them to the newly imported mesh. After importing, you might translate and scale the object accordingly.def load_model_into_scene(task_id, status, result_path): ... # Suppose we retrieve mesh_obj from self._clean_imported_glb/_clean_imported_usd mesh_obj.location = position mesh_obj.scale = scale🧰 Tools
🪛 Ruff (0.8.2)
1281-1281: f-string without any placeholders
Remove extraneous
fprefix(F541)
1281-1281: Remove extraneous f-string prefix.
This line is an f-string with no placeholders.- print(f"Model imported successfully") + print("Model imported successfully")🧰 Tools
🪛 Ruff (0.8.2)
1281-1281: f-string without any placeholders
Remove extraneous
fprefix(F541)
1485-1487: Combine nested if statements.
You can consolidate these checks to enhance readability.-if node.type == 'TEX_IMAGE' and node.image: - if not os.path.exists(node.image.filepath): - ... +if node.type == 'TEX_IMAGE' and node.image and not os.path.exists(node.image.filepath): + ...🧰 Tools
🪛 Ruff (0.8.2)
1485-1487: Use a single
ifstatement instead of nestedifstatements(SIM102)
1632-1638: Remove duplicated imports.
These modules appear to be re-imported here, but they’re already imported above.-import asyncio -import os -import time -import json -import requests -import shutil import zipfile from pathlib import Path🧰 Tools
🪛 Ruff (0.8.2)
1632-1632:
asyncioimported but unusedRemove unused import:
asyncio(F401)
1633-1633: Redefinition of unused
osfrom line 12Remove definition:
os(F811)
1634-1634: Redefinition of unused
timefrom line 8Remove definition:
time(F811)
1635-1635: Redefinition of unused
jsonfrom line 5Remove definition:
json(F811)
1636-1636: Redefinition of unused
requestsfrom line 9Remove definition:
requests(F811)
1638-1638: Redefinition of unused
shutilfrom line 13Remove definition:
shutil(F811)
1738-1738: Avoid re-importing asyncio.
Since you’ve already importedasyncioat the file level, remove this function-level import.- import asyncio🧰 Tools
🪛 Ruff (0.8.2)
1738-1738: Redefinition of unused
asynciofrom line 1632Remove definition:
asyncio(F811)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
addon.py(8 hunks)src/blender_mcp/server.py(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/blender_mcp/server.py (1)
addon.py (1)
generate_beaver3d_from_text_or_image(1186-1329)
🪛 Ruff (0.8.2)
src/blender_mcp/server.py
532-532: Do not use mutable data structures for argument defaults
Replace with None; initialize within function
(B006)
533-533: Do not use mutable data structures for argument defaults
Replace with None; initialize within function
(B006)
addon.py
1281-1281: f-string without any placeholders
Remove extraneous f prefix
(F541)
1485-1487: Use a single if statement instead of nested if statements
(SIM102)
1632-1632: asyncio imported but unused
Remove unused import: asyncio
(F401)
1633-1633: Redefinition of unused os from line 12
Remove definition: os
(F811)
1634-1634: Redefinition of unused time from line 8
Remove definition: time
(F811)
1635-1635: Redefinition of unused json from line 5
Remove definition: json
(F811)
1636-1636: Redefinition of unused requests from line 9
Remove definition: requests
(F811)
1638-1638: Redefinition of unused shutil from line 13
Remove definition: shutil
(F811)
1738-1738: Redefinition of unused asyncio from line 1632
Remove definition: asyncio
(F811)
🔇 Additional comments (3)
addon.py (3)
227-233: Looks consistent with existing feature-flag pattern.
Registering Beaver3D handlers only whenblendermcp_use_beaver3dis enabled aligns well with how Hyper3D and PolyHaven are handled.
1337-1353: Neat fallback logic for WebP textures.
The try/except block correctly handles unsupported extensions by retrying the import with adjusted parameters.
1413-1496:Details
✅ Verification successful
Solid USD import approach.
The code handles multi-object imports, texture path updates, and hierarchical empties well. Consider verifying that all users have USD import support.
🏁 Script executed:
#!/bin/bash # Checking if USD import add-on is enabled or available in Blender (some distributions might exclude it). rg -A 3 "usd_import"Length of output: 212
USD Import Handling Verified
The updated code robustly manages USD imports by effectively handling multi-object scenarios, updating texture paths, and preserving hierarchical empties. The verification confirms that the code indeed invokesbpy.ops.wm.usd_importas expected. Please ensure that users’ Blender environments have the USD import add-on enabled, as this is critical for the feature to work correctly.🧰 Tools
🪛 Ruff (0.8.2)
1485-1487: Use a single
ifstatement instead of nestedifstatements(SIM102)
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (4)
addon.py (4)
37-39: Cache dictionaries are fine.
Storing task IDs in_image_url_cacheand_text_prompt_cacheis straightforward. If you anticipate a large or long-lived cache, consider a strategy for eviction or bounding to prevent excessive memory usage.
1281-1281: Remove extraneous f-string.
There are no placeholders in this string, so thefprefix is unnecessary.- print(f"Model imported successfully") + print("Model imported successfully")🧰 Tools
🪛 Ruff (0.8.2)
1281-1281: f-string without any placeholders
Remove extraneous
fprefix(F541)
1485-1487: Consider consolidating nested if statements.
Static analysis suggests using a single compound condition to reduce nesting. For example:- if node.type == 'TEX_IMAGE' and node.image: - if not os.path.exists(node.image.filepath): - ... + if node.type == 'TEX_IMAGE' and node.image and not os.path.exists(node.image.filepath): + ...🧰 Tools
🪛 Ruff (0.8.2)
1485-1487: Use a single
ifstatement instead of nestedifstatements(SIM102)
1632-1638: Unused or redefined imports.
These lines introduce duplicate imports (asyncio,os,time,json,requests,shutil) which are already defined or not used. Removing them helps avoid overshadowing and keeps the code clean.- import asyncio - import os - import time - import json - import requests - import shutilAlso applies to: 1738-1738
🧰 Tools
🪛 Ruff (0.8.2)
1632-1632:
asyncioimported but unusedRemove unused import:
asyncio(F401)
1633-1633: Redefinition of unused
osfrom line 12Remove definition:
os(F811)
1634-1634: Redefinition of unused
timefrom line 8Remove definition:
time(F811)
1635-1635: Redefinition of unused
jsonfrom line 5Remove definition:
json(F811)
1636-1636: Redefinition of unused
requestsfrom line 9Remove definition:
requests(F811)
1638-1638: Redefinition of unused
shutilfrom line 13Remove definition:
shutil(F811)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
addon.py(8 hunks)src/blender_mcp/server.py(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/blender_mcp/server.py (1)
addon.py (1)
generate_beaver3d_from_text_or_image(1186-1329)
addon.py (1)
src/blender_mcp/server.py (1)
generate_beaver3d_from_text_or_image(528-568)
🪛 Ruff (0.8.2)
src/blender_mcp/server.py
532-532: Do not use mutable data structures for argument defaults
Replace with None; initialize within function
(B006)
533-533: Do not use mutable data structures for argument defaults
Replace with None; initialize within function
(B006)
addon.py
1281-1281: f-string without any placeholders
Remove extraneous f prefix
(F541)
1485-1487: Use a single if statement instead of nested if statements
(SIM102)
1632-1632: asyncio imported but unused
Remove unused import: asyncio
(F401)
1633-1633: Redefinition of unused os from line 12
Remove definition: os
(F811)
1634-1634: Redefinition of unused time from line 8
Remove definition: time
(F811)
1635-1635: Redefinition of unused json from line 5
Remove definition: json
(F811)
1636-1636: Redefinition of unused requests from line 9
Remove definition: requests
(F811)
1638-1638: Redefinition of unused shutil from line 13
Remove definition: shutil
(F811)
1738-1738: Redefinition of unused asyncio from line 1632
Remove definition: asyncio
(F811)
🔇 Additional comments (3)
src/blender_mcp/server.py (1)
527-569: Overall logic looks good.
The function robustly checks for required parameters, invokes Blender’s command, and handles both success and error paths. Good job on returning human-readable messages.🧰 Tools
🪛 Ruff (0.8.2)
532-532: Do not use mutable data structures for argument defaults
Replace with
None; initialize within function(B006)
533-533: Do not use mutable data structures for argument defaults
Replace with
None; initialize within function(B006)
addon.py (2)
227-233: Good conditional inclusion for Beaver3D handlers.
You correctly restrict registration of the Beaver3D command to only when the user has enabled Beaver3D integration.
1976-1988: Beaver3D scene properties look solid.
These new properties align with your existing approach for enabling features. Great job integrating the Beaver3D config into Blender’s scene properties.
|
Prioritize obtaining the API Key from environment variables rather than from properties. |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
addon.py (2)
1624-1628: Remove duplicate imports to silence Ruff F811 warnings
os,requests, andshutilwere already imported at the top of the file (lines 12–14).
Re‑importing them here shadows the originals and triggers Ruff’s F811 (“re‑definition of unused name”) warning.-import os -import requests -import zipfile -import shutil -from pathlib import Path +from pathlib import Path # keep only the new dependency(This also keeps
zipfile, which is genuinely new in this block.)
Cleaning these duplicates avoids unnecessary namespace noise and eliminates the static‑analysis errors.🧰 Tools
🪛 Ruff (0.8.2)
1624-1624: Redefinition of unused
osfrom line 12Remove definition:
os(F811)
1625-1625: Redefinition of unused
requestsfrom line 9Remove definition:
requests(F811)
1627-1627: Redefinition of unused
shutilfrom line 13Remove definition:
shutil(F811)
1671-1679: Large ZIP download loads entire file into memory
requests.get(file_url)withoutstream=Truedownloads the whole archive into RAM before writing it to disk:response = requests.get(file_url) with open(zip_path, "wb") as f: f.write(response.content)For high‑poly Beaver3D models the ZIP can exceed hundreds of MB, risking memory spikes or even crashes.
Stream it in chunks instead:
-response = requests.get(file_url) -with open(zip_path, "wb") as f: - f.write(response.content) +response = requests.get(file_url, stream=True, timeout=30) +response.raise_for_status() +with open(zip_path, "wb") as f: + for chunk in response.iter_content(chunk_size=8192): + if chunk: + f.write(chunk)This is more memory‑efficient and adds basic error handling.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
addon.py(8 hunks)
🧰 Additional context used
🧠 Learnings (1)
addon.py (1)
Learnt from: cuericlee
PR: ahujasid/blender-mcp#102
File: addon.py:37-39
Timestamp: 2025-04-15T08:11:07.292Z
Learning: Thread safety for caching dictionaries in Blender addons (like `_image_url_cache` and `_text_prompt_cache`) isn't necessary since Blender SDK doesn't support real asynchronous calls and typically runs in a single-threaded execution model.
🧬 Code Graph Analysis (1)
addon.py (1)
src/blender_mcp/server.py (1)
generate_beaver3d_from_text_or_image(528-568)
🪛 Ruff (0.8.2)
addon.py
1624-1624: Redefinition of unused os from line 12
Remove definition: os
(F811)
1625-1625: Redefinition of unused requests from line 9
Remove definition: requests
(F811)
1627-1627: Redefinition of unused shutil from line 13
Remove definition: shutil
(F811)
🔇 Additional comments (2)
addon.py (2)
1186-1221: Cache dictionaries may grow unbounded
self._image_url_cacheandself._text_prompt_cachenever evict entries, so a hobbyist creating many prompts in one session could leak memory.If you expect heavy usage consider an eviction strategy—e.g.
functools.lru_cache‑style max‑size or timestamp pruning.
1329-1344: Great resilience against unsupported WebP texturesCatching
EXT_texture_webpand retrying with safe import flags is a pragmatic workaround that prevents hard failures when the extension is absent.
Nice touch!
|
@ahujasid fixed all issue found in review and unit test is passed, pls help on merge, thanks! |
|
@ahujasid would you pls help on merge? |
|
@cuericlee I couldn't find Beaver3D online, is there someplace I can look it up? |
User description
Create 3D objects using the Beaver3D model service in Blender, an additional alternative to Gen3D.
PR Type
Enhancement
Description
Added Beaver3D integration for 3D model generation in Blender.
Implemented caching for task IDs to optimize repeated requests.
Enhanced error handling for importing GLB and USD files.
Updated Blender UI to include Beaver3D configuration options.
How to use this gen3d addon, apply the API KEY of beaver3d service and send following prompt
Changes walkthrough 📝
addon.py
Added Beaver3D integration and UI updates.addon.py
server.py
Added server-side Beaver3D generation tool.src/blender_mcp/server.py
Summary by CodeRabbit
New Features
Bug Fixes