bambu-3mf-export
NewExport finished STL meshes to a real Bambu Studio .3mf PROJECT with print settings baked in, no "not from Bambu Lab, load geometry data only" warning, support/infill/profile already set. Use when the user wants a sliceable/printable file for a Bambu Lab printer (A1/A1 Mini/P1/X1), to "export to 3mf", bake print settings, split parts across plates, or set up FINE/FAST print profiles. Also the reference for Bambu .3mf internals and lib3mf gotchas. Pairs with the 3d-print-modeling skill.
Summary
3mf project files with print settings baked in, eliminating the 'not from Bambu Lab' warning.
- It packages watertight STL meshes into multi-plate projects with per-object overrides for support, infill, brim, and print profiles (FINE/FAST), ready for slicing and printing on Bambu Lab printers.
Overview
Bambu .3mf export (settings baked in)
A plain trimesh/lib3mf .3mf carries geometry only, so Bambu Studio shows "The 3mf is not from Bambu Lab, load geometry data only" and you re-enter every setting by hand. This skill writes the same package layout Bambu Studio itself writes, so the file opens as a real project with the process / filament / printer + per-object support, infill, and brim already set.
How to use
- Run the project's
build.pyfirst so the STLs are current and verified watertight + single-body.
Only PRINTED parts go in (bought parts, motors, bearings, screws, clutches, are excluded).
- Copy
scripts/bambu3mf.py+scripts/bambu_profile_template.jsoninto the project. - Adapt
scripts/export_bambu_example.py→export_bambu.py: list your parts under FINE/FAST,
set each part's print orientation rotation and per-object overrides, tune FINE/FAST settings.
python3 export_bambu.py→bambu_fine1.3mf,bambu_fast1.3mf, ... auto-packed onto 256×256
plates, bed-checked, settings embedded. Gitignore the .3mf (regenerable).
write_bambu_3mf(path, plates, overrides, plate_name=None) is the one call you need. plates is a list of {name, parts:[...]}, where each part is {name, mesh, pos:(x,y), obj_settings:{...}}; the writer tiles plates along +X, maps them to Bambu plater_ids 1..N (named via each plate's name), centres each part on its bed, and drops it to z=0. Backward-compat: pass a flat list of part dicts and it's treated as one plate (named via plate_name). overrides are project_settings.config keys (the global process/filament/printer profile); obj_settings are per-object keys written into model_settings.config.
The two-profile strategy (fast + light bulk)
Split parts so only detail needs fine layers:
- •FINE, gears, threads, contact surfaces: 0.16 mm, 4 walls, denser infill (25–50 % on
load-bearing rims/races).
- •FAST, housings, enclosures, big simple parts: 0.28 mm layer (~40 % less print TIME),
3 walls, 10 % lightning infill, with per-object overrides bumping structural parts (bearing/ motor seats) back to 4 walls / 15 %.
On one measured leg, FAST-style settings (0 % infill + 0.28 mm + 2 walls) cut −44 % time / −38 % filament vs the 15 %/0.20 default. The layer height is the big time lever; infill + walls are the material levers.
A third named profile shows up often enough to keep around:
- •CLEAR, a see-through PETG lid / window: 0.16 mm, 100 % infill, and
ironing_type: "top"
(+ ironing_flow, ironing_spacing) for a glassy top face. Transparent PETG only reads clear with solid infill, sparse infill is opaque and ugly through the wall. (A clear hollow chamber is the opposite case: 0 % infill + a thin defined shell + a tapered self-supporting roof, see the fdm-design-rules hollowing note.) ironing_type: "top" is also the move for any flush-engraved top surface, e.g. an angle scale recessed into a face, so it reads cleanly.
Group plates by filament too, not only by quality
Quality (FINE/FAST/CLEAR) and material/color are independent axes. The A1 prints one filament per job (single nozzle), so when parts span colors or materials the real split is by filament. The turntable shipped four filament groups: white PLA (top plate), black PLA (shell + cradle), red PETG-fine (gears, threads), red PETG-med (bulk mechanism). Each group is its own *_SETTINGS dict (own filament + nozzle/bed temps + the right quality) fed through the same emit(); name the outputs bambu_<material><n>.3mf so you know which spool to load. You usually pick one filament group AND one quality per plate (e.g. red PETG at FINE for gears, black PLA at FAST for the shell).
True multi-color within one plate is a different mechanism: it needs the AMS and a per-object filament_id in model_settings.config (more config, and a real wipe-tower time/material cost). For single-nozzle, one-color-per-plate, don't reach for that, just group the parts and emit separate files.
A bigger nozzle (0.6 / 0.8 mm) is not a one-key change
Setting nozzle_diameter: "0.8" alone makes perimeter-heavy parts slower: flow stays capped at the 0.4 profile's filament_max_volumetric_speed (~12 mm³/s), so wider lines just take longer per mm. To get the time win: raise filament_max_volumetric_speed to ~21, scale the *_line_width keys to the nozzle, go coarser on layer_height (0.5–0.6), and set print_compatible_printers to the 0.8-nozzle machine or Studio refuses the plate with "not compatible". The catch: a 0.8 nozzle's ±0.25 mm tolerance swamps a designed 0.1–0.2 mm interference, so press-fits become glue-fits, keep bearing seats and snap pins on a 0.4 plate, run the bulk structural parts on the 0.8. (Design side of this is in the fdm-design-rules reference.)
Support: per-object, auto tree, always check
- •Bambu honors support PER-OBJECT (
model_settings.config), NOT the global project flag from a
hand-built .3mf. The example stamps support onto every object via the SUPPORT dict, keep that.
- •Auto tree support ON is the safe default. It builds only under real overhangs and leaves
self-supporting faces (open-top cavities, ramped catches, flat-top-down lids) bare, and peels off easily. Disabling support globally to "save time" silently fails the parts that needed it (enclosure roofs, wire holes, recess ceilings). Don't set enable_support:0 without checking each part's overhangs.
Orientation + packing
- •Load each part in its print orientation: lids and flat-faced detail parts flipped top-down
(FLIP_X) so the smooth face / detail lands on the bed and cavities open upward (self-supporting).
- •
shelf_packis a first-fit-decreasing nest: turns parts landscape (longest edge along +X), rows
tallest-first, opens a new plate when full. The export bed-checks every part (flags "OFF PLATE").
- •Pack brim-aware, not bounds-aware. The global profile gives every part an outer brim, so nest
the brim-grown footprint (bbox + 2·brim_width per axis), not the raw mesh bounds, and keep a small air gap (~6 mm) between footprints. The part centre = footprint centre (a symmetric outer brim stays centred), so the recorded pos still lands the mesh in-bed. Skip this and neighbouring brims merge into one blob and the bed-check passes a part whose brim actually hangs off the plate.
Group + name plates by category (selective printing)
Don't dump all parts onto sequential "Plate 1/2/3". Group by function/category and name each plate after its category, so the Bambu Studio plate tabs read "Bolts", "Connectors", etc. and a user can print just one category by printing one plate. In this project the category map lives in the viewer (tools/viewer.py CATEGORIES + CAT_ORDER); the exporter imports it (from viewer import ...) so the two never drift, define the grouping once. Pattern: bucket parts by category, shelf_pack each bucket independently onto its own plate(s), and when a category overflows the bed name the spill "Cat (1/2)", "Cat (2/2)"; a single-plate category stays just "Cat". This composes with the filament-grouping axis below: group by filament first (one spool per job), by category within that.
PETG / water parts
Override the profile to Generic PETG @ A1: 240/250 °C nozzle, 80 °C textured-PEI bed, reduced cooling (none for the first ~3 layers), 8 mm outer brim, slow ~20 mm/s first layer, ~0.15 mm elephant-foot comp, and for watertight: 6 wall loops + 8 top / 8 bottom shells, ~40 % infill on chunky bosses. Print on a clean plate (wipe PEI with IPA, skin oils kill PETG grip). See the fdm-design-rules reference in the 3d-print-modeling skill for the warp/adhesion story.
What makes a .3mf "a Bambu project" (reverse-engineered)
The package (a ZIP) needs:
- •
3D/3dmodel.model, meshes + the production ('p') extension (p:UUID on objects/build items) +
<metadata name="Application">BambuStudio-....
- •
Metadata/project_settings.config, the ~557-key process/filament/printer JSON. Start from a
validated template lifted from a real Studio export (bambu_profile_template.json, an "0.20mm Standard @BBL A1 / Generic PLA" baseline) and override only the keys you care about.
- •
Metadata/model_settings.config, object names, the<plate>mapping, per-object overrides. - •
Metadata/slice_info.config,[Content_Types].xml,_rels/.rels, package glue.
lib3mf / .3mf gotchas (the reason we hand-assemble the ZIP)
- •lib3mf silently DROPS Bambu sidecar configs on write. A
model.QueryWriter("3mf").WriteToFile()
round-trip loses project_settings.config / model_settings.config / subtypes. So bambu3mf.py assembles the ZIP by hand (raw XML strings). Use lib3mf write ONLY for geometry-only output.
- •A Bambu
.3mfis standard 3MF plus Bambu-only sidecars. Standard geometry (meshes, transforms,
components, build items) is safe through lib3mf; the Bambu layer (negative_part/modifier_part subtypes, plate config, slicer profile, thumbnails) needs raw ZIP+XML.
- •Object IDs renumber, Bambu reassigns top-level IDs when objects are added/removed in Studio.
Don't hardcode them across sessions; re-inspect first. (The writer here assigns ids from 2 up.)
- •Two transform conventions coexist in one file.
3dmodel.model<item>/<component>transform
= 12 floats (row-major 4×3, translation in the last 3). model_settings.config <metadata key="matrix"> = 16 floats (row-major 4×4, translation in the 4th column of each row). Same effective transform, different shape, don't paste between them.
- •`negative_part` only carves as a sub-part of the same `<object>`. A standalone object marked
negative_part does nothing; it must live inside the target object's <components> AND have a matching <part subtype="negative_part"> in model_settings.config under that object's id. And the slicer does union(positives) − union(negatives) in one pass, so a positive feature placed inside a negative cavity gets subtracted away, redefine the cavity mesh to exclude that volume (do it as CSG in build.py instead of fighting subtypes).
- •Three.js `3MFLoader` ignores Bambu's multi-file production extension (separate
/3D/Objects/object_N.model files) → gives an empty scene. For the viewer, bake to GLB.
- •Hand-assembled XML must escape attribute values. Plate/part/category names get interpolated
straight into value="..." attributes. An unescaped &, <, >, or " (e.g. plate name "Nuts & inserts") makes model_settings.config malformed and Bambu Studio fails to load the whole project. Run every interpolated value through _a() (xml.sax.saxutils.escape + "). Numeric override values are safe, names are not.
- •Plate/object names are validated as filenames. Bambu Studio rejects
< > : / \ | ? * "in a
plate or object name with "Invalid name, the following characters are not allowed". This bites the obvious "Cat (1/2)" plate label, the / is illegal. Use "Cat 1 of 2". (& is allowed by the name validator but still needs XML-escaping per the rule above.)
- •Multi-plate: match Bambu's PartPlate grid or parts float off the plates. Each object's world
coords decide which plate cell it's drawn in. Bambu lays plates out as a grid, NOT a single row: cols = compute_colum_count(n) = round(sqrt(n)) (+1 when sqrt(n) > round(sqrt(n))), stride = bed_size * (1 + 1/5) (LOGICAL_PART_PLATE_GAP), plate i origin = (col*stride, -row*stride) with row, col = divmod(i, cols) (rows march toward -Y). Place each part at plate_origin + bed_local_pos. A naive i*(bed+gap) along +X puts every part in one line hovering off the plate grid. (Source: src/slic3r/GUI/PartPlate.cpp, compute_shape_position.)
Headless slicing for measurement (BambuStudio CLI)
To actually measure print time / filament (not estimate), see the foreign-cad-import skill's "BambuStudio CLI" section, flatten the inherits profile chain, set curr_bed_type to "Textured PEI Plate" for PETG, put overrides in the flattened process JSON, then BambuStudio --load-settings "m.json;p.json" --load-filaments "f.json" --slice 0 --outputdir OUT model.stl.
Install & Usage
mkdir -p .claude/skillsmkdir -p .claude/skills && curl -o .claude/skills/bambu-3mf-export.md https://raw.githubusercontent.com/m-esm/bambu-3mf-export/main/SKILL.md/bambu-3mf-exportUse Cases
Usage Examples
/bambu-3mf-export Export the current project's printed parts as bambu_fine1.3mf with FINE profile and auto-tiling.
Run export_bambu.py to generate .3mf files for both FINE and FAST profiles from the STLs in build/.
I need a .3mf for my Bambu P1S with these parts: base.stl, lid.stl, and handle.stl. Use FINE settings with 15% grid infill and brim on the base.
Security Audits
Frequently Asked Questions
What is bambu-3mf-export?
This skill generates Bambu Studio-compatible .3mf project files with print settings baked in, eliminating the 'not from Bambu Lab' warning. It packages watertight STL meshes into multi-plate projects with per-object overrides for support, infill, brim, and print profiles (FINE/FAST), ready for slicing and printing on Bambu Lab printers.
How to install bambu-3mf-export?
To install bambu-3mf-export: create the skills directory (mkdir -p .claude/skills), then run: mkdir -p .claude/skills && curl -o .claude/skills/bambu-3mf-export.md https://raw.githubusercontent.com/m-esm/bambu-3mf-export/main/SKILL.md. Finally, /bambu-3mf-export in Claude Code.
What is bambu-3mf-export best for?
bambu-3mf-export is a skill categorized under General. Created by m-esm.
What can I use bambu-3mf-export for?
bambu-3mf-export is useful for: Export a multi-part 3D model as a .3mf file with FINE and FAST print profiles for a Bambu X1C printer.; Bake per-object support enforcers and infill settings into a .3mf project to avoid manual setup in Bambu Studio.; Automatically split a large assembly across multiple 256x256 plates, each with its own plate name and print settings.; Generate a sliceable .3mf file for a Bambu A1 Mini from a watertight STL, with brim and tree supports pre-configured.; Convert a flat list of STL parts into a tiled .3mf project with centered parts and z=0 drop for optimal bed adhesion.; Create a reproducible .3mf export pipeline that regenerates from source STLs, ensuring print settings stay version-controlled..