Skip to main content
fabricating-assets is a skill from the Summer skill library: a guide your AI agent reads when the task matches, so it builds the good version on the first try. Fabricate meshes with a Blender script (summer_fabricate_3d): exact-size kits, VFX meshes, post-processing.

When your agent uses it

  • Writing a Blender Python (bpy) script for summer_fabricate_3d and deciding whether fabrication, generation, or the free library is the right route for an asset
  • A parametric hard-surface set with exact dimensions, booleans, bevels, and shared materials that must stay consistent across many pieces
  • VFX meshes — shatter fragments, curve sweeps, trails, LOD chains — or decimating, UV-unwrapping, and baking a generated model before it goes into the scene
  • A fabrication failed (blender_not_found, script_error, export_empty, import_failed, busy, timeout) and the result needs to be read correctly

Install

summer setup installs every skill for your agent. To add only this one:
Swap claude-code for your agent, such as codex or cursor. See Skills for every option.

The skill

Overview

summer_fabricate_3d runs a Blender Python (bpy) script you write in the user’s own installed Blender, headless and supervised by the engine, validates the .glb the script’s objects were exported to, moves it under res://, waits for the import, and (optionally) instantiates it into the open scene with target_size normalization. One call: script → glb → import → scene. It exists for a narrow slice of assets: the mesh-processing mid-tier that the engine does not have (exact booleans, bevels, UV unwrap, decimate, remesh, curve sweeps, fracture) and that generation and libraries serve badly. Everywhere else it is the wrong tool — route first. Three facts shape every use:
  1. It is optional and detect-never-bundle. Summer does not ship, download, or install Blender. If the machine has none, the result is a structured blender_not_found with the fix, and the decision to install belongs to the user.
  2. It is blind until import. There is no viewport while the script runs. Perception comes back only through the result (objects, dimensions, warnings, skipped) and then through the normal scene loop (summer_snapshot_diff + summer_screenshot).
  3. One fabrication at a time per editor. A second call while one is in flight fails with busy. Wait for the result; never fire in parallel.

Route first — fabricate, generate, or import?

Fabricate wins in exactly three rows. If the request is not one of them, stop here and use asset-strategy to pick the specialist skill.

Prerequisite — the user’s Blender

The engine looks for Blender 4.2 LTS or newer in this order:
  1. blender_path passed in the call (only when the user named a path — never guess one);
  2. the SUMMER_BLENDER_BIN environment variable of the editor process;
  3. Editor Settings → FileSystem → Import → Blender → Blender Path (filesystem/import/blender/blender_path, the same setting .blend import uses);
  4. PATH, the platform’s well-known install locations, and ~/.summer/tools/blender-<version>/.
When none resolves, the result is failure_reason: blender_not_found with tried (every path checked) and a prescriptive error. Relay it as a one-line decision: “Fabrication needs Blender (4.2 LTS or newer) on this machine. Install it from the official Blender download page, or point SUMMER_BLENDER_BIN at an existing executable and restart the editor. Or I can generate the asset or import one from the free library instead.” Then do what the user chooses. Do not install software on their behalf, and do not retry the call hoping the answer changes. The blender_version in every successful result is a fact, not a suggestion: bpy APIs change across majors, and models skew toward older bpy from training data. When a script_error names a missing attribute or operator, check it against the version that actually ran.

The script contract

source is plain bpy code. It runs in a factory-startup Blender (no user add-ons, no preferences, online access off) with an empty scene — no default cube, light, or camera — and these names already bound: bpy, bmesh, mathutils, math, Vector, Matrix, Euler. The script must create mesh objects and leave them linked in the scene. Nothing else. The engine’s bootstrap then collects, audits, exports, and validates:
  • Do not export, do not save, do not quit. bpy.ops.export_scene.gltf, bpy.ops.wm.save_mainfile, bpy.ops.wm.quit_blender in your script break the contract and the run.
  • What exports: MESH, CURVE, SURFACE, META, FONT objects become meshes; EMPTY and ARMATURE keep hierarchy and skins; cameras, lights, probes and speakers are dropped.
  • Hidden means helper. An object hidden in the viewport or disabled for render is treated as a boolean cutter or guide: it is not exported and is listed in skipped. Use this deliberately for cutters; check skipped when a piece is missing.
  • Modifiers are applied at export (export_apply), including Geometry Nodes — the scene stays non-destructive, so do not modifier_apply by hand unless you need the result for a later step (a boolean whose output feeds a second boolean, for instance). Consequence: shape keys are dropped when modifiers exist; a warning says so.
  • Only a Principled BSDF surface survives glTF. Any other shader (Emission-only, Diffuse, custom node group) exports grey or black — a warning names the material. Build materials as Principled BSDF with constant values or Image Texture inputs.
  • Procedural Base Color is baked for you (Cycles, 2K cap, capped material count; meshes without UVs get a Smart UV Project first) and reported in baked. Procedural Metallic / Roughness / Normal / Emission / Alpha are NOT baked — set constants, or bake them to an Image Texture in your script; the warnings list tells you which channel was left procedural.
  • Axis and units: model in Blender’s native Z-up, in metres; the export converts to the engine’s +Y-up (dimensions in the result is already +Y-up). A 2 m wall segment is dimensions.y ≈ 2.
  • Naming: object names become node names in the imported scene. Name every object and material (obj.name = "Wall_2m_A"). The name argument names the .glb (res://assets/fabricated/<name>.glb unless out_path says otherwise) and the instantiated node; it is sanitized to [A-Za-z0-9_-].
  • Determinism: seed anything random (random.seed(7), noise textures with fixed seeds). A re-run must produce the same mesh so the loop below converges.
  • No network. Blender’s online access is off; scripts that fetch anything fail. Reference images and existing models come from the project on disk (below).

Minimal template

Call it with name: "wall_2m_door", import_to_scene: {parent: "./Kit"}, no target_size (the piece is already modelled at 2 × 0.2 × 3 m — a kit that snaps must keep its authored dimensions).

The loop

  1. summer_get_project_context — confirm the engine is up and which project is bound; read projectPath if the script needs a file from the project.
  2. summer_world_snapshot — the BEFORE baseline; keep snapshot_id.
  3. summer_fabricate_3d — source, name, import_to_scene: {parent, position?}, and target_size only when the asset is not dimension-authored (a hero prop whose size you are normalizing, not a kit piece). Default max_seconds 120 covers boot + script + bake + export for ordinary pieces; raise it (up to 600) for exact booleans on dense meshes or several bakes.
  4. Read the result before looking at anything else. ok and ran; objects[] (each with vertices, faces, triangles, materials, modifiers) against what you meant to build; dimensions in +Y-up metres against the spec; skipped for pieces that vanished because they were hidden; warnings for grey-material and unbaked-channel problems; baked for what the bootstrap did to your materials; output for your own print()s. A non-zero exit_code after a validated export is a warning, not a failure — the artifact is what was verified.
  5. summer_snapshot_diff from_id:<the id> — the structural receipt: exactly one new subtree under parent named <name>, nothing else changed.
  6. summer_screenshot — look at it. target:"scene" framing:"camera" when materials or lighting matter; a preset framing for silhouette and scale checks.
  7. Iterate on the script and re-run with the same name — the .glb is overwritten in place (overwrote_existing: true), and the previously instantiated node is still in the scene: remove it with summer_remove_node before re-importing to scene, or import without import_to_scene and let the existing instance pick up the new file.
  8. summer_save_scene once the piece is right. Instantiation is one undo action in the open scene, not a save — an unsaved instance is lost when the editor closes.
Never claim a visual result without step 6. The full perception discipline is verifying-scenes.

Post-processing a generated model

Generation delivers dense triangulated geometry with poor UVs. Fabrication is the fix:
Substitute the real absolute project path for PROJECT_DIR (a string literal built from summer_get_project_context → projectPath); scripts have no res://. Write the result to a new out_path (res://assets/generated/goblin_statue_lod1.glb) so the original stays for comparison, and compare objects[].triangles before and after in the result.

Shatter fragments without add-ons

Factory startup disables add-ons, so cell-fracture operators are not available. Cut with bmesh.ops.bisect_plane in a loop over random planes, bmesh.ops.split_edges / separate by loose parts, and name each fragment Shard_00, Shard_01, … — the imported scene then carries one MeshInstance3D per shard, ready for a RigidBody3D wrap in a summer_run_script pass.

Failure taxonomy — read failure_reason, then act

Every failure leaves the project untouched except import_failed / instantiate_failed, which are honest about the .glb they did write. A no_rewind_point: true on any result means no SummerGit checkpoint exists for the run — say so before doing more destructive work.

Licensing and attribution

  • Blender is the user’s. Summer runs the executable they installed as a separate process and talks to it only through arguments, files, and the exit code. Nothing in Summer links against Blender or imports bpy; the small export bootstrap the engine writes to a scratch directory is a separately licensed GPL file precisely because it does. Do not move project logic into bpy scripts expecting it to stay proprietary.
  • Output is unencumbered. Meshes made with Blender belong to the user; the .glb in their project carries no Blender licence obligation.
  • Scripts you write here are created on the user’s machine and never distributed by Summer. If you adapt a bpy snippet from elsewhere, respect its licence — prefer your own code or CC0 sources, and record the origin in .summer/memory/decisions/ when it matters for the project.
  • Trademark. “Blender” is a trademark of the Blender Foundation. Refer to it factually as the dependency (“requires Blender”); never describe the feature as powered by, built on, or branded with Blender, and never use its logo. Summer’s name for this capability is mesh fabrication.
  • Imported library assets keep their own licences (assets.search results carry license); fabrication does not change that.

Red Flags — STOP

Related skills:
  • asset-strategy — the route decision for every “I need a [thing]”.
  • environment-kit, prop-model, character-model — the generation and library specialists fabrication defers to.
  • scene-scripting — in-scene geometry, placement loops, and wrapping fabricated pieces in bodies and collisions.
  • verifying-scenes and verification-before-completion — the evidence discipline after import.

From a shell

No Summer MCP tools in this session? Run each one as a command; when summer --version is missing or below 4.0, use npx -y summer-engine@latest in place of summer. Fields are flags in kebab-case (--scene-path res://main.tscn) or one --input '<json>', and summer ops describe <op> prints the exact input. The summer-cli skill has the rest.
  • asset-strategy: Route any ‘I need a [thing]’ asset request to the right skill: 2D, 3D, audio, video, VFX or animation.
  • environment-kit: Generate a modular environment kit: walls, floor tiles, pillars, doors, arches in one visual style.
  • prop-model: Generate one static 3D prop (sword, barrel, chest, lantern, statue): one object, no rig, a MeshInstance3D.
  • character-model: Generate a rigged humanoid (player, NPC, enemy, boss) from a T-pose reference, previewed before rigging.
  • scene-scripting: Build scenes, levels, HUDs and wiring with one editor GDScript (summer_run_script) instead of call chains.
  • verifying-scenes: Prove scene work landed: snapshot, diff, screenshots, runtime reads, 3D audits, shot sheets and zooms.
  • verification-before-completion: Run verification and read its output before claiming work done, fixed or passing: evidence first.

Source: library/skills/fabricating-assets/SKILL.md, rendered from summer-engine@4.3.0 on npm.