Skip to content

Add Blender material preset tools#41

Merged
Ker102 merged 2 commits into
mainfrom
feature/blender-material-node-presets
May 7, 2026
Merged

Add Blender material preset tools#41
Ker102 merged 2 commits into
mainfrom
feature/blender-material-node-presets

Conversation

@Ker102
Copy link
Copy Markdown
Owner

@Ker102 Ker102 commented May 7, 2026

Summary

  • add deterministic Blender addon commands for material presets and material node graph inspection
  • expose create_material_preset and inspect_material_node_graph as LangChain tools
  • update material guidance, prompt routing, capability inventory, and migration plan status

Validation

  • python -m py_compile desktop/assets/vipermesh-addon.py public/downloads/vipermesh-addon.py
  • npx tsx scripts/test/test-blender-capability-inventory.ts
  • npx tsx scripts/test/test-blender-rag-skill-migration.ts
  • npx tsc --noEmit --incremental false
  • npm run lint (existing baseline-browser-mapping freshness warning only)

Notes

  • Verified Blender 5 Material docs before using current surface_render_method / use_raytrace_refraction with guarded fallbacks.

Summary by CodeRabbit

  • New Features
    • Material preset creation tool and material-node inspector for deterministic Principled BSDF workflows; optional direct assignment to objects and slot handling.
  • Documentation
    • Materials guide updated for Blender 5.x: prefer presets for glass/water/emissive, clarified PBR texture roles, color-space expectations, assignment rules, and a verification checklist for node graphs.
    • Capability inventory and agent guidance updated to include the new material tools.

@github-actions github-actions Bot added documentation Improvements or additions to documentation backend desktop scripts labels May 7, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 7, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

This PR introduces deterministic material preset creation to the ViperMesh Blender MCP addon. Two new commands—create_material_preset and inspect_material_node_graph—enable agents to author Principled BSDF materials with preset-driven configuration, optional texture map wiring, and node graph verification. Changes span addon implementation, TypeScript tool wrappers, agent guidance, and documentation updates.

Changes

Material Preset Tooling & Deterministic Principled BSDF Creation

Layer / File(s) Summary
Material Guide & Preset Usage Documentation
data/tool-guides/materials-guide.md
Guide prioritizes create_material_preset over create_material for Principled BSDF setups. Added special materials table (Glass/Water/Skin/Emissive) with preset names and IOR/roughness ranges. Documented assignment rules, PBR texture map role aliases with color space routing, and verification via inspect_material_node_graph. Updated common mistakes to address preset-first workflow.
Blender Addon: Helper Methods
desktop/assets/vipermesh-addon.py, public/downloads/vipermesh-addon.py
Added _as_rgba() to normalize color inputs, _set_socket_value() to set Principled socket defaults with warning collection, and _load_material_image() to resolve/load image textures with role-based colorspace configuration (sRGB for base_color/emission, Non-Color for others).
Blender Addon: Material Preset & Inspection
desktop/assets/vipermesh-addon.py, public/downloads/vipermesh-addon.py
Implemented create_material_preset(): rebuilds node tree from scratch, applies preset defaults (dielectric/plastic/rubber/fabric/ceramic/metal/glass/emissive), sets socket values (base color, metallic, roughness, alpha, transmission, IOR, emission), optionally wires texture maps from texture_maps with role aliasing (base_color→Base Color, metallic→Metallic, normal→Normal Map, displacement→Displacement), and optionally assigns material via existing assign_material(). Implemented inspect_material_node_graph(): returns node/link summaries, Principled BSDF default values, image textures with filepaths/colorspaces, and assigned objects/slots.
Blender Addon: Command Dispatch
desktop/assets/vipermesh-addon.py, public/downloads/vipermesh-addon.py
Extended execute_command() dispatcher to recognize and route create_material_preset and inspect_material_node_graph commands.
TypeScript: LangChain Tool Wrappers & Registry
lib/ai/agents.ts, lib/orchestration/tool-registry.ts
Added createMaterialPreset tool with preset selection, optional overrides, texture map configuration, and assignment controls. Added inspectMaterialNodeGraph tool for material node graph inspection. Registered both tools in ALL_TOOLS and TOOL_REGISTRY under materials category.
Agent System Prompt & Tool Selection
lib/orchestration/prompts/blender-agent-system.md, lib/orchestration/tool-filter.ts
Updated material tools section with create_material_preset and inspect_material_node_graph entries. Refined "No Duplicate Calls" rule for preset creation. Added both tools to materials category in tool-filter (removed execute_code from materials).
Capability Inventory & Documentation
docs/blender-mcp-capability-inventory.md, lib/blender/capability-inventory.ts, docs/plans/2026-05-05-blender-docs-skill-pipeline.md
Updated capability inventory command count (45→47). Marked ViperMesh addon as "native" for Blender API/manual lookup capability. Updated recommendations for materials/lighting/camera to emphasize deterministic preset creation. Added new command snapshots. Updated plan milestone to reflect preset implementation via addon commands and LangChain tools.
Tests
scripts/test/test-blender-capability-inventory.ts
Updated assertion for "Blender API/manual lookup" capability to verify ViperMesh addon reports as "native" instead of "missing".

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • Ker102/ViperMesh#38: PR #38 establishes the capability inventory framework and audit methodology; this PR builds on that foundation to add material preset tooling and update the inventory accordingly.
  • Ker102/ViperMesh#39: Related updates to the material/node-graph preset workflow and plan alignment; likely touches the same docs/plan entries.

Poem

🐰 Hop, hop, materials take shape—
Presets deterministic, no escape!
Principled BSDF, sockets so true,
Glass and gold and leather too.
Inspect the graph, verify the glow,
ViperMesh commands now steal the show!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 55.56% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add Blender material preset tools' directly summarizes the main change: introducing two new deterministic material preset commands (create_material_preset and inspect_material_node_graph) to the Blender addon.
Description check ✅ Passed The pull request description provides a clear summary, validation steps, and notes, covering the key aspects of the change. However, it does not fully follow the template structure (missing explicit Type of Change, Motivation/Context, Testing details in template sections).
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/blender-material-node-presets

Warning

Review ran into problems

🔥 Problems

Timed out fetching pipeline failures after 30000ms


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@data/tool-guides/materials-guide.md`:
- Around line 54-55: Add blank lines immediately before the "### Special
Materials" heading and after the table block (the lines containing "| Surface |
Key Properties | Notes |") so the heading and its table are separated from
surrounding content; update the "Special Materials" heading and the adjacent
table block in the markdown to ensure an empty line above the "### Special
Materials" line and an empty line after the table rows to resolve MD022/MD058
warnings.

In `@desktop/assets/vipermesh-addon.py`:
- Around line 1204-1210: The _as_rgba helper currently always clamps each
component to [0,1], which wrongly strips HDR emission values; change _as_rgba to
accept an optional clamp=True parameter and only perform the min/max clamping
when clamp is True, leaving value normalization/conversion and alpha handling
intact; then update the emission assignment call socket_values["Emission Color"]
= self._as_rgba(resolved_emission, defaults["color"]) to pass clamp=False so HDR
emission_color values are preserved while other callers keep default clamping.

In `@lib/orchestration/tool-filter.ts`:
- Line 6: Remove "execute_code" from the materials category array in
tool-filter.ts so material-intent requests no longer match that tool; update the
materials entry (materials: ["create_material_preset",
"inspect_material_node_graph", "set_texture"]) by deleting the "execute_code"
token and keep execute_code available only in its appropriate category(s) if
needed (refer to the materials array and any routing logic that reads it to
verify deterministic routing behavior).

In `@lib/orchestration/tool-registry.ts`:
- Around line 100-102: The create_material_preset tool metadata currently lists
only a subset of material fields; update the parameters string for
create_material_preset in lib/orchestration/tool-registry.ts to enumerate all
supported options (e.g., add alpha?: number, emission?: number, emission_color?:
number[], transmission?: number, ior?: number, replacement?: boolean|string,
slot?: string, and any slot/texture control objects) alongside existing keys
(name, preset, object_name, base_color, metallic, roughness, texture_maps) so
the planner sees full capabilities; ensure the syntax matches the existing
metadata format (string describing keys and types) and include the new keys in
the same comma-separated list.

In `@public/downloads/vipermesh-addon.py`:
- Around line 1293-1324: The material setup currently ignores the alpha channel
in base_color unless the separate alpha parameter is provided; update the logic
so that when alpha is None we derive alpha from base_color (resolved_color[3])
and use that to (1) set uses_transparency (treat as transparent if preset_key ==
"glass" OR resolved alpha < 1.0), (2) choose the proper blend/surface render
mode (existing uses_transparency assignment), and (3) populate
socket_values["Alpha"] from resolved_color[3] when no explicit alpha is given;
update the code around variables preset_key, alpha, base_color, resolved_color,
uses_transparency, and socket_values to read resolved_color[3] as the fallback
alpha.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: d20f3196-75ab-45d5-a4bc-3bcb6bad9007

📥 Commits

Reviewing files that changed from the base of the PR and between e8c8a26 and 24d0f97.

📒 Files selected for processing (11)
  • data/tool-guides/materials-guide.md
  • desktop/assets/vipermesh-addon.py
  • docs/blender-mcp-capability-inventory.md
  • docs/plans/2026-05-05-blender-docs-skill-pipeline.md
  • lib/ai/agents.ts
  • lib/blender/capability-inventory.ts
  • lib/orchestration/prompts/blender-agent-system.md
  • lib/orchestration/tool-filter.ts
  • lib/orchestration/tool-registry.ts
  • public/downloads/vipermesh-addon.py
  • scripts/test/test-blender-capability-inventory.ts

Comment on lines +54 to 55
### Special Materials
| Surface | Key Properties | Notes |
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add blank lines around the “Special Materials” heading/table block.

This currently triggers markdownlint warnings (MD022/MD058).

🧹 Suggested formatting fix
 ### Non-Metals (metallic = 0.0)
 | Surface | Color [R,G,B] | Roughness | Notes |
 |---|---|---|---|
 ...
 | Fabric/cloth | [varies] | 0.85–0.95 | Very matte |

 ### Special Materials
+
 | Surface | Key Properties | Notes |
 |---|---|---|
 | Glass | Transmission Weight = 0.9–1.0, IOR = 1.45–1.52, Roughness = 0.0–0.05 | Use `create_material_preset` preset `glass` |
 | Water | Transmission Weight = 0.9, IOR = 1.33, Roughness = 0.02 | Use `create_material_preset` preset `glass` with IOR override |
 | Skin | Subsurface Weight = 0.3, Subsurface Radius = [0.1, 0.05, 0.02] | |
 | Emissive | Emission Color + Emission Strength | Use `create_material_preset` preset `emissive` |
+
 ## BLENDER 5.x SOCKET NAME CHANGES
📝 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.

Suggested change
### Special Materials
| Surface | Key Properties | Notes |
### Special Materials
| Surface | Key Properties | Notes |
|---|---|---|
| Glass | Transmission Weight = 0.9–1.0, IOR = 1.45–1.52, Roughness = 0.0–0.05 | Use `create_material_preset` preset `glass` |
| Water | Transmission Weight = 0.9, IOR = 1.33, Roughness = 0.02 | Use `create_material_preset` preset `glass` with IOR override |
| Skin | Subsurface Weight = 0.3, Subsurface Radius = [0.1, 0.05, 0.02] | |
| Emissive | Emission Color + Emission Strength | Use `create_material_preset` preset `emissive` |
## BLENDER 5.x SOCKET NAME CHANGES
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 54-54: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 55-55: Tables should be surrounded by blank lines

(MD058, blanks-around-tables)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@data/tool-guides/materials-guide.md` around lines 54 - 55, Add blank lines
immediately before the "### Special Materials" heading and after the table block
(the lines containing "| Surface | Key Properties | Notes |") so the heading and
its table are separated from surrounding content; update the "Special Materials"
heading and the adjacent table block in the markdown to ensure an empty line
above the "### Special Materials" line and an empty line after the table rows to
resolve MD022/MD058 warnings.

Comment thread desktop/assets/vipermesh-addon.py Outdated
Comment thread lib/orchestration/tool-filter.ts Outdated
Comment thread lib/orchestration/tool-registry.ts
Comment thread public/downloads/vipermesh-addon.py Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

♻️ Duplicate comments (1)
lib/orchestration/tool-filter.ts (1)

6-6: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Material intents still reintroduce execute_code through the geometry matcher.

Removing it from materials helps, but prompts like “create a glass material” still match the generic create|add|generate|build rule and pull in geometry, which adds execute_code back into the shortlist. That keeps routing non-deterministic against this PR’s migration goal.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/orchestration/tool-filter.ts` at line 6, The material intents still
trigger the generic geometry matcher and reintroduce execute_code; update the
shortlist/matching logic so that when an intent is in the materials array
(materials:
["create_material_preset","inspect_material_node_graph","set_texture"]) the
geometry matcher (the "geometry" matcher / rule that uses the generic
create|add|generate|build pattern) is not applied — either by adding an explicit
exclusion in the geometry matcher or by short-circuiting the matching flow:
detect material intents first and return/skip adding geometry, so material
prompts cannot fall through to the geometry rule and re-add execute_code.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@desktop/assets/vipermesh-addon.py`:
- Around line 1409-1415: The code currently treats assignment warnings as
non-fatal and still reports success and returns assigned_object even when
self.assign_material(object_name, ...) returned an error; change the flow so
that after calling self.assign_material you treat assignment.get("error") as a
failure: do not set assigned_slot or assigned_object when assignment contains an
error, and set the overall result success flag to False (and include the error
message) in that case; update the logic around assigned_slot, assigned_object
and the final response construction (references: assigned_slot,
self.assign_material, assignment.get("error"), and assigned_object) so a failed
assignment produces success=False and does not echo an assigned_object.
- Around line 1288-1293: The current removal of the existing material
(bpy.data.materials.remove(existing)) causes all users to lose the datablock
before a replacement exists; instead either mutate the existing datablock in
place or, if you must create a new datablock, create the new material first and
rebind users to it before removing the old one. Concretely: in the
replace_existing branch avoid calling bpy.data.materials.remove(existing)
immediately — either reuse the found material object (existing) as mat and
rebuild/clear its node_tree in place, or create new_mat =
bpy.data.materials.new(name=name), iterate every object and material slot that
references existing and set slot.material = new_mat to rebind users, then safely
remove(existing). Update the code paths that set mat (the existing/or-new
assignment and subsequent node tree rebuild) to operate on the rebound/updated
material.
- Around line 1303-1311: Compute uses_transparency and set the refraction flags
after the transmission override is resolved and treat materials as refractive
not only for preset_key == "glass" but also when transmission_weight > 0 (and a
physical IOR > 1.0) or when resolved_alpha < 1.0; specifically, update the
condition that sets uses_transparency (currently using preset_key == "glass" or
resolved_alpha < 1.0) to also consider transmission_weight > 0 and ior > 1.0,
and move the blocks that set mat.surface_render_method/mat.blend_method and
mat.use_raytrace_refraction/mat.use_screen_refraction to run after transmission
resolution so the refraction booleans honor the transmission override (also
apply the same change to the other occurrence around the 1330–1333 region).
- Around line 1330-1342: The code uses Blender 4.0+ Principled BSDF socket names
("Transmission Weight", "Emission Color", "Coat Weight", "Sheen Weight") but the
addon claims Blender 3.x support, so sockets are not found on 3.x; update the
socket population logic that builds socket_values (and the places that call
_set_socket_value and use self._as_rgba) to resolve legacy aliases based on
bpy.app.version (or probe node.inputs): map "Transmission Weight" ->
"Transmission", "Emission Color" -> "Emission", "Coat Weight" -> "Clearcoat",
"Sheen Weight" -> "Sheen" when running under Blender < (4,0,0) or when the
preferred name is missing, and use the first existing name when setting
socket_values so _set_socket_value succeeds without silent warnings.

In `@public/downloads/vipermesh-addon.py`:
- Around line 1288-1315: The current logic always forces mat.use_nodes = True
and immediately calls nodes.clear() even when an existing material is being
reused (existing variable) and replace_existing is False; change the behavior so
that if existing is found and replace_existing is False you do not mutate the
existing material's node tree—either return early (preserve nodes) or add a new
boolean update_existing flag and only run mat.use_nodes = True and nodes.clear()
when update_existing is True; additionally, when you overwrite a non-empty node
tree you must surface a warning/flag in the function response (e.g., include
reused_existing=False and an overwrite_warning) so callers know nodes were
replaced; locate references to replace_existing, existing, mat.use_nodes,
nodes.clear and the function's return path to implement this guard and the new
flag/warning.
- Around line 1395-1401: When wiring a ShaderNodeDisplacement in the
displacement branch (role == "displacement") ensure the material's Cycles
displacement mode is set: obtain the material (mat) used for this node setup,
check cycles via getattr(mat, "cycles", None) and if present set
mat.cycles.displacement_method = "BOTH" or "DISPLACEMENT" before linking
displacement.outputs["Displacement"] to output.inputs["Displacement"]; this
ensures the connection produces real displacement (remember true displacement
still requires a subdivided mesh).

---

Duplicate comments:
In `@lib/orchestration/tool-filter.ts`:
- Line 6: The material intents still trigger the generic geometry matcher and
reintroduce execute_code; update the shortlist/matching logic so that when an
intent is in the materials array (materials:
["create_material_preset","inspect_material_node_graph","set_texture"]) the
geometry matcher (the "geometry" matcher / rule that uses the generic
create|add|generate|build pattern) is not applied — either by adding an explicit
exclusion in the geometry matcher or by short-circuiting the matching flow:
detect material intents first and return/skip adding geometry, so material
prompts cannot fall through to the geometry rule and re-add execute_code.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 15ef8775-d4bd-4f60-80bb-0185ba884c35

📥 Commits

Reviewing files that changed from the base of the PR and between 24d0f97 and 08bec54.

📒 Files selected for processing (5)
  • data/tool-guides/materials-guide.md
  • desktop/assets/vipermesh-addon.py
  • lib/orchestration/tool-filter.ts
  • lib/orchestration/tool-registry.ts
  • public/downloads/vipermesh-addon.py

Comment on lines +1288 to +1293
existing = bpy.data.materials.get(name)
if existing and replace_existing:
bpy.data.materials.remove(existing)
existing = None

mat = existing or bpy.data.materials.new(name=name)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

replace_existing=True currently strips the material from every current user.

bpy.data.materials.remove(existing) deletes the datablock before the replacement is created, so any other object already using this material loses its assignment. Since the node tree is rebuilt a few lines later anyway, this should either mutate existing in place or explicitly rebind former users to the new datablock.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@desktop/assets/vipermesh-addon.py` around lines 1288 - 1293, The current
removal of the existing material (bpy.data.materials.remove(existing)) causes
all users to lose the datablock before a replacement exists; instead either
mutate the existing datablock in place or, if you must create a new datablock,
create the new material first and rebind users to it before removing the old
one. Concretely: in the replace_existing branch avoid calling
bpy.data.materials.remove(existing) immediately — either reuse the found
material object (existing) as mat and rebuild/clear its node_tree in place, or
create new_mat = bpy.data.materials.new(name=name), iterate every object and
material slot that references existing and set slot.material = new_mat to rebind
users, then safely remove(existing). Update the code paths that set mat (the
existing/or-new assignment and subsequent node tree rebuild) to operate on the
rebound/updated material.

Comment on lines +1303 to +1311
uses_transparency = preset_key == "glass" or resolved_alpha < 1.0
if hasattr(mat, "surface_render_method"):
mat.surface_render_method = "BLENDED" if uses_transparency else "DITHERED"
elif hasattr(mat, "blend_method"):
mat.blend_method = "BLEND" if uses_transparency else "OPAQUE"
if hasattr(mat, "use_raytrace_refraction"):
mat.use_raytrace_refraction = preset_key == "glass"
elif hasattr(mat, "use_screen_refraction"):
mat.use_screen_refraction = preset_key == "glass"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Honor transmission_weight when deciding whether the material is refractive.

uses_transparency and the refraction flags are computed before the transmission override is resolved and only special-case the glass preset. A call like preset="plastic", transmission_weight=1.0, ior=1.49 will set the BSDF socket but still leave the material configured as opaque/non-refractive.

Suggested fix
-            uses_transparency = preset_key == "glass" or resolved_alpha < 1.0
+            resolved_transmission = float(
+                defaults.get("transmission", 0.0) if transmission_weight is None else transmission_weight
+            )
+            uses_transparency = resolved_alpha < 1.0 or resolved_transmission > 0.0
             if hasattr(mat, "surface_render_method"):
                 mat.surface_render_method = "BLENDED" if uses_transparency else "DITHERED"
             elif hasattr(mat, "blend_method"):
                 mat.blend_method = "BLEND" if uses_transparency else "OPAQUE"
             if hasattr(mat, "use_raytrace_refraction"):
-                mat.use_raytrace_refraction = preset_key == "glass"
+                mat.use_raytrace_refraction = resolved_transmission > 0.0
             elif hasattr(mat, "use_screen_refraction"):
-                mat.use_screen_refraction = preset_key == "glass"
+                mat.use_screen_refraction = resolved_transmission > 0.0
...
             if "transmission" in defaults or transmission_weight is not None:
-                socket_values["Transmission Weight"] = float(defaults.get("transmission", 0.0) if transmission_weight is None else transmission_weight)
+                socket_values["Transmission Weight"] = resolved_transmission

Also applies to: 1330-1333

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@desktop/assets/vipermesh-addon.py` around lines 1303 - 1311, Compute
uses_transparency and set the refraction flags after the transmission override
is resolved and treat materials as refractive not only for preset_key == "glass"
but also when transmission_weight > 0 (and a physical IOR > 1.0) or when
resolved_alpha < 1.0; specifically, update the condition that sets
uses_transparency (currently using preset_key == "glass" or resolved_alpha <
1.0) to also consider transmission_weight > 0 and ior > 1.0, and move the blocks
that set mat.surface_render_method/mat.blend_method and
mat.use_raytrace_refraction/mat.use_screen_refraction to run after transmission
resolution so the refraction booleans honor the transmission override (also
apply the same change to the other occurrence around the 1330–1333 region).

Comment on lines +1330 to +1342
if "transmission" in defaults or transmission_weight is not None:
socket_values["Transmission Weight"] = float(defaults.get("transmission", 0.0) if transmission_weight is None else transmission_weight)
if "ior" in defaults or ior is not None:
socket_values["IOR"] = float(defaults.get("ior", 1.45) if ior is None else ior)
if "coat" in defaults:
socket_values["Coat Weight"] = float(defaults["coat"])
if "sheen" in defaults:
socket_values["Sheen Weight"] = float(defaults["sheen"])

resolved_emission = emission_color if emission_color is not None else defaults.get("emission_color")
if resolved_emission is not None:
socket_values["Emission Color"] = self._as_rgba(resolved_emission, defaults["color"], clamp=False)
socket_values["Emission Strength"] = float(defaults.get("emission_strength", 1.0) if emission_strength is None else emission_strength)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

In Blender 3.x versus 4.x/5.x Principled BSDF, what are the input socket names for Transmission, Emission, Clearcoat, and Sheen, and when were they renamed to Transmission Weight, Emission Color, Coat Weight, and Sheen Weight?

💡 Result:

In Blender 3.x (e.g., 3.6), the Principled BSDF input socket names were Transmission (factor), Emission (color), Clearcoat (factor), and Sheen (factor or with Tint) [1][2][3]. In Blender 4.x/5.x (e.g., 4.0+), they are Transmission Weight, Emission Color (or Emission), Coat Weight, and Sheen Weight [4][5][6][7]. These renames, part of a major revamp to support more material types with layered weights and shortened names, occurred in Blender 4.0 [8][9][10]. Emission rename is explicitly noted in development tasks [9], Transmission/Clearcoat/Sheen changed from direct factors to explicit Weight sockets [10][11].

Citations:


🏁 Script executed:

# Find the file and check its structure
find . -name "vipermesh-addon.py" -type f

Repository: Ker102/ViperMesh

Length of output: 134


🏁 Script executed:

# Get the file size to understand how to read it
wc -l desktop/assets/vipermesh-addon.py

Repository: Ker102/ViperMesh

Length of output: 99


🏁 Script executed:

# Check addon metadata (typically at top of file) for Blender version
head -50 desktop/assets/vipermesh-addon.py

Repository: Ker102/ViperMesh

Length of output: 1465


🏁 Script executed:

# Examine the _set_socket_value method to understand error handling
rg "_set_socket_value" -A 10 desktop/assets/vipermesh-addon.py

Repository: Ker102/ViperMesh

Length of output: 977


🏁 Script executed:

# Check lines 1330-1342 in detail
sed -n '1325,1345p' desktop/assets/vipermesh-addon.py

Repository: Ker102/ViperMesh

Length of output: 1526


🏁 Script executed:

# Check lines 1471-1482 to confirm they have the same issue
sed -n '1466,1487p' desktop/assets/vipermesh-addon.py

Repository: Ker102/ViperMesh

Length of output: 858


🏁 Script executed:

# Search for version checks or legacy/compatibility handling
rg "blender_version|3\.|4\.|Transmission|Clearcoat|Coat Weight" -B 2 -A 2 desktop/assets/vipermesh-addon.py | head -100

Repository: Ker102/ViperMesh

Length of output: 4141


🏁 Script executed:

# Double-check lines around 1471-1482 to see full context of inspection code
sed -n '1465,1495p' desktop/assets/vipermesh-addon.py

Repository: Ker102/ViperMesh

Length of output: 1310


Use only Blender 4.0+ socket names, but addon claims Blender 3.0 minimum support.

The addon advertises blender: (3, 0, 0) in bl_info but uses Blender 4.0+ Principled BSDF input names: Transmission Weight, Emission Color, Coat Weight, Sheen Weight. In Blender 3.x, these sockets are named Transmission, Emission, Clearcoat, and Sheen respectively. When _set_socket_value() cannot find these sockets, it silently appends a warning and continues, causing material presets and inspection results to be silently broken on supported versions.

Affects both material application (lines 1330–1342) and inspection (lines 1471–1482). Add legacy aliases, version checks, or update the minimum Blender version to 4.0.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@desktop/assets/vipermesh-addon.py` around lines 1330 - 1342, The code uses
Blender 4.0+ Principled BSDF socket names ("Transmission Weight", "Emission
Color", "Coat Weight", "Sheen Weight") but the addon claims Blender 3.x support,
so sockets are not found on 3.x; update the socket population logic that builds
socket_values (and the places that call _set_socket_value and use self._as_rgba)
to resolve legacy aliases based on bpy.app.version (or probe node.inputs): map
"Transmission Weight" -> "Transmission", "Emission Color" -> "Emission", "Coat
Weight" -> "Clearcoat", "Sheen Weight" -> "Sheen" when running under Blender <
(4,0,0) or when the preferred name is missing, and use the first existing name
when setting socket_values so _set_socket_value succeeds without silent
warnings.

Comment on lines +1409 to +1415
assigned_slot = None
if object_name:
assignment = self.assign_material(object_name, mat.name, slot_index)
if assignment.get("error"):
warnings.append(assignment["error"])
else:
assigned_slot = assignment.get("slot")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't report a successful preset result when assignment failed.

When object_name is provided, a bad target or unsupported object type only appends a warning here, but the tool still reports success: True and echoes assigned_object. That makes the create+assign flow look complete even though no material slot was updated.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@desktop/assets/vipermesh-addon.py` around lines 1409 - 1415, The code
currently treats assignment warnings as non-fatal and still reports success and
returns assigned_object even when self.assign_material(object_name, ...)
returned an error; change the flow so that after calling self.assign_material
you treat assignment.get("error") as a failure: do not set assigned_slot or
assigned_object when assignment contains an error, and set the overall result
success flag to False (and include the error message) in that case; update the
logic around assigned_slot, assigned_object and the final response construction
(references: assigned_slot, self.assign_material, assignment.get("error"), and
assigned_object) so a failed assignment produces success=False and does not echo
an assigned_object.

Comment on lines +1288 to +1315
existing = bpy.data.materials.get(name)
if existing and replace_existing:
bpy.data.materials.remove(existing)
existing = None

mat = existing or bpy.data.materials.new(name=name)
mat.use_nodes = True

resolved_color = self._as_rgba(base_color, defaults["color"])
if alpha is not None:
resolved_alpha = float(max(0.0, min(1.0, alpha)))
resolved_color = (resolved_color[0], resolved_color[1], resolved_color[2], resolved_alpha)
else:
resolved_alpha = float(max(0.0, min(1.0, resolved_color[3])))

uses_transparency = preset_key == "glass" or resolved_alpha < 1.0
if hasattr(mat, "surface_render_method"):
mat.surface_render_method = "BLENDED" if uses_transparency else "DITHERED"
elif hasattr(mat, "blend_method"):
mat.blend_method = "BLEND" if uses_transparency else "OPAQUE"
if hasattr(mat, "use_raytrace_refraction"):
mat.use_raytrace_refraction = preset_key == "glass"
elif hasattr(mat, "use_screen_refraction"):
mat.use_screen_refraction = preset_key == "glass"

nodes = mat.node_tree.nodes
links = mat.node_tree.links
nodes.clear()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

replace_existing=False still wipes the existing material's node tree.

When a material with name already exists and replace_existing=False, the code intentionally reuses the data-block (mat = existing or ...), but immediately forces mat.use_nodes = True and nodes.clear() and rebuilds from scratch. Any user-authored nodes/links/properties on that material are silently destroyed, while reused_existing: True is reported back as if the material was preserved.

This is a footgun for an "agent-driven" tool that is likely to be invoked repeatedly with the same material name. Consider either:

  • Treating replace_existing=False as "do not touch an existing material" (return early with a clear message), or
  • Adding a distinct update_existing flag and at minimum surfacing a warning in the response when a non-empty pre-existing node tree is overwritten.
🛡️ Suggested guard
             existing = bpy.data.materials.get(name)
             if existing and replace_existing:
                 bpy.data.materials.remove(existing)
                 existing = None
+            elif existing:
+                had_nodes = bool(existing.use_nodes and existing.node_tree and len(existing.node_tree.nodes) > 0)
+                if had_nodes:
+                    warnings.append(
+                        f"Existing material '{name}' was rebuilt in place; previous node graph was discarded. "
+                        "Pass replace_existing=true for a fresh data-block, or use a different name to preserve it."
+                    )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@public/downloads/vipermesh-addon.py` around lines 1288 - 1315, The current
logic always forces mat.use_nodes = True and immediately calls nodes.clear()
even when an existing material is being reused (existing variable) and
replace_existing is False; change the behavior so that if existing is found and
replace_existing is False you do not mutate the existing material's node
tree—either return early (preserve nodes) or add a new boolean update_existing
flag and only run mat.use_nodes = True and nodes.clear() when update_existing is
True; additionally, when you overwrite a non-empty node tree you must surface a
warning/flag in the function response (e.g., include reused_existing=False and
an overwrite_warning) so callers know nodes were replaced; locate references to
replace_existing, existing, mat.use_nodes, nodes.clear and the function's return
path to implement this guard and the new flag/warning.

Comment on lines +1395 to +1401
if role == "displacement":
displacement = nodes.new(type="ShaderNodeDisplacement")
displacement.location = (-160, tex.location.y)
links.new(tex.outputs["Color"], displacement.inputs["Height"])
if output.inputs.get("Displacement"):
links.new(displacement.outputs["Displacement"], output.inputs["Displacement"])
continue
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

Blender Cycles material displacement_method default value bump only

💡 Result:

In Blender Cycles, the material displacement setting “displacement_method” defaults to bump-only. Details 1) Default value The Cycles material displacement method enum is documented as an enum with default value ‘BUMP’ (Bump Only). [1][2] 2) What “Bump Only” means The Blender manual describes Bump Only as using a modified surface normal during shading instead of actually moving mesh vertices (i.e., it’s an approximation and does not change geometry). [3][4] 3) What you need for “displacement only / displacement and bump” To get true geometry displacement, you must use the corresponding displacement method setting and provide enough geometric subdivision (the manual notes true displacement requires a finely subdivided mesh). [3][5] So if you only change a displacement input but the displacement_method is still at its default, you will see bump/normal shading but not actual geometric variation. [1][3]

Citations:


🏁 Script executed:

# Find the file and check context around lines 1395-1401
fd -t f "vipermesh-addon.py" --exec wc -l {} \;

Repository: Ker102/ViperMesh

Length of output: 144


🏁 Script executed:

# List the file if found
fd -t f "vipermesh-addon.py"

Repository: Ker102/ViperMesh

Length of output: 130


🏁 Script executed:

# Try broader search for the file
find . -type f -name "*vipermesh*" 2>/dev/null

Repository: Ker102/ViperMesh

Length of output: 299


🏁 Script executed:

# Read context around the displacement code
sed -n '1380,1420p' ./public/downloads/vipermesh-addon.py

Repository: Ker102/ViperMesh

Length of output: 2047


🏁 Script executed:

# Check for warnings variable - search for its initialization
rg "warnings\s*=" ./public/downloads/vipermesh-addon.py -A 2 -B 2 | head -50

Repository: Ker102/ViperMesh

Length of output: 313


🏁 Script executed:

# Also check how other similar issues are handled in this function
sed -n '1300,1410p' ./public/downloads/vipermesh-addon.py | head -150

Repository: Ker102/ViperMesh

Length of output: 5822


🌐 Web query:

Blender bpy.types.CyclesMaterialSettings displacement_method enum values

💡 Result:

The enum values for Blender’s Cycles material displacement_method (bpy.types.CyclesMaterialSettings) are: - 'BUMP' (Bump Only) - 'DISPLACEMENT' (Displacement Only) - 'BOTH' (Displacement and Bump) These are defined as an enum in ['BUMP', 'DISPLACEMENT', 'BOTH'] with default 'BUMP'. [1][2] Example (to set): mat = bpy.context.object.active_material mat.cycles.displacement_method = 'DISPLACEMENT'

Citations:


Displacement texture wiring has no visible effect without setting cycles.displacement_method.

Material Output's Displacement socket defaults to bump-only in Cycles, so wiring a ShaderNodeDisplacement to output.Displacement without setting mat.cycles.displacement_method won't actually displace geometry—the connection appears to work but only applies bump/normal shading. The material will also report the link as functional.

Set mat.cycles.displacement_method to 'BOTH' or 'DISPLACEMENT' when a displacement map is wired. Use getattr(mat, "cycles", None) to safely access since mat.cycles only exists when Cycles' RNA is registered. Also note that true displacement requires a subdivided mesh.

Suggested change
                if role == "displacement":
                    displacement = nodes.new(type="ShaderNodeDisplacement")
                    displacement.location = (-160, tex.location.y)
                    links.new(tex.outputs["Color"], displacement.inputs["Height"])
                    if output.inputs.get("Displacement"):
                        links.new(displacement.outputs["Displacement"], output.inputs["Displacement"])
+                    cycles_settings = getattr(mat, "cycles", None)
+                    if cycles_settings is not None and hasattr(cycles_settings, "displacement_method"):
+                        try:
+                            cycles_settings.displacement_method = "BOTH"
+                        except Exception as exc:
+                            warnings.append(f"Could not set displacement_method: {exc!s}")
+                    else:
+                        warnings.append(
+                            "Displacement node wired, but Cycles displacement_method is unavailable; "
+                            "ensure the Cycles addon is enabled and the mesh is subdivided for true displacement."
+                        )
                    continue
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@public/downloads/vipermesh-addon.py` around lines 1395 - 1401, When wiring a
ShaderNodeDisplacement in the displacement branch (role == "displacement")
ensure the material's Cycles displacement mode is set: obtain the material (mat)
used for this node setup, check cycles via getattr(mat, "cycles", None) and if
present set mat.cycles.displacement_method = "BOTH" or "DISPLACEMENT" before
linking displacement.outputs["Displacement"] to output.inputs["Displacement"];
this ensures the connection produces real displacement (remember true
displacement still requires a subdivided mesh).

@Ker102 Ker102 merged commit 6278b80 into main May 7, 2026
6 checks passed
@Ker102 Ker102 deleted the feature/blender-material-node-presets branch May 7, 2026 17:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend desktop documentation Improvements or additions to documentation scripts

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant