You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Negative volumes in a third-party 2.x-style 3MF are printed solid — exit 0, no message (3.0.0-alpha11) #15694
A 3MF that carries its volume types in Metadata/Slic3r_PE_model.config (the PrusaSlicer 2.x project layout) is loaded as plain geometry unless its value is PrusaSlicer- followed by a remainder that parses as a Semver and is ≤ 2.99.1. Every volume then becomes a ModelPart, so a NegativeVolume is sliced as solid geometry — with exit code 0, the normal Slicing result exported to … line and empty stderr.
One archive, sliced five times with the same profiles, with only that metadata value changed between runs:
filament used [mm]
last ;Z:
objects_info
ThirdPartyTool-1.0
8397.98
55
file stem, 40 × 40 mm
PrusaSlicer-3.0.0
8397.98
55
file stem, 40 × 40 mm
PrusaSlicer-notaversion
8397.98
55
file stem, 40 × 40 mm
PrusaSlicer-2.99.1
1955.53
15
CubeObj, 30 × 30 mm
PrusaSlicer-2.9.6
1955.53
15
CubeObj, 30 × 30 mm
The last two rows are the intended result: the 40 mm NegativeVolume removes the top of the 30 mm body. The other three slice the cutter as solid geometry — 4.3× the filament of the correct part, and 2.5× the body on its own (solid.3mf: 3417.63 mm, last ;Z:30). The object also keeps the file stem instead of the name from the model config.
PrusaSlicer 2.9.6 slices the ThirdPartyTool-1.0 and PrusaSlicer-2.9.6 files to the same G-code (2328.01 mm, last ;Z:15.05, both 135 924 bytes; diff reports exactly one differing line, the generation timestamp in the header). It applies the volume types regardless of the producer string.
Source, tag version_3.0.0-alpha11
src/slic3r-shared/src/Slic3r/Biz/Format/3mf/Model3mf.cpp:916is_old_stored_version() requires all three: the Application metadata starts with "PrusaSlicer-" (:921), the remainder parses as a Semver (:923), and that version is ≤ last_old_stored_version (2.99.1, :150) (:926). :943-:949 then records Read3mfIssueType::legacy_loader_required (:944) and stops the XML parser (:949).
src/slic3r-shared/src/Slic3r/Biz/Format/3mf.cpp:671-672 throws on that issue, and FileLoadingLogic.cpp:668 → :671 reaches load_legacy_project() (:579) only through that exception. Both CLI branches arrive there: the geometry path calls load_from_project(input_file_path, std::nullopt) at FileLoadingLogic.cpp:721, and src/slic3r-app-cli/src/Slic3r/App/CLI/LoadPrintData.cpp:210-223 routes to that geometry path whenever profile options are given (has_full_config_from_profiles(), comment: "We have a full bunch of options from profiles set, so just load a geometry.").
The new loader never opens Metadata/Slic3r_PE_model.config; that name exists only in the legacy reader (src/slic3r-shared/src/Slic3r/Biz/Config/Legacy/3mf_legacy.cpp:128). In the new loader the file merely becomes Read3mfIssueType::unprocessed_file_in_3mf (Format/3mf.cpp:745), and src/slic3r-app-cli/ contains no reference to the collected issues, so nothing is printed.
Expected: the volume types are applied for any producer (2.9.6 does this), or the file is refused with a message. The alpha11 release notes say "loading projects using custom printers or projects from other slicers is not fully supported yet. You may encounter errors in these cases." — here there is no error at all, only a different part. The same section states the principle directly for the reverse direction: "If you are running PrusaSlicer 2.9.1 or later, you will be notified about this. In older PrusaSlicer versions, the config will be skipped silently." Silently skipping the config is treated there as the old behaviour to be avoided.
Possible fix: choose the legacy reader by archive content (Metadata/Slic3r_PE_model.config present, Metadata/PrusaSlicer3_project.json absent) instead of by the Application string. The file also already announces itself with a key the new loader parses: slic3rpe:Version3mf is mapped to ModelMetadataNames::Slic3r_version at Format/3mf/Metadata.cpp:24 and read back through read_name() (:66), but every use of that value is commented out (Metadata.cpp:50, Model3mf.cpp:1893, :1907). Gating on either signal would not depend on the producer name at all. If neither is wanted, at least surface the ignored file on the CLI rather than dropping the unprocessed_file_in_3mf issue.
Worth knowing before suggesting the obvious workaround: stamping the file PrusaSlicer-2.9.6 routes it to the legacy loader, which then depends on the printer config being resolvable. With the three profile options used below, --info succeeds (exit 0). Without them it fails — "%PS3%" --datadir "%DD%" --info neg_prusaslicer.3mf gives exit 1 and neg_prusaslicer.3mf: Loading file failed: Cannot find compatible printer HW config, while the same command on neg_thirdparty.3mf gives exit 0 and reports the cutter as geometry (number_of_facets = 24, number_of_parts = 2, size_z = 55).
Related, not duplicates: #15662 (third-party 3MF metadata, object names only) and #15644 (2.x project crashes on preset save).
Zipped project file & How to reproduce
Nothing attached — the three 3MFs are 1.4–1.6 kB and are written by the script at the bottom.
1. Unpack PrusaSlicer-3.0.0-alpha11.zip. Point --datadir at a directory a GUI run has already initialised and that carries the MK4 presets used below. With an empty --datadir the alpha crashes while populating the vendor bundle (exit -1073740791 / 0xC0000409, last log line [BundleLoader.cpp:43] Populate vendor prusa-research-fff/PrusaResearch, nothing on stderr) — unrelated to this report, but it will stop you before you get here.
2. In an empty folder, save the script below as make_3mf.py and run it:
python make_3mf.py
wrote solid.3mf 1450 bytes
wrote neg_thirdparty.3mf 1582 bytes
wrote neg_prusaslicer.3mf 1581 bytes
solid.3mf is a 30 mm cube. neg_thirdparty.3mf is that cube plus a 40 mm box (triangles 12..23) declared volume_type=NegativeVolume in Metadata/Slic3r_PE_model.config. neg_prusaslicer.3mf is the same archive with changed from ThirdPartyTool-1.0 to PrusaSlicer-2.9.6; every other byte of every part is identical.
Each run: exit 0, stderr empty (0 bytes). The only non-log line on stdout is Slicing result exported to a_*.gcode; spdlog output also goes to stdout and, depending on the data directory, may include unrelated [error] lines from preset loading — 107 stdout lines, 85 of them [error], in my run, identical for all three files (e.g. Invalid key top_one_perimeter_type). Nothing in that output refers to the 3MF, the ignored config file, or the volume types.
3b. The profile options are not what causes this. LoadPrintData.cpp:210-223 sends the file to load_geometry_project whenever profiles are given, so repeat without them:
a_solid.gcode:; objects_info = {"objects":[{"name":"solid","polygon":[[143.000,143.000],[113.000,143.000],[113.000,113.000],[143.000,113.000]]}]}
a_solid.gcode:; filament used [mm] = 3417.63
a_third.gcode:; objects_info = {"objects":[{"name":"neg_thirdparty","polygon":[[148.000,148.000],[108.000,148.000],[108.000,108.000],[148.000,108.000]]}]}
a_third.gcode:; filament used [mm] = 8397.98
a_prusa.gcode:; objects_info = {"objects":[{"name":"CubeObj","polygon":[[113.000,113.000],[143.000,113.000],[143.000,143.000],[113.000,143.000]]}]}
a_prusa.gcode:; filament used [mm] = 1955.53
The top layer is the last ;Z: line of each file: ;Z:30 for a_solid, ;Z:55 for a_third, ;Z:15 for a_prusa. The footprint in objects_info is 40 × 40 mm for a_third and 30 × 30 mm for a_prusa.
4b. To see that the gate is the parsed version and not the PrusaSlicer- prefix, copy neg_thirdparty.3mf three more times, replacing ThirdPartyTool-1.0 in 3D/3dmodel.model with PrusaSlicer-3.0.0, PrusaSlicer-notaversion and PrusaSlicer-2.99.1, then slice as in step 3. The first two give 8397.98 mm and last ;Z:55; only PrusaSlicer-2.99.1 gives 1955.53 mm and last ;Z:15.
5. Optional 2.9.6 comparison, built-in defaults, fresh data directory so no installed preset interferes:
p_third.gcode:; filament used [mm] = 2328.01
p_prusa.gcode:; filament used [mm] = 2328.01
Both 135 924 bytes, last ;Z:15.05, and identical apart from the timestamp in the first line — 2.9.6 subtracts the cutter for both producers.
make_3mf.py — python3, standard library only
# Writes three 2.x-style 3MF projects (stdlib only, python3 make_3mf.py).# Body: 30 mm cube, triangles 0..11, z 0..30 after the build transform.# Cutter: 40 mm box, triangles 12..23, z 15..55, declared NegativeVolume in# Metadata/Slic3r_PE_model.config -> expected: top of the body removed, max Z 15.# neg_thirdparty.3mf and neg_prusaslicer.3mf differ ONLY in .importzipfile, osdefbox(z0, z1, half):
v= [(-half,-half,z1),(half,-half,z1),(half,half,z1),(-half,half,z1),
(-half,-half,z0),(half,-half,z0),(half,half,z0),(-half,half,z0)]
t= [(0,1,2),(0,2,3),(5,4,7),(5,7,6),(4,0,3),(4,3,7),
(1,5,6),(1,6,2),(4,5,1),(4,1,0),(3,2,6),(3,6,7)]
returnv, tdefmodel(app, cutter):
verts, tris=box(-15, 15, 15) # bodyifcutter:
v2, t2=box(0, 40, 20) # cutter, ids 12..23n=len(verts); verts+=v2; tris+= [(a+n,b+n,c+n) fora,b,cint2]
vx="".join(''%pforpinverts)
tx="".join(''%pforpintris)
return ('\n''' xmlns:slic3rpe="http://schemas.slic3r.org/3mf/2017/06"'' xmlns="http://schemas.microsoft.com/3dmanufacturing/core/2015/02">''1''%s'''%s%s''''''\n') % (app, vx, tx)
defcfg(cutter):
extra= ('''''''') ifcutterelse''return ('\n'''''''''''%s\n') %extraCT= ('\n'package/2006/content-types">'openxmlformats-package.relationships+xml"/>\n')
RELS= ('\n'openxmlformats.org/package/2006/relationships">' Id="rel-1" Type="http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel"/>''\n')
defwrite(name, app, cutter):
withzipfile.ZipFile(name, "w", zipfile.ZIP_DEFLATED) asz:
z.writestr("[Content_Types].xml", CT)
z.writestr("_rels/.rels", RELS)
z.writestr("3D/3dmodel.model", model(app, cutter))
z.writestr("Metadata/Slic3r_PE_model.config", cfg(cutter))
print("wrote", name, os.path.getsize(name), "bytes")
write("solid.3mf", "ThirdPartyTool-1.0", False)
write("neg_thirdparty.3mf", "ThirdPartyTool-1.0", True)
write("neg_prusaslicer.3mf", "PrusaSlicer-2.9.6", True)
Context: this was found by a CLI automation tool that writes 2.x-style projects. Modifier, support-blocker and support-enforcer volumes are declared in the same file and pass through the same gate; only NegativeVolume was measured here.
Version of PrusaSlicer
3.0.0-alpha11 (Windows zip, PrusaSlicer.exe), compared against 2.9.6
Description of the bug
A 3MF that carries its volume types in
Metadata/Slic3r_PE_model.config(the PrusaSlicer 2.x project layout) is loaded as plain geometry unless itsvalue isPrusaSlicer-followed by a remainder that parses as a Semver and is ≤ 2.99.1. Every volume then becomes aModelPart, so aNegativeVolumeis sliced as solid geometry — with exit code 0, the normalSlicing result exported to …line and empty stderr.One archive, sliced five times with the same profiles, with only that metadata value changed between runs:
;Z:objects_infoThirdPartyTool-1.0PrusaSlicer-3.0.0PrusaSlicer-notaversionPrusaSlicer-2.99.1CubeObj, 30 × 30 mmPrusaSlicer-2.9.6CubeObj, 30 × 30 mmThe last two rows are the intended result: the 40 mm
NegativeVolumeremoves the top of the 30 mm body. The other three slice the cutter as solid geometry — 4.3× the filament of the correct part, and 2.5× the body on its own (solid.3mf: 3417.63 mm, last;Z:30). The object also keeps the file stem instead of the name from the model config.PrusaSlicer 2.9.6 slices the
ThirdPartyTool-1.0andPrusaSlicer-2.9.6files to the same G-code (2328.01 mm, last;Z:15.05, both 135 924 bytes;diffreports exactly one differing line, the generation timestamp in the header). It applies the volume types regardless of the producer string.Source, tag
version_3.0.0-alpha11src/slic3r-shared/src/Slic3r/Biz/Format/3mf/Model3mf.cpp:916is_old_stored_version()requires all three: theApplicationmetadata starts with"PrusaSlicer-"(:921), the remainder parses as a Semver (:923), and that version is ≤last_old_stored_version(2.99.1,:150) (:926).:943-:949then recordsRead3mfIssueType::legacy_loader_required(:944) and stops the XML parser (:949).src/slic3r-shared/src/Slic3r/Biz/Format/3mf.cpp:671-672throws on that issue, andFileLoadingLogic.cpp:668→:671reachesload_legacy_project()(:579) only through that exception. Both CLI branches arrive there: the geometry path callsload_from_project(input_file_path, std::nullopt)atFileLoadingLogic.cpp:721, andsrc/slic3r-app-cli/src/Slic3r/App/CLI/LoadPrintData.cpp:210-223routes to that geometry path whenever profile options are given (has_full_config_from_profiles(), comment: "We have a full bunch of options from profiles set, so just load a geometry.").Metadata/Slic3r_PE_model.config; that name exists only in the legacy reader (src/slic3r-shared/src/Slic3r/Biz/Config/Legacy/3mf_legacy.cpp:128). In the new loader the file merely becomesRead3mfIssueType::unprocessed_file_in_3mf(Format/3mf.cpp:745), andsrc/slic3r-app-cli/contains no reference to the collected issues, so nothing is printed.Expected: the volume types are applied for any producer (2.9.6 does this), or the file is refused with a message. The alpha11 release notes say "loading projects using custom printers or projects from other slicers is not fully supported yet. You may encounter errors in these cases." — here there is no error at all, only a different part. The same section states the principle directly for the reverse direction: "If you are running PrusaSlicer 2.9.1 or later, you will be notified about this. In older PrusaSlicer versions, the config will be skipped silently." Silently skipping the config is treated there as the old behaviour to be avoided.
Possible fix: choose the legacy reader by archive content (
Metadata/Slic3r_PE_model.configpresent,Metadata/PrusaSlicer3_project.jsonabsent) instead of by theApplicationstring. The file also already announces itself with a key the new loader parses:slic3rpe:Version3mfis mapped toModelMetadataNames::Slic3r_versionatFormat/3mf/Metadata.cpp:24and read back throughread_name()(:66), but every use of that value is commented out (Metadata.cpp:50,Model3mf.cpp:1893,:1907). Gating on either signal would not depend on the producer name at all. If neither is wanted, at least surface the ignored file on the CLI rather than dropping theunprocessed_file_in_3mfissue.Worth knowing before suggesting the obvious workaround: stamping the file
PrusaSlicer-2.9.6routes it to the legacy loader, which then depends on the printer config being resolvable. With the three profile options used below,--infosucceeds (exit 0). Without them it fails —"%PS3%" --datadir "%DD%" --info neg_prusaslicer.3mfgives exit 1 andneg_prusaslicer.3mf: Loading file failed: Cannot find compatible printer HW config, while the same command onneg_thirdparty.3mfgives exit 0 and reports the cutter as geometry (number_of_facets = 24,number_of_parts = 2,size_z = 55).Related, not duplicates: #15662 (third-party 3MF metadata, object names only) and #15644 (2.x project crashes on preset save).
Zipped project file & How to reproduce
Nothing attached — the three 3MFs are 1.4–1.6 kB and are written by the script at the bottom.
1. Unpack
PrusaSlicer-3.0.0-alpha11.zip. Point--datadirat a directory a GUI run has already initialised and that carries the MK4 presets used below. With an empty--datadirthe alpha crashes while populating the vendor bundle (exit-1073740791/ 0xC0000409, last log line[BundleLoader.cpp:43] Populate vendor prusa-research-fff/PrusaResearch, nothing on stderr) — unrelated to this report, but it will stop you before you get here.2. In an empty folder, save the script below as
make_3mf.pyand run it:solid.3mfis a 30 mm cube.neg_thirdparty.3mfis that cube plus a 40 mm box (triangles 12..23) declaredvolume_type=NegativeVolumeinMetadata/Slic3r_PE_model.config.neg_prusaslicer.3mfis the same archive withchanged fromThirdPartyTool-1.0toPrusaSlicer-2.9.6; every other byte of every part is identical.3. Slice all three from that folder:
Each run: exit 0, stderr empty (0 bytes). The only non-log line on stdout is
Slicing result exported to a_*.gcode; spdlog output also goes to stdout and, depending on the data directory, may include unrelated[error]lines from preset loading — 107 stdout lines, 85 of them[error], in my run, identical for all three files (e.g.Invalid key top_one_perimeter_type). Nothing in that output refers to the 3MF, the ignored config file, or the volume types.3b. The profile options are not what causes this.
LoadPrintData.cpp:210-223sends the file toload_geometry_projectwhenever profiles are given, so repeat without them:neg_thirdparty.3mf: exit 0,; filament used [mm] = 8395.98, last;Z:55— still solid.neg_prusaslicer.3mf: exit 1,neg_prusaslicer.3mf: Loading file failed: Cannot find compatible printer HW config. Both CLI branches reach the same gate: the geometry path callsload_from_projectatFileLoadingLogic.cpp:721.4. Compare:
The top layer is the last
;Z:line of each file:;Z:30fora_solid,;Z:55fora_third,;Z:15fora_prusa. The footprint inobjects_infois 40 × 40 mm fora_thirdand 30 × 30 mm fora_prusa.4b. To see that the gate is the parsed version and not the
PrusaSlicer-prefix, copyneg_thirdparty.3mfthree more times, replacingThirdPartyTool-1.0in3D/3dmodel.modelwithPrusaSlicer-3.0.0,PrusaSlicer-notaversionandPrusaSlicer-2.99.1, then slice as in step 3. The first two give 8397.98 mm and last;Z:55; onlyPrusaSlicer-2.99.1gives 1955.53 mm and last;Z:15.5. Optional 2.9.6 comparison, built-in defaults, fresh data directory so no installed preset interferes:
Both 135 924 bytes, last
;Z:15.05, and identical apart from the timestamp in the first line — 2.9.6 subtracts the cutter for both producers.make_3mf.py— python3, standard library onlyContext: this was found by a CLI automation tool that writes 2.x-style projects. Modifier, support-blocker and support-enforcer volumes are declared in the same file and pass through the same gate; only
NegativeVolumewas measured here.Version of PrusaSlicer
3.0.0-alpha11 (Windows zip,
PrusaSlicer.exe), compared against 2.9.6Operating system
Windows
Operating system version
11 Pro 26200 (x64)
Printer model
Original Prusa MK4