blender-syntax-modifiers
blender-syntax-modifiers
Quick Reference
Critical Warnings
NEVER apply modifiers while the object is in Edit Mode — modifier_apply requires Object Mode. Switch with bpy.ops.object.mode_set(mode='OBJECT') first.
NEVER use dict context overrides for modifier_apply in Blender 4.0+ — use context.temp_override() instead. Dict overrides raise TypeError.
NEVER read obj.data.vertices expecting post-modifier results — use obj.evaluated_get(depsgraph).to_mesh() to access evaluated mesh data.
NEVER forget obj_eval.to_mesh_clear() after obj_eval.to_mesh() — this causes memory leaks. ALWAYS use a try/finally block.
NEVER assume Geometry Nodes modifier input identifiers match socket names — identifiers are auto-generated as Socket_N. ALWAYS look up identifiers via modifier.node_group.interface.items_tree.
NEVER modify the evaluated mesh — it is read-only. Modify the original object, then let the depsgraph re-evaluate.
Modifier Type Decision Tree
Need to add geometry procedurally?
├── Repeat geometry in a pattern → ARRAY
├── Combine/subtract meshes → BOOLEAN
├── Add thickness to surfaces → SOLIDIFY
├── Subdivide for smoothness → SUBSURF
├── Symmetry across an axis → MIRROR
├── Edge beveling → BEVEL
├── Custom node-based logic → NODES (Geometry Nodes)
└── Reduce polygon count → DECIMATE
Need deformation?
├── Skeleton-based → ARMATURE
├── Surface projection → SHRINKWRAP
├── Volume-based deform → MESH_DEFORM
├── Grid-based deform → LATTICE
└── Along a path → CURVE
AEC-Relevant Modifiers
| Modifier | AEC Use Case | Type String |
|---|---|---|
| Array | Repetitive elements (columns, railings, facade panels) | 'ARRAY' |
| Boolean | Wall openings, structural cutouts, MEP penetrations | 'BOOLEAN' |
| Solidify | Thickness to imported 2D geometry, wall shells | 'SOLIDIFY' |
| Geometry Nodes | Parametric components, distribution, rule-based generation | 'NODES' |
| Mirror | Symmetric buildings, reflected floor plans | 'MIRROR' |
| Bevel | Edge finishing for architectural details | 'BEVEL' |
Essential Patterns
Pattern 1: Add a Modifier
# Blender 3.x/4.x/5.x — add modifier to an object
import bpy
obj = bpy.data.objects.get("Wall")
if obj is None:
raise RuntimeError("Object 'Wall' not found")
# Add modifier — returns the new Modifier object
mod = obj.modifiers.new(name="MyArray", type='ARRAY')
# name: display name (Blender may append .001 if duplicate)
# type: modifier type enum string (see table above)
# Configure modifier properties
mod.count = 5
mod.relative_offset_displace = (1.0, 0.0, 0.0)
Pattern 2: Remove a Modifier
# Blender 3.x/4.x/5.x — remove modifier by reference or name
import bpy
obj = bpy.data.objects.get("Wall")
# Remove by reference
mod = obj.modifiers.get("MyArray")
if mod is not None:
obj.modifiers.remove(mod)
# Remove all modifiers
for mod in obj.modifiers[:]: # Copy list — modifiers change during removal
obj.modifiers.remove(mod)
Pattern 3: Apply a Modifier (Version-Critical)
Applying a modifier permanently bakes its effect into the mesh. This is an IRREVERSIBLE operation. ALWAYS requires Object Mode.
# Blender 3.2+/4.x/5.x — apply modifier using temp_override
import bpy
obj = bpy.data.objects.get("Wall")
# Ensure Object Mode
if bpy.context.mode != 'OBJECT':
bpy.ops.object.mode_set(mode='OBJECT')
# Apply a single modifier
with bpy.context.temp_override(object=obj, active_object=obj):
bpy.ops.object.modifier_apply(modifier="MyArray")
# Apply ALL modifiers (copy list — stack changes during apply)
with bpy.context.temp_override(object=obj, active_object=obj):
for mod in obj.modifiers[:]:
bpy.ops.object.modifier_apply(modifier=mod.name)
# Blender < 3.2 ONLY (legacy — do NOT use for new code)
override = bpy.context.copy()
override['object'] = obj
override['active_object'] = obj
bpy.ops.object.modifier_apply(override, modifier="MyArray")
Pattern 4: Evaluated Mesh via Depsgraph (Preview Without Applying)
Use this to read the post-modifier mesh WITHOUT permanently applying modifiers.
# Blender 3.x/4.x/5.x — read evaluated mesh data
import bpy
obj = bpy.data.objects.get("Wall")
depsgraph = bpy.context.evaluated_depsgraph_get()
obj_eval = obj.evaluated_get(depsgraph)
mesh_eval = obj_eval.to_mesh()
try:
print(f"Original verts: {len(obj.data.vertices)}")
print(f"Evaluated verts: {len(mesh_eval.vertices)}")
# Read vertex positions in world space
for v in mesh_eval.vertices:
world_co = obj_eval.matrix_world @ v.co
# process world_co...
finally:
obj_eval.to_mesh_clear() # ALWAYS clean up
Pattern 5: Geometry Nodes Modifier — Assign and Configure Inputs
# Blender 4.0+ — assign node group and set input values
import bpy
obj = bpy.data.objects.get("Building")
mod = obj.modifiers.new(name="GeoNodes", type='NODES')
# Assign an existing node group
node_group = bpy.data.node_groups.get("AEC_WallGenerator")
if node_group is None:
raise RuntimeError("Node group 'AEC_WallGenerator' not found")
mod.node_group = node_group
# Find input identifiers by name (REQUIRED — identifiers are NOT names)
def get_gn_input_identifier(modifier, input_name):
"""Return the Socket_N identifier for a named GN input."""
for item in modifier.node_group.interface.items_tree:
if item.item_type == 'SOCKET' and item.in_out == 'INPUT':
if item.name == input_name:
return item.identifier
raise KeyError(f"Input '{input_name}' not found in node group")
# Set input values using identifiers
height_id = get_gn_input_identifier(mod, "Height")
mod[height_id] = 3.5
thickness_id = get_gn_input_identifier(mod, "Thickness")
mod[thickness_id] = 0.2
# Force scene update after changing GN inputs
bpy.context.view_layer.update()
# Blender 4.0+ — list all GN modifier inputs
for item in mod.node_group.interface.items_tree:
if item.item_type == 'SOCKET' and item.in_out == 'INPUT':
identifier = item.identifier # e.g., "Socket_2"
name = item.name # e.g., "Height"
socket_type = item.socket_type # e.g., "NodeSocketFloat"
current_value = mod.get(identifier)
print(f"{name} ({socket_type}): {identifier} = {current_value}")
Pattern 6: Reorder Modifiers
# Blender 4.0+/5.x — move modifier to specific position
import bpy
obj = bpy.data.objects.get("Wall")
with bpy.context.temp_override(object=obj):
bpy.ops.object.modifier_move_to_index(modifier="Boolean", index=0)
# Blender 3.x/4.x/5.x — move up/down by one position
with bpy.context.temp_override(object=obj):
bpy.ops.object.modifier_move_up(modifier="Boolean")
bpy.ops.object.modifier_move_down(modifier="Array")
Common Operations
Array Modifier — Repetitive AEC Elements
# Blender 3.x/4.x/5.x — create array of columns
import bpy
obj = bpy.data.objects.get("Column")
mod = obj.modifiers.new(name="ColumnArray", type='ARRAY')
# Fixed count
mod.use_relative_offset = True
mod.relative_offset_displace = (2.0, 0.0, 0.0) # Spacing in object-relative units
mod.count = 10
# Constant offset (absolute units)
mod.use_constant_offset = True
mod.constant_offset_displace = (3.0, 0.0, 0.0) # 3m spacing in scene units
# Fit to length
mod.fit_type = 'FIT_LENGTH'
mod.fit_length = 30.0 # Total span in scene units
# Fit to curve
mod.fit_type = 'FIT_CURVE'
mod.curve = bpy.data.objects.get("ArrayPath")
Boolean Modifier — Wall Openings
# Blender 3.x/4.x/5.x — create opening in wall
import bpy
wall = bpy.data.objects.get("Wall")
opening = bpy.data.objects.get("WindowOpening")
mod = wall.modifiers.new(name="WindowCut", type='BOOLEAN')
mod.operation = 'DIFFERENCE' # 'INTERSECT', 'UNION', 'DIFFERENCE'
mod.object = opening # Cutter object
mod.solver = 'FAST' # 'FAST' or 'EXACT' (Blender 3.0+)
# EXACT is slower but handles edge cases better (coplanar faces, thin geometry)
# Hide cutter in viewport (common AEC pattern)
opening.hide_set(True)
opening.hide_render = True
Solidify Modifier — Wall Thickness
# Blender 3.x/4.x/5.x — add thickness to planar geometry
import bpy
obj = bpy.data.objects.get("WallSurface")
mod = obj.modifiers.new(name="WallThickness", type='SOLIDIFY')
mod.thickness = 0.3 # Wall thickness in scene units
mod.offset = -1.0 # -1 = outward, 0 = centered, 1 = inward
mod.use_even_offset = True # Uniform thickness on angled surfaces
mod.use_quality_normals = True # Better normals on complex geometry
Copy Modifier Settings Between Objects
# Blender 3.x/4.x/5.x — copy all modifiers from source to target
import bpy
source = bpy.data.objects.get("SourceWall")
target = bpy.data.objects.get("TargetWall")
# Use operator to copy modifiers
with bpy.context.temp_override(
object=target,
active_object=source,
selected_objects=[target]
):
bpy.ops.object.make_links_data(type='MODIFIERS')
Check if Modifier Exists Before Operating
# Blender 3.x/4.x/5.x — safe modifier access
import bpy
obj = bpy.data.objects.get("Wall")
# Check by name
mod = obj.modifiers.get("MyArray")
if mod is not None:
print(f"Found modifier: {mod.name}, type: {mod.type}")
# Check by type
has_boolean = any(m.type == 'BOOLEAN' for m in obj.modifiers)
# Iterate with type filter
for mod in obj.modifiers:
if mod.type == 'ARRAY':
print(f"Array: {mod.name}, count: {mod.count}")
Version-Specific Notes
| Feature | Blender 3.x | Blender 4.0+ |
|---|---|---|
Context override for modifier_apply |
Dict override (deprecated 3.2) | context.temp_override() REQUIRED |
| GN node group sockets API | node_group.inputs.new(...) |
node_group.interface.new_socket(...) |
| GN modifier input identifiers | Input_N (Blender 3.x) |
Socket_N (Blender 4.0+) |
modifier_move_to_index |
Not available | Available |
Boolean solver 'EXACT' |
Available (3.0+) | Available |
bmesh.from_object() depsgraph param |
Optional | REQUIRED |
BVHTree.FromObject() depsgraph param |
Optional | REQUIRED |
Reference Links
- references/methods.md — Complete API signatures for ObjectModifiers, Modifier base, and specific modifier types
- references/examples.md — Working code examples for all common modifier operations
- references/anti-patterns.md — What NOT to do with modifiers, with explanations
Official Sources
- https://docs.blender.org/api/current/bpy.types.ObjectModifiers.html
- https://docs.blender.org/api/current/bpy.types.Modifier.html
- https://docs.blender.org/api/current/bpy.types.NodesModifier.html
- https://docs.blender.org/api/current/bpy.types.ArrayModifier.html
- https://docs.blender.org/api/current/bpy.types.BooleanModifier.html
- https://docs.blender.org/api/current/bpy.types.SolidifyModifier.html
- https://docs.blender.org/api/current/bpy.types.Depsgraph.html
More from openaec-foundation/computational-design-day-delft-march-2026
blender-core-api
Guides Blender Python API usage including bpy module structure, RNA data access, context system, dependency graph, and operator invocation. Activates when writing bpy scripts, creating Blender addons, or accessing Blender data blocks programmatically.
1blender-syntax-panels
Defines Blender UI panel creation including bpy.types.Panel, draw() method, UILayout API (row/column/box/split), bl_space_type, bl_region_type, bl_category, sub-panels, draw_header, menus, and UIList. Activates when creating custom Blender panels, building addon interfaces, or working with UILayout elements.
1ifcos-syntax-api
Documents the ifcopenshell.api module system with all 30+ API modules, invocation patterns via api.run() and direct module calls, parameter conventions, and module categorization. Activates when creating IFC entities, modifying properties, managing spatial structure, or using any ifcopenshell.api function.
1bonsai-impl-bcf
Guides implementation of BIM Collaboration Format (BCF) workflows in Bonsai including creating BCF topics, adding viewpoints with camera snapshots, managing comments, importing/exporting BCF files (v2.1 and v3.0), and integrating BCF issue tracking with IFC element references. Activates when working with BCF files, BIM issue tracking, clash report management, or collaboration workflows in Bonsai.
1ifcos-impl-mep
Guides MEP (Mechanical, Electrical, Plumbing) modeling in IFC using ifcopenshell.api.system including IfcSystem, IfcDistributionElement, ports, connections, flow segments, fittings, and MEP-specific property sets. Activates when creating HVAC systems, piping networks, electrical circuits, or MEP elements in IFC models.
1ifcos-impl-cost
Guides IFC cost management using ifcopenshell.api.cost including cost schedules, cost items, cost values, cost quantities, and 5D BIM workflows. Activates when implementing cost estimation in IFC models, creating cost schedules, or linking quantities to cost items.
1