Add 2D CSG boolean operations #99911
Draft
+166
−0
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
For now empty draft PR because I need the pr id and link but as can be seen by the animation all nodes and operations are already more or less fully functional.
Adds nodes and functions for 2D CSG boolean operations.
Implements proposal godotengine/godot-proposals#3731
.. and many related proposals that asked for similar functionality just under a different name.
(I need to collect their links, if you find them first add them please).
Wait, what is CSG?
To quote from the older 3D CSG blog that can be found here.
So basically you can do everything what you can do currently in scripts using the
Geometry2D
merge and slice related boolean operation functions but more convenient with nodes. There are also plenty of extra features on top.CSG Node types
Largely copies the workflow and nodes from the 3D CSG although accounting for some specific 2D quirks.
Capsule shape with radius and height value. Can be switched to cone mode.
Basic circle with radius and segment count for detail or to turn the shape into e.g. a hexa or with only min 3 segments into a triangle.
Does nothing itself other than combining csg children and stops the csg propagation for better organisation.
Accepts a rendering mesh resource that uses the
ARRAY_FLAG_USE_2D_VERTICES
format.Drawable polygon outline similar to e.g. Polygon2D node.
Basic rectangle with size property.
Also shared properties like snap and material slots.
Note that using materials will make both the csg ops and the rendering highly inefficient due to the 2d canvas item mesh api.
I am still on the fence if this should be even added considering how bad it works compared to 3D materials.
CSG Operators
The available operators are the same as in 3D:
Union merges the shape to the parent or higher sibling node shape while substraction cuts into them. The intersection is the weird one that removes all shape parts that are not found in the combined shapes.
Operations are done in node tree order same as in 3D, or to quote from the old CSG 3D blog.
Baking CSG results to static geometry
Same as 3D PR #93252
CSG options to bake to static mesh and collision shape same as 3D has, aka you can "design" your stuff with CSG and bake the result to a more efficient static version for performance (or to avoid physics collision seam issues).
The jury is still out if there is need for conversion to other node types e.g. Polygon2D for skeleton animation and uv editing.
Why name it CSG in 2D?
Although CSG "Construtive Solid Geometry” is more a name used in 3D modelling context I stayed with the name for 2D because users are already very familiar with the term in Godot from 3D. The 2D and 3D nodes and workflows are kept very similar on purpose as it allows better knowledge and documentation sharing.
Performance
Compared to the far more complex 3D CSG the 2D version has actually pretty good performance for what it does. Although I still would not recommend planning to use it with hundreds of changing sub nodes at runtime.
For rendering performance, if only the CSG root node transform is changed that costs basically nothing at runtime. The 2D CSG does not use the Node2D draw functions for the polygons or lines like many other 2d related nodes. The actual rendering geometry is baked to a single static 2d mesh in the end of the operations. So the entire performance cost of moving the CSG root node at runtime is a
canvas_item_set_transform()
call.This is similar to what the navmesh baking does but the huge difference for runtime change performance is that the specialised CSG nodes can all catch their own intermediate result. So on changes only the parts that actually change up in the tree order need to be reparsed and recalculated instead of absolutely everything.
Help! My polygons are all breaking!
The boolean operations are done with the Clipper2 polytree backend base on polygon outline paths. As such all the usual polygon outline limitations apply that can be read in detail here https://angusj.com/clipper2/Docs/Robustness.htm.
As always when dealing with outline to polygon conversions, dont (upscale) float precision fumble your layouts, avoid self-intersection at all time and never cross the (edge) streams in any way guys!
Don't cross the (edge) streams!
The CSG can fix a lot of weird shapes but if the source shapes have already grievous outline errors the CSG chain still can break. This will be more a problem with the CSGPolygon2D and CSGMesh2D nodes as they allow the creation of all kinds of invalid users input. It can also happen when weird node scaling is used as this may cause vertices to end up in unintended rasterization cells when float positions are upscales back and forth. Same can happen when shapes that are perfectly aligned with shared vertices at corners in the editor. These kind of "pixel-perfectionist" layouts regularly stop to work the moment the float positions get upscaled as suddenly vertices may end up inside other shapes, either keep some error margin or create those shapes separated. You have been warned :)