Preserve per-triangle painting through Boolean, merge and Simplify - #11444
Preserve per-triangle painting through Boolean, merge and Simplify#11444Cstm3DBldr wants to merge 2 commits into
Conversation
|
How often do public releases get pushed and what is the deciding factor when to push? |
|
@Haidiye00 still waiting on the merge is there something i need to do to get this pushed in? |
|
Hello @Cstm3DBldr could you help to rebase to the newest code base? sorry previously we missed this PR |
|
@lanewei120 I have a few tools that would be nice to have in the works as well mostly related to painting models. Ill Keep you posted when those are ready to merge and fully working. Thanks for reaching out. |
Boolean, merge-into-single-part and Simplify all rebuild a volume's mesh, and
until now the painted annotations did not survive the rebuild. PaintReproject
already carries paint through cutting and repair; this wires the remaining
mesh-rebuilding operations into it, following the extension point described in
PaintReproject.hpp ("pass the boolean result as the destination mesh and each
participating operand mesh as a source, nearest-distance source selection").
reproject_paint_from_volumes() reprojects one operand at a time through
reproject_paint_geometric - a single source per call, so operands cannot
overwrite one another where they overlap - and each destination face then takes
the paint of the nearest operand that covers it. An operand covers a face only
when the closest point lies inside one of its own faces, which keeps paint from
spreading past a part's outline where two parts share a plane; a tie there goes
to the smaller part. A face no operand reached, such as surface newly cut by a
boolean, takes the plain filament of its nearest operand.
A part's solid colour is carried as well as its brush strokes. A boolean or
merge result is a single volume with a single extruder, so an area left
unpainted would silently take that one colour. TriangleSelector::fill_unpainted()
bakes each operand's own filament into its unpainted areas while leaving painted
detail untouched. This also fixes colour loss in merge-into-single-part, where
the per-face copy left the area around each brush stroke unpainted and those
faces fell back to the merged volume's extruder.
Simplify keeps its paint by going through ModelVolume::set_mesh_keep_paint()
instead of set_mesh(); the clear_before_change_mesh() call that preceded it
reset the paint on every volume of the object, which defeated the reprojection.
Signed-off-by: Cstm3DBldr <34087122+Cstm3DBldr@users.noreply.github.com>
check_boolean_possible() runs CGAL, whose does_self_intersect() test rejects a part outright, while the boolean that follows runs mcut, which copes with far messier meshes. Because a CGAL verdict skipped the attempt entirely, a model with even a couple of overlapping triangles never reached mcut: the operation fell through to the branch below, which merges the parts' meshes and reports a failure. The user asked for a boolean and silently got the parts concatenated, overlap included, plus a warning. Allowing SelfIntersect through lets mcut try. The call is already wrapped in try/catch and its result is only accepted when non-empty, so a mesh mcut cannot handle still lands in the same merge fallback and warning as before. This only turns former hard failures into successes; nothing that already worked changes. Note that combine_mesh_fff() is shared with STL export, so a self-intersecting model now exports the boolean result rather than the merged fallback. Signed-off-by: Cstm3DBldr <34087122+Cstm3DBldr@users.noreply.github.com>
c6e9112 to
ea8cf95
Compare
|
@lanewei120 Rebased onto the latest master — no conflicts, and it shows as mergeable now. One thing I want to flag rather than let you find it in review: this is not the same patch What it does now:
I checked the results by reading the per-triangle annotations back out of saved 3MFs rather Separately, I opened #12088 for the SVG work I mentioned — reading CSS styling and clip |
Follow-up to #11105, which brought painting through merge, split, merge-to-multipart and repair. This covers the remaining mesh-rebuilding operations: Boolean (union / intersection / difference, both the gizmo and the right-click menu), merge into single part, and Simplify.
Rebased onto current master.
Approach
PaintReproject.hppalready described this as the intended extension point:reproject_paint_from_volumes()implements the nearest-distance source selection described there. Each operand is reprojected on its own throughreproject_paint_geometric- a single source per call, so operands cannot overwrite one another where they overlap - and each destination face then takes the paint of the nearest operand that covers it. An operand only counts as covering a face when the closest point lies inside one of its own faces, which stops paint spreading past a part's outline where two parts share a plane; a tie there goes to the smaller part. A face no operand reached, such as surface newly cut by a boolean, takes the plain filament of its nearest operand. The header comment is updated accordingly.Solid part color
A boolean or merge result is a single volume with a single extruder, so any area left unpainted silently takes that one color.
TriangleSelector::fill_unpainted()bakes each operand's own filament into its unpainted areas while leaving painted detail untouched.This also fixes color loss in merge-into-single-part: the per-face copy left the area around each brush stroke unpainted, and those faces fell back to the merged volume's extruder, so a part merged with a differently-colored one lost its color.
Simplify keeps its paint via
ModelVolume::set_mesh_keep_paint()instead ofset_mesh(). Theclear_before_change_mesh()call that preceded it reset the paint on every volume of the object, which defeated the reprojection.Second commit: self-intersecting parts
check_boolean_possible()runs CGAL, whosedoes_self_intersect()rejects a part outright, while the boolean that follows runs mcut, which copes with far messier meshes. Because a CGAL verdict skipped the attempt entirely, a model with even a couple of overlapping triangles never reached mcut - the operation fell through to the branch that merges the parts' meshes and reports a failure, so the user asked for a boolean and silently got the parts concatenated, overlap included.Allowing
SelfIntersectthrough lets mcut try. The call is already wrapped in try/catch and its result is only accepted when non-empty, so a mesh mcut cannot handle still lands in the same merge fallback and warning as before. This only turns former hard failures into successes. Notecombine_mesh_fff()is shared with STL export, so a self-intersecting model now exports the boolean result rather than the merged fallback. Kept as a separate commit so it can be dropped independently.Testing
Verified by decoding the per-triangle annotations out of saved 3MFs rather than by eye. Two parts on different filaments, each brush-painted, then each operation run and saved:
Cut and repair were re-checked for regressions and are unchanged; the surface a cut newly creates stays unpainted, as before.
Also exercised on a real 45,680-triangle sculpted model: unpainted faces dropped from 68.2% to 0.1%, brush detail rose from 704 to 1,063 faces as the boolean subdivided along existing paint boundaries, and the operation succeeded on a mesh that self-intersects.
Demos
Boolean Union (toolbar)

Boolean Intersection (toolbar)

Boolean Subtraction (toolbar)

Right-click Boolean

Simplify, high to low resolution

Merge into single part - the operation this PR fixes for parts on different filaments (clip from #11105, showing the original merge preservation)

Repair (unchanged by this PR, shown for continuity with #11105)
