More Boolean Solvers #120182
Labels
No Label
Interest
Alembic
Interest
Animation & Rigging
Interest
Asset Browser
Interest
Asset Browser Project Overview
Interest
Audio
Interest
Automated Testing
Interest
Blender Asset Bundle
Interest
BlendFile
Interest
Collada
Interest
Compatibility
Interest
Compositing
Interest
Core
Interest
Cycles
Interest
Dependency Graph
Interest
Development Management
Interest
EEVEE
Interest
EEVEE & Viewport
Interest
Freestyle
Interest
Geometry Nodes
Interest
Grease Pencil
Interest
ID Management
Interest
Images & Movies
Interest
Import Export
Interest
Line Art
Interest
Masking
Interest
Metal
Interest
Modeling
Interest
Modifiers
Interest
Motion Tracking
Interest
Nodes & Physics
Interest
OpenGL
Interest
Overlay
Interest
Overrides
Interest
Performance
Interest
Physics
Interest
Pipeline, Assets & IO
Interest
Platforms, Builds & Tests
Interest
Python API
Interest
Render & Cycles
Interest
Render Pipeline
Interest
Sculpt, Paint & Texture
Interest
Text Editor
Interest
Translations
Interest
Triaging
Interest
Undo
Interest
USD
Interest
User Interface
Interest
UV Editing
Interest
VFX & Video
Interest
Video Sequencer
Interest
Virtual Reality
Interest
Vulkan
Interest
Wayland
Interest
Workbench
Interest: X11
Legacy
Blender 2.8 Project
Legacy
Milestone 1: Basic, Local Asset Browser
Legacy
OpenGL Error
Meta
Good First Issue
Meta
Papercut
Meta
Retrospective
Meta
Security
Module
Animation & Rigging
Module
Core
Module
Development Management
Module
EEVEE & Viewport
Module
Grease Pencil
Module
Modeling
Module
Nodes & Physics
Module
Pipeline, Assets & IO
Module
Platforms, Builds & Tests
Module
Python API
Module
Render & Cycles
Module
Sculpt, Paint & Texture
Module
Triaging
Module
User Interface
Module
VFX & Video
Platform
FreeBSD
Platform
Linux
Platform
macOS
Platform
Windows
Priority
High
Priority
Low
Priority
Normal
Priority
Unbreak Now!
Status
Archived
Status
Confirmed
Status
Duplicate
Status
Needs Info from Developers
Status
Needs Information from User
Status
Needs Triage
Status
Resolved
Type
Bug
Type
Design
Type
Known Issue
Type
Patch
Type
Report
Type
To Do
No Milestone
No project
No Assignees
8 Participants
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: blender/blender#120182
Loading…
Reference in New Issue
No description provided.
Delete Branch "%!s(<nil>)"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Why More Boolean Solvers?
The Mesh Boolean node in Blender 4.1 and earlier uses only the Exact Boolean geometry solver, unlike the modifier which allowed a choice between the native float boolean solver and the Exact Boolean solver that was put into Blenlib, using the "Mesh Arrangements" paper for the algorithm.
Users have long asked for the float solver to be included as an option in the Mesh Boolean node. And in general, complained about the speed of the Exact Boolean solver. For example, this bug: Extremely Long time delay using geometry nodes with simple mesh boolean node 37706ms #98020.
Some recent papers and open source projects have offered hope of even better and even faster solvers. Having a super-fast solver (even faster than the current float solver) is desirable especially for these use cases:
Which Solvers to Add?
Native Float Solver
The obvious first one to add is the current native float solver.
Pros
And in fact, the main branch of the 4.2 alpha already has the native float solver added. #119294.
Cons
Manifold Float Solver
We recently became aware of the manifold open source boolean solver. This is a float solver too, but it uses a technique of symbolic perturbation to make it guaranteed to product a manifold output when given a manifold input. Here "manifold" means that the input edges are all used in exactly two faces and the those faces are joined with consistently facing normals. The upshot of this is that it has some restrictions but is much more robust than our current float solver, and is probably faster to boot.
Pros
Cons
Ember Solver
Another task, Exact Boolean V2 #114476, describes a new native exact solver that I have been working on. It is based on using fixed-length multiprecision integer arithmetic. The authors of the paper that it is based on claim that their implementation is faster than float solvers they have measured against (not including Manifold, which post-dates the paper). It had been my intention to use this to replace the old Exact Solver, and maybe avoid the need for any float solver, but at this point I am a lot less clear that that can happen. It has taken a lot longer than i expected to get it working (and it is still quite a way from really working), so when the Manifold library came to my attention, I switched priorities to get that solver in first.
Pros
Cons
I did some very rough timings on the existing float solver to attempt to identify areas of potential optimization inside the existing
BM_mesh_intersect
function. For a mesh with 491,522 vertices and 524,288 faces, doing a Trim Lasso operation on the mesh in a developer build took 16.3s inside theBM_mesh_intersect
function.The areas of largest runtime are as follows:
Performing the Tri-Tri Intersections
(tree_overlap_tot = 3570558) taking 5.2s (31.9% of runtime)
Iteratively Removing Faces
(Removing 139446 / 525801 faces) taking 10.1s (61.9% of runtime)
Of the two areas, parallelizing the former seems less intrusive but still requires minor adjustments to some internal
struct
s to support splitting this work across a number of threads.@ideasman42 @JacquesLucke hey, any thoughts here?
FYI some context from @LazyDodo re adding
manifold
lib to our dependencies:I think it makes sense to pursue using the manifold library. Ideally the performance will be very good and we will be satisfied there. And ideally it could replace the existing exact solver. Adding library and its dependencies makes sense then. If it doesn't work out, they can always be removed again anyway.
For boolean performance in general, I think it would make sense to make the topology cleanup and de-triangulation optional. For many procedural workflows this shouldn't be necessary, and I could foresee it taking a significant portion of the overall runtime.
I think it would be good to talk a bit more about the different use-cases of mesh boolean solvers. Then we can check which solvers work well for certain types of work. This approach also makes it easier to tell users which solvers they should use.
Currently, I can think of these main use-cases, but I might be missing some stuff:
Am I missing something important? Which solver would you use for each of these tasks?
I don't think any of our current or proposed Boolean solvers are good for your use case 1 (fixing broken models). But there is an aspect to the question: if you have a broken model, which solver is more likely to give what people think a boolean operation should do on those models. This usually means falling back on ray tracing to discover insideness-outsideness, but there is a bit of an art to doing that. I would say it is a tossup right now whether the current float solver or the current exact solver is better here.
For use case 2, either the current exact solver or the manifold solver would be best IMO. The float solver sometimes works for this case but is unreliable -- sometimes the output will be utterly wrong. But I will note that within case 2 there are still many users who are very unsatisfied with the speed of the current exact solver.
Case 3 seems to be mostly about sculpting, but I case could also apply to cases where the resulting geometry is just rendered as is, using some kind of texturing that doesn't care about topololgy.
There is also the question of: is attribute propagation from input to output important? LIke untriangulating, that also slows things down and may not be needed by some users.
Should I start a user feedback thread on devtalk to ask about their requirements/desiderata for boolean solvers?
Thanks.
It definitely is in general, i.e. the code should support it. Currently, one can generally assume that nodes in geometry nodes propagate all attributes and I think the same will be true for boolean operations. However, we also give users tools to avoid propagating attributes: named attributes can be removed before the boolean node and anonymous attributes are detected whether they need to be propagated automatically.
I think a general "don't propagate attributes" checkbox in the boolean node is way to inflexible. What could help is to provide additional information in the timing overlay. We could explicitly show how long the attribute propagation takes so that it is more obvious to the user that it might make sense to optimize this part.
Might be useful to get a better understanding of what trade-offs we can make to get better performance. I'd assume that generally performance gets worse in the following cases:
It would also be useful to know if people often only use one boolean operation or whether they usually use multiple chained operations. For the latter case we should figure out a way to keep the mesh data in a boolean solver friendly data-structure for longer instead of converting back and forth for each operation.
Overall, I think the Manifold library seems to be a good fit for procedural workflows, probably better than the other solvers we have already. Are there functional benefits of using Ember over Manifold? Does Manifold handle coplanar faces as well as Ember? How does Ember decide what's inside when the input is not a closed volume?
Just wanted to mention that in my professional work I deal with insane amount of boolean operations. I prepare stop-motion puppet replacement parts for 3D printing and sometimes do multiple boolean operators on 50-100+ objects in a day. I also use booleans for sculpting. I would be happy to test in my workflow whatever library needs to be tested
I'll answer Jacques' questions later, but Nika: would you like to explain your particular requirements on boolean here? Do you care about the output topology (do you care if they are all triangles?). Do you care about reliability in cases where one edge of one part exactly intersects an edge or vertex of another part? Do you care about handling coplanar intersections properly? How large are your models typically? Do your booleans typically have only two operands or many operands? Do you chain many booleans together? How fast, ideally, do you need boolean to run?
I don't think so. Nowadays 3D printers can print almost any topology. Sometimes we do get some weirdness, but it's all fixable. Sometimes I even run Decimate on models but they still print fine. What is hard requirement for us is that silhouette of the object isn't changed and object is watertight. Judging by the name only Manifold might help here.
I'm not sure I fully understand that, but because my models are typically 300,000+ polygons, and there are lot of intersections (also because they're tiny), so in many cases where I run booleans there are lot of vertices and edges that are intersecting or very close to each other. Sometimes I use "Merge by Distance" to reduce the number, and it can take out even 30,000k vertices that are close to each other. It's important for me that boolean can handle that and doesn't need merge beforehand. For example, I had mustache and head as separate objects, and before printing I was using union boolean to combine them, and in at least 2/3 of cases I had to run "Merge by Distance" on head object because otherwise boolean would screw up.
Our models are 30-50 mm, but sometimes we don't follow real-life scale in Blender, because it's harder to work on smaller sizes and we scale them up to meters, and then rescale them in 3D printing software.
I use Bool Tools automatic option, which adds boolean and immediatelly applies it. So even though I use multiple booleans on single objects, they're never chained in a sense that I never have all modifiers at the same time.
So that answers your last question as well, speed is the most important requirement for me besides it just preserving mesh well. I've had workdays where I had to spend 4+ hours just doing booleans, and when each operation takes 10+ seconds on 500,000 poly mesh it's quite daunting.
Niki,
Thanks for your response.
By "how large", I meant "how many polygons", but you answered that already.
Can I assume you are currently using the "Float" solver in the modifier? (I don't know if Bool Tool always uses Float, or if there is an option). With models that large, the 'Exact' solver must be too slow by far. But that would be why you have to fiddle with merging so often in order to not have boolean "screw up".
While I understand that you use Bool Tool and thus do one boolean at a time, if there were a way to do multiple booleans at a time, about how many total operands would there be in your typical use?
Mostly I use Exact, because even though it's slower, it's most important that it looks the best. Objects we print are gonna be shot in close-ups and shown on large screens, so they have to look best. Sometimes there are cases where Float also can do the job, but problem is if I spend time checking Float, seeing if it works, and if it doesn't then undoing and redoing with Exact, I spend more time than if I do all Exacts.
That reminds me that undo speed is biggest performance hit in my workflow. Because every time Exact solver fails and I have to adjust my mesh and redo Blender freezes on undo and triples waiting time.
Either 3 or 4 operands on each object.
From the sculpting side of things, this comment and others in the issue may be helpful in gathering some requirements / desired features, though it also concerns more specific Trim functionality that isn't necessarily relevant to the boolean solver itself.
Answering Jacques' earlier questions:
Ember is an exact solver that handles special cases such as coplanar faces, edges exactly intersecting edges at something other than their endpoints, etc., as one would expect. Manifold is a "robust float solver", where the "robust" means that it will not create topological nonsense. But it does that by taking cases like coplanar faces and edges exactly intersecting edges and perturbing them in the decision process so that the special cases don't happen. This means that it can create zero area faces and the like. There is a cleanup process that does merges and other operations to try to get rid of such things. We need to experiment to see how successful that is in practice. But note that there are several caveats to saying that Ember is exact: first, it is only exact in the space where all vertex positions are converted to 26-bit integers (after scaling the floats to take full advantage of that range, of course). Second, sometimes "exact" doesn't correspond to what users actually want -- for instance, if you rotate a cube by what feels like 90 degrees, maybe the rotation matrix makes the vertex positions slightly away from their exact values if the rotation were perfect; then if you expected a face in that to be coplanar with another mesh's face, you might be disappointed that it creates an almost-zero-area face instead.
Ember, as described in the paper creates a strange output topology compared to Manifold. Ember's faces have t-junctions all over the place. If I continue working on Ember, I will try to fix that problem, which I think Blender users who care about output toplogy would not find very acceptable.
Ember might be faster than Manifold huge models, because it pays more attention to short-circuiting around large parts of models that are unaffected by intersection. But this is just a hypothesis at this point, not proven.
Manifold determines inside-vs-outside via local computations with the entities involved in the intersections, followed by some kind of sort. (Basically, the local computations determine which elements are "shadowed by", and hence "inside" in some sense, which other elements.) It is hard to see how to adapt this to the cases where our current Float and Exact solvers can deal with non-manifold meshes.
One possibility open to us: I take Manifold's code as a starting point, remove its dependencies on clipper, thrust, and glm, and then adapt it to Blender's specific use case, adding optimizations like the short-circuiting of Ember. The big downside of this would be losing (or at least making much harder) any future improvements to Manifold by its author.
I'm not sure how much effort it would be, but I'd really like to see a prototype with Manifold where one can chain multiple mesh boolean nodes and everything is evaluated as efficiently as possible, i.e. no unnecessary data structure conversions and attribute propagations (I'm fine with using a hacky approach to implement this). If that feels fast enough, it may be good to work towards properly implementing it. Otherwise, we might have to hope that Ember can feel fast enough in this situation. I can't really put numbers on what "fast enough" means yet unfortunately.. Right now it just too often feels slow when one would expect things to be real-time still.
I can see what I can do. Interestingly, Manifold internally keeps a tree of boolean operations, unevaluated, until one asks for the mesh output.
I did a very rough timing test yesterday (unsatisfactory mixture of Debug and Release code) that leads me to believe that Manifold will likely be about the same, or maybe somewhat faster, than our current Float solver. Which is much better than current Exact, but still not where I'd like it to be (Sean was ready to investigate improving the Float solver for sculpting as it was still sometimes too slow there).
To do the prototype you talk about, I can see several approaches:
Make a cache property of Mesh runtime that holds the internal manifold, or something like it, and not evaluate the manifold until some Geometry input really wants to suck up the realized mesh. The boolean node could then use such an cached value input instead of freshly converting input Mesh into manifold.
Try to examine the geo node tree and make connected components of boolean operations, and then make a boolean expression tree from that and evaluate it all at once.
Thoughts? Other ideas?
Here are some timings for a Release build on my Macbook Pro M3 Max (12 performance cores, 4 efficiency cores):
For a sphere-sphere difference where sphere1 has 200k faces (400k triangles) and sphere2 has 500k face (1 M triangles):
exact: 1281 ms
float: 422 ms
manifold: 327 ms. (triangle output for now; the other two have quad output except in the actual intersection areas)
Regarding how to integrate manifold for prototyping work (and making builds on the buildbot for people to try it out once ready enough), an idea would be to put it and its dependencies into the
extern/
of the wip branch for the time being. Then if we decide it's worth it and when it's ready for main, we can properly add the libraries to our pre-compiled repository instead.I kind of like that idea, Bastien, and think I will try it out. For one thing, it makes it easier for me to experiment with some things without having to involve the platform maintainers (though I thank LazyDodo for his help so far, which has made it possible for me to get this far).
I may even be able to figure out how to remove some, if not all, of the dependencies beyond manifold itself (I think, but have to verify), that clipper and hull are only needed for features that we don't need for 3D Boolean.
Hi all, maintainer of Manifold here - sorry I didn't notice this thread earlier. I don't think you'll gain much by short-circuiting more than Manifold already does. Already almost all of the work is focused on intersection regions. We used to have a short circuit to avoid computing the winding numbers of the non-intersecting verts, but it turned out the parallel computation of winding numbers was so efficient that it was faster than the short circuiting approach.
As for handling non-manifold meshes, all I can say is that may sound nice, but you'll always get some very surprising results. The reason is that fundamentally a Boolean operation is not well-defined for non-closed meshes, so the result will always be heuristic. You can certainly create intersections between any meshes, but deciding which part(s) to keep gets hard, particularly when a mesh only cuts part-way through another.
As to dependencies - clipper and hull should be easy to remove as they are siloed in related files. I tried to keep the library partitioned this way, but we haven't had any users consuming it like that yet, so would be happy to help make that process easier. I don't think you want to remove GLM - it's tiny, header-only, totally open and underpins everything in the library. As for Thrust - it's also header-only and it works just fine on Mac - that's what I develop on and our CI runs on Linux, Mac, and Windows. We don't use any of it's CUDA backend anymore, so really it's just a wrapper for TBB. We're actively looking to replace it with something else, maybe PSTL, but we're waiting for adequate compiler support first.
Thanks for the information on your attempts to short circuit. Interesting, and I guess I won't worry about that so much now.
I understand and agree with what you say, but Blender users have gotten used to things usually working when thy use some kinds of non-manifold meshes. And is a little hard to argue with an artist who will look at some non-manifold mesh and say "come on, it is clear to everyone what the result is here". To give three examples
So I need some fallback or alternative for users to use when things such as these occur. Users will understand that it is caveat emptor whether or not the result is surprising, but know that they usually won't be surprised.
I do have an experimental branch right now where I put manifold's source into blender's "external" folder, copied the distributed source for gsm and thrust into the third_party folder, and commented out about a half a dozen places in the code that depended on clipper2 and hull, so this does get rid of those dependencies. It is a hack at the moment, with hand-done CMake; it would be nice to get this more configurable upstream (the losses in functionality are: convex hull; polygon stuff, and most primitive shapes -- none of these are needed for the Blender Boolean application.
A robust "Make Manifold" would have value even outside of boolean. Maybe something like this
robust inside outside segmentation could be utilized to manifold the mesh prior to Manifold Boolean?
Some thoughts on EMBER:
Thank you Blender and Howard for this incredible effort that I get to see happen
Manifold will properly handle unioning two cubes with coplanar faces, but if the mesh has an extra face inside already, that will be a problem. Also, Manifold has no concept of a "self-union" (remove self-intersections). I am working on that, but it's novel research, so no promises.
Fair, but Manifold doesn't deal in heuristics. I would recommend an alternative: fill all mesh holes with simple, arbitrary fans of triangles to create a Manifold, and mark these faces with a special OriginalID. Perform the Boolean op, then remove the triangles with that ID. Now the heuristic is on your side of the code (hole filling) and in cases when it doesn't work well, likely those would also be unclear cases for even a heuristic Boolean anyway. This should be robust, as Manifold is robust to self-intersected geometry.
This can be handled by extruding the shape beyond the bounding box of the object it is operating on. This is how Manifold does TrimByPlane() - the plane is automatically turned into a sufficiently large cube.
Yes, this has a lot of value - Netfabb made a whole business out of this a decade ago. I remember their founder said to me "well, if the mesh is too terrible, I can always just give you back a cube - that'll be manifold at least". The hard part is protecting the desirable parts of the mesh, since it's entirely heuristic.