Skill: emergencescience/blender-bpy-enhanced (version 2.0.0)

--- name: blender-bpy title: Blender 3D Automation — Python bpy Scripting description: Comprehensive Blender automation via Python bpy API. Procedural modeling, material nodes, lighting, rendering, and asset generation. Includes verified gear/mechanical parts demo. version: 2.0.0 author: Approxima (via skillhub.cn) tags: [blender, 3d, modeling, rendering, bpy, procedural, automation] requires: bins: [blender] --- # Blender Python Automation (bpy) — v2.0.0 ## When to Use This Skill Invoke when the user wants to: - Create 3D objects procedurally (gears, mechanical parts, architectural elements) - Set up materials with node-based textures (metal, brushed, procedural) - Configure 3-point lighting and camera - Render still images or animations in headless mode - Batch process or automate Blender workflows - Export to GLB/glTF for web or game engines ## Prerequisites ```bash # Install Blender apt-get install blender # Linux (Debian/Ubuntu) # Or: brew install blender # macOS # Verify blender --version ``` ## Core Patterns ### 1. Headless Execution ```bash blender --background --python script.py ``` ### 2. Scene Setup ```python import bpy, math # Clear scene bpy.ops.object.select_all(action='SELECT') bpy.ops.object.delete(use_global=False) for mat in list(bpy.data.materials): bpy.data.materials.remove(mat) ``` ### 3. Procedural Gear Creation ```python def create_gear(name, radius=2.0, teeth=16, thickness=0.8): """Create a gear with teeth and center hole""" # Base cylinder bpy.ops.mesh.primitive_cylinder_add( vertices=teeth * 4, radius=radius, depth=thickness, location=(0, 0, 0) ) gear = bpy.context.object gear.name = name # Edit mode: select vertices at tooth positions bpy.ops.object.mode_set(mode='EDIT') bpy.ops.mesh.select_all(action='DESELECT') for v in gear.data.vertices: angle = math.atan2(v.co.y, v.co.x) tooth_angle = 2 * math.pi / teeth angle_diff = abs((angle % tooth_angle) - tooth_angle / 2) if angle_diff < tooth_angle * 0.35: v.select = True # Extrude and scale for teeth bpy.ops.mesh.extrude_region_move( TRANSFORM_OT_translate={"value": (0, 0, 0)} ) bpy.ops.transform.resize( value=((radius + 0.4) / radius,) * 2 + (1,), orient_type='GLOBAL' ) bpy.ops.object.mode_set(mode='OBJECT') # Center hole via Boolean bpy.ops.mesh.primitive_cylinder_add( vertices=32, radius=0.5, depth=thickness * 1.5, location=(0, 0, 0) ) cutter = bpy.context.object bool_mod = gear.modifiers.new(name="Hole", type='BOOLEAN') bool_mod.operation = 'DIFFERENCE' bool_mod.object = cutter bpy.context.view_layer.objects.active = gear gear.select_set(True) bpy.ops.object.modifier_apply(modifier="Hole") bpy.data.objects.remove(cutter, do_unlink=True) # Modifier stack bevel = gear.modifiers.new(name="Bevel", type='BEVEL') bevel.width = 0.05; bevel.segments = 2; bevel.limit_method = 'ANGLE' subdiv = gear.modifiers.new(name="Subdivision", type='SUBSURF') subdiv.levels = 1; subdiv.render_levels = 2 return gear ``` ### 4. Procedural Metal Material (Node-based) ```python def create_metal_material(name, base_color, metallic=0.85, roughness=0.25, noise_scale=30.0, use_brushed=True): """Create a procedural metal material with optional brushed effect""" mat = bpy.data.materials.new(name=name) mat.use_nodes = True nodes = mat.node_tree.nodes links = mat.node_tree.links nodes.clear() output = nodes.new(type='ShaderNodeOutputMaterial') output.location = (400, 0) bsdf = nodes.new(type='ShaderNodeBsdfPrincipled') bsdf.location = (0, 0) bsdf.inputs['Base Color'].default_value = base_color bsdf.inputs['Metallic'].default_value = metallic bsdf.inputs['Roughness'].default_value = roughness if use_brushed: tex = nodes.new(type='ShaderNodeTexCoord') tex.location = (-400, 100) noise = nodes.new(type='ShaderNodeTexNoise') noise.location = (-200, 0) noise.inputs['Scale'].default_value = noise_scale noise.inputs['Detail'].default_value = 2.0 ramp = nodes.new(type='ShaderNodeValToRGB') ramp.location = (0, 100) ramp.color_ramp.elements[0].color = ( base_color[0]*0.8, base_color[1]*0.8, base_color[2]*0.8, 1.0) ramp.color_ramp.elements[1].color = ( base_color[0]*1.1, base_color[1]*1.1, base_color[2]*1.1, 1.0) links.new(tex.outputs['Object'], noise.inputs['Vector']) links.new(noise.outputs['Fac'], ramp.inputs['Fac']) links.new(ramp.outputs['Color'], bsdf.inputs['Base Color']) links.new(bsdf.outputs['BSDF'], output.inputs['Surface']) return mat ``` ### 5. 3-Point Lighting Setup ```python def setup_lighting(base_intensity=600): """Standard 3-point lighting: key, fill, rim""" # Key light (main) key = bpy.ops.object.light_add( type='AREA', location=(5, -4, 6), rotation=(0.8, 0, 0.7)) key = bpy.context.object key.data.energy = base_intensity key.data.size = 4 # Fill light fill = bpy.ops.object.light_add( type='AREA', location=(-4, 3, 3), rotation=(0.5, 0, -1.0)) fill = bpy.context.object fill.data.energy = base_intensity * 0.5 fill.data.size = 3 # Rim/back light rim = bpy.ops.object.light_add( type='AREA', location=(0, 5, 5), rotation=(0.5, 0, 1.57)) rim = bpy.context.object rim.data.energy = base_intensity * 0.4 rim.data.size = 2 ``` ### 6. Camera Setup ```python def setup_camera(location=(5.5, -4.5, 3.5), target=(0, 0, 0)): bpy.ops.object.camera_add(location=location) cam = bpy.context.object # Point camera at target direction = (target[0] - location[0], target[1] - location[1], target[2] - location[2]) cam.rotation_euler = ( math.asin(direction[2] / math.sqrt(sum(d**2 for d in direction))), 0, math.atan2(direction[1], direction[0]) + math.pi/2 ) bpy.context.scene.camera = cam return cam ``` ### 7. Rendering ```python def render(output_path="/tmp/render.png", engine='BLENDER_EEVEE', width=1920, height=1080, percentage=50): scene = bpy.context.scene scene.render.engine = engine scene.render.resolution_x = width scene.render.resolution_y = height scene.render.resolution_percentage = percentage scene.render.filepath = output_path scene.render.image_settings.file_format = 'PNG' bpy.ops.render.render(write_still=True) print(f"✅ Rendered: {scene.render.filepath}") ``` ## Complete Demo: Procedural Gear See `scripts/demo_gear.py` for the full working demo. ```bash blender --background --python scripts/demo_gear.py ``` Output: `/tmp/blender_demo_gear.png` and `/tmp/blender_demo_gear.blend` ## Render Engines | Engine | Best For | Notes | |--------|----------|-------| | `BLENDER_EEVEE` | Fast preview, real-time | No denoiser needed, good for demos | | `CYCLES` | Photorealistic | Needs OpenImageDenoiser; use `samples=128` for quick tests | ## Common Pitfalls - **Render fails with "Build without OpenImageDenoiser"** → Switch to Eevee: `scene.render.engine = 'BLENDER_EEVEE'` - **Boolean modifier not applying** → Set `gear.select_set(True)` and `bpy.context.view_layer.objects.active = gear` first - **Bmesh edit mode errors** → Always call `bpy.ops.object.mode_set(mode='EDIT')` before `bmesh.from_edit_mesh()` - **Material not showing on export** → Must assign to object's face data: `obj.data.materials.append(mat)` - **Grid primitive fails** → In Blender 4.0+, use `x_subdivisions=N` and `y_subdivisions=N` instead of `subdivisions=N` - **Headless EGL warnings** → These are harmless; Eevee falls back to surfaceless rendering automatically


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)