Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add 2D CSG boolean operations #99911

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft

Add 2D CSG boolean operations #99911

wants to merge 1 commit into from

Conversation

smix8
Copy link
Contributor

@smix8 smix8 commented Dec 2, 2024

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?

csg2d_bake_01

To quote from the older 3D CSG blog that can be found here.

CSG stands for “Construtive Solid Geometry”, and is a tool to combine basic (and not so basic) shapes to create more complex shapes. In the 3D modelling software, CSG is mostly known as “Boolean Operators”.

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.

csg2d

  • CSGCapsule2D
    Capsule shape with radius and height value. Can be switched to cone mode.
  • CSGCircle2D
    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.
  • CSGCombiner2D
    Does nothing itself other than combining csg children and stops the csg propagation for better organisation.
  • CSGMesh2D
    Accepts a rendering mesh resource that uses the ARRAY_FLAG_USE_2D_VERTICES format.
  • CSGPolygon2D
    Drawable polygon outline similar to e.g. Polygon2D node.
  • CSGRectangle2D
    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
  • INTERSECTION
  • SUBTRACTION

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.

Every CSG node will first process it’s children nodes (an their operation: union, intersection substraction), in tree order and apply them to itself one after the other.

Baking CSG results to static geometry

csg_menu_bake

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).

  • Bake to MeshInstance2D
  • Bake to CollisionShape2D
var baked_mesh: ArrayMesh = CSGShape2D.bake_static_mesh()
var baked_shape: ConcavePolygonShape2D = CSGShape2D.bake_collision_shape()

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!

crossthestreams
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 :)

Adds 2D CSG boolean operations.
@fire
Copy link
Member

fire commented Dec 2, 2024

Awesome, need some time to test and fix up the integration tests but looks promising.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants