Skip to content

Commit 6209b20

Browse files
committed
Add CSG2D for boolean operations
Adds nodes and functions for 2D CSG boolean operations.
1 parent 71a9948 commit 6209b20

34 files changed

+3638
-0
lines changed

modules/csg_2d/SCsub

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#!/usr/bin/env python
2+
from misc.utility.scons_hints import *
3+
4+
Import("env")
5+
Import("env_modules")
6+
7+
env_csg_2d = env_modules.Clone()
8+
9+
# Godot's own source files
10+
env_csg_2d.add_source_files(env.modules_sources, "*.cpp")
11+
if env.editor_build:
12+
env_csg_2d.add_source_files(env.modules_sources, "editor/*.cpp")

modules/csg_2d/config.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
def can_build(env, platform):
2+
return True
3+
4+
5+
def configure(env):
6+
pass
7+
8+
9+
def get_doc_classes():
10+
return [
11+
"CSGCapsule2D",
12+
"CSGCircle2D",
13+
"CSGCombiner2D",
14+
"CSGMesh2D",
15+
"CSGPolygon2D",
16+
"CSGPrimitive2D",
17+
"CSGShape2D",
18+
"CSGRectangle2D",
19+
]
20+
21+
22+
def get_doc_path():
23+
return "doc_classes"

modules/csg_2d/csg_2d.cpp

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/**************************************************************************/
2+
/* csg_2d.cpp */
3+
/**************************************************************************/
4+
/* This file is part of: */
5+
/* GODOT ENGINE */
6+
/* https://godotengine.org */
7+
/**************************************************************************/
8+
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9+
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10+
/* */
11+
/* Permission is hereby granted, free of charge, to any person obtaining */
12+
/* a copy of this software and associated documentation files (the */
13+
/* "Software"), to deal in the Software without restriction, including */
14+
/* without limitation the rights to use, copy, modify, merge, publish, */
15+
/* distribute, sublicense, and/or sell copies of the Software, and to */
16+
/* permit persons to whom the Software is furnished to do so, subject to */
17+
/* the following conditions: */
18+
/* */
19+
/* The above copyright notice and this permission notice shall be */
20+
/* included in all copies or substantial portions of the Software. */
21+
/* */
22+
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23+
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24+
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25+
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26+
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27+
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28+
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29+
/**************************************************************************/
30+
31+
#include "csg_2d.h"
32+
33+
void CSGBrush2D::copy_from(const CSGBrush2D &p_brush, const Transform2D &p_xform) {
34+
outlines.resize(p_brush.outlines.size());
35+
36+
for (uint32_t i = 0; i < outlines.size(); i++) {
37+
for (uint32_t j = 0; j < outlines[i].vertices.size(); j++) {
38+
outlines[i].vertices[j] = p_xform.xform(p_brush.outlines[i].vertices[j]);
39+
}
40+
}
41+
42+
poly_paths.clear();
43+
44+
for (Clipper2Lib::PathsD::size_type i = 0; i < p_brush.poly_paths.size(); ++i) {
45+
const Clipper2Lib::PathD &path = p_brush.poly_paths[i];
46+
47+
Clipper2Lib::PathD poly_path;
48+
49+
for (Clipper2Lib::PathsD::size_type j = 0; j < path.size(); ++j) {
50+
Point2 vertex = Point2(static_cast<real_t>(path[j].x), static_cast<real_t>(path[j].y));
51+
vertex = p_xform.xform(vertex);
52+
poly_path.push_back(Clipper2Lib::PointD(vertex.x, vertex.y));
53+
}
54+
poly_paths.push_back(poly_path);
55+
}
56+
57+
_regen_outline_rects();
58+
}
59+
60+
void CSGBrush2D::build_from_outlines(const LocalVector<Outline> &p_outlines) {
61+
poly_paths.clear();
62+
63+
Clipper2Lib::ClipperD clipper_D;
64+
clipper_D.PreserveCollinear(false);
65+
66+
for (const Outline &outline : p_outlines) {
67+
const LocalVector<Vector2> &vertices = outline.vertices;
68+
Clipper2Lib::PathD subject_path;
69+
subject_path.reserve(vertices.size());
70+
for (const Vector2 &vertex : vertices) {
71+
Clipper2Lib::PointD point = Clipper2Lib::PointD(vertex.x, vertex.y);
72+
subject_path.emplace_back(point);
73+
}
74+
75+
Clipper2Lib::PathsD subject_paths;
76+
subject_paths.push_back(subject_path);
77+
78+
if (outline.is_hole) {
79+
clipper_D.AddClip(subject_paths);
80+
} else {
81+
clipper_D.AddSubject(subject_paths);
82+
}
83+
}
84+
85+
clipper_D.Execute(Clipper2Lib::ClipType::Union, Clipper2Lib::FillRule::NonZero, poly_paths);
86+
}

modules/csg_2d/csg_2d.h

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/**************************************************************************/
2+
/* csg_2d.h */
3+
/**************************************************************************/
4+
/* This file is part of: */
5+
/* GODOT ENGINE */
6+
/* https://godotengine.org */
7+
/**************************************************************************/
8+
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9+
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10+
/* */
11+
/* Permission is hereby granted, free of charge, to any person obtaining */
12+
/* a copy of this software and associated documentation files (the */
13+
/* "Software"), to deal in the Software without restriction, including */
14+
/* without limitation the rights to use, copy, modify, merge, publish, */
15+
/* distribute, sublicense, and/or sell copies of the Software, and to */
16+
/* permit persons to whom the Software is furnished to do so, subject to */
17+
/* the following conditions: */
18+
/* */
19+
/* The above copyright notice and this permission notice shall be */
20+
/* included in all copies or substantial portions of the Software. */
21+
/* */
22+
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23+
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24+
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25+
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26+
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27+
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28+
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29+
/**************************************************************************/
30+
31+
#pragma once
32+
33+
#include "core/math/transform_2d.h"
34+
#include "core/templates/local_vector.h"
35+
36+
#include "thirdparty/clipper2/include/clipper2/clipper.h"
37+
38+
struct CSGBrush2D {
39+
struct Outline {
40+
LocalVector<Vector2> vertices;
41+
bool is_hole = false;
42+
Rect2 rect;
43+
};
44+
45+
LocalVector<Outline> outlines;
46+
47+
inline void _regen_outline_rects() {
48+
for (uint32_t i = 0; i < outlines.size(); i++) {
49+
outlines[i].rect = Rect2();
50+
if (outlines[i].vertices.size() > 0) {
51+
outlines[i].rect.position = outlines[i].vertices[0];
52+
for (uint32_t j = 1; j < outlines[i].vertices.size(); j++) {
53+
outlines[i].rect.expand_to(outlines[i].vertices[j]);
54+
}
55+
}
56+
}
57+
}
58+
59+
Clipper2Lib::PathsD poly_paths;
60+
61+
void copy_from(const CSGBrush2D &p_brush, const Transform2D &p_xform);
62+
63+
void build_from_outlines(const LocalVector<Outline> &p_outlines);
64+
};

modules/csg_2d/csg_capsule_2d.cpp

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/**************************************************************************/
2+
/* csg_capsule_2d.cpp */
3+
/**************************************************************************/
4+
/* This file is part of: */
5+
/* GODOT ENGINE */
6+
/* https://godotengine.org */
7+
/**************************************************************************/
8+
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9+
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10+
/* */
11+
/* Permission is hereby granted, free of charge, to any person obtaining */
12+
/* a copy of this software and associated documentation files (the */
13+
/* "Software"), to deal in the Software without restriction, including */
14+
/* without limitation the rights to use, copy, modify, merge, publish, */
15+
/* distribute, sublicense, and/or sell copies of the Software, and to */
16+
/* permit persons to whom the Software is furnished to do so, subject to */
17+
/* the following conditions: */
18+
/* */
19+
/* The above copyright notice and this permission notice shall be */
20+
/* included in all copies or substantial portions of the Software. */
21+
/* */
22+
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23+
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24+
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25+
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26+
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27+
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28+
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29+
/**************************************************************************/
30+
31+
#include "csg_capsule_2d.h"
32+
33+
#include "core/math/geometry_2d.h"
34+
#include "scene/resources/world_2d.h"
35+
#include "servers/physics_server_2d.h"
36+
37+
#include "thirdparty/misc/polypartition.h"
38+
39+
#ifdef TOOLS_ENABLED
40+
bool CSGCapsule2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const {
41+
return Geometry2D::is_point_in_polygon(p_point, _get_vertices());
42+
}
43+
#endif // TOOLS_ENABLED
44+
45+
CSGBrush2D *CSGCapsule2D::_build_brush() {
46+
CSGBrush2D *new_brush = memnew(CSGBrush2D);
47+
48+
LocalVector<CSGBrush2D::Outline> &outlines = new_brush->outlines;
49+
outlines.resize(1);
50+
CSGBrush2D::Outline &outline = outlines[0];
51+
52+
const Vector<Vector2> &cached_vertices = _get_vertices();
53+
54+
LocalVector<Vector2> &vertices = outline.vertices;
55+
56+
vertices.resize(cached_vertices.size() + 1);
57+
58+
for (int i = 0; i < cached_vertices.size(); i++) {
59+
vertices[i] = cached_vertices[i];
60+
}
61+
vertices[cached_vertices.size()] = cached_vertices[0];
62+
63+
brush_outlines.resize(1);
64+
brush_outlines[0] = vertices;
65+
66+
new_brush->build_from_outlines(outlines);
67+
68+
return new_brush;
69+
}
70+
71+
Vector<Vector2> CSGCapsule2D::_get_vertices() const {
72+
if (vertex_cache_dirty) {
73+
vertex_cache_dirty = false;
74+
75+
vertex_cache.resize(radial_segments * 4 + 2);
76+
Vector2 *vertex_cache_ptrw = vertex_cache.ptrw();
77+
int vertices_index = 0;
78+
79+
const real_t turn_step = Math::TAU / float(radial_segments * 4);
80+
for (int i = 0; i < radial_segments * 4; i++) {
81+
Vector2 ofs = Vector2(0, (i > radial_segments && i <= radial_segments * 3) ? -height * 0.5 + radius : height * 0.5 - radius);
82+
83+
vertex_cache_ptrw[vertices_index++] = Vector2(Math::sin(i * turn_step), Math::cos(i * turn_step)) * radius + ofs;
84+
if (i == radial_segments || i == radial_segments * 3) {
85+
vertex_cache_ptrw[vertices_index++] = Vector2(Math::sin(i * turn_step), Math::cos(i * turn_step)) * radius - ofs;
86+
}
87+
}
88+
}
89+
90+
return vertex_cache;
91+
}
92+
93+
void CSGCapsule2D::_bind_methods() {
94+
ClassDB::bind_method(D_METHOD("set_radius", "radius"), &CSGCapsule2D::set_radius);
95+
ClassDB::bind_method(D_METHOD("get_radius"), &CSGCapsule2D::get_radius);
96+
97+
ClassDB::bind_method(D_METHOD("set_height", "height"), &CSGCapsule2D::set_height);
98+
ClassDB::bind_method(D_METHOD("get_height"), &CSGCapsule2D::get_height);
99+
100+
ClassDB::bind_method(D_METHOD("set_radial_segments", "radial_segments"), &CSGCapsule2D::set_radial_segments);
101+
ClassDB::bind_method(D_METHOD("get_radial_segments"), &CSGCapsule2D::get_radial_segments);
102+
103+
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp,suffix:m"), "set_radius", "get_radius");
104+
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp,suffix:m"), "set_height", "get_height");
105+
ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_segments", PROPERTY_HINT_RANGE, "1,100,1"), "set_radial_segments", "get_radial_segments");
106+
}
107+
108+
void CSGCapsule2D::set_radius(float p_radius) {
109+
radius = p_radius;
110+
vertex_cache_dirty = true;
111+
_make_dirty();
112+
}
113+
114+
float CSGCapsule2D::get_radius() const {
115+
return radius;
116+
}
117+
118+
void CSGCapsule2D::set_height(float p_height) {
119+
height = p_height;
120+
vertex_cache_dirty = true;
121+
_make_dirty();
122+
}
123+
124+
float CSGCapsule2D::get_height() const {
125+
return height;
126+
}
127+
128+
void CSGCapsule2D::set_radial_segments(int p_segments) {
129+
radial_segments = p_segments > 0 ? p_segments : 1;
130+
vertex_cache_dirty = true;
131+
_make_dirty();
132+
}
133+
134+
int CSGCapsule2D::get_radial_segments() const {
135+
return radial_segments;
136+
}

modules/csg_2d/csg_capsule_2d.h

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/**************************************************************************/
2+
/* csg_capsule_2d.h */
3+
/**************************************************************************/
4+
/* This file is part of: */
5+
/* GODOT ENGINE */
6+
/* https://godotengine.org */
7+
/**************************************************************************/
8+
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9+
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10+
/* */
11+
/* Permission is hereby granted, free of charge, to any person obtaining */
12+
/* a copy of this software and associated documentation files (the */
13+
/* "Software"), to deal in the Software without restriction, including */
14+
/* without limitation the rights to use, copy, modify, merge, publish, */
15+
/* distribute, sublicense, and/or sell copies of the Software, and to */
16+
/* permit persons to whom the Software is furnished to do so, subject to */
17+
/* the following conditions: */
18+
/* */
19+
/* The above copyright notice and this permission notice shall be */
20+
/* included in all copies or substantial portions of the Software. */
21+
/* */
22+
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23+
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24+
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25+
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26+
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27+
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28+
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29+
/**************************************************************************/
30+
31+
#pragma once
32+
33+
#include "csg_shape_2d.h"
34+
35+
class CSGCapsule2D : public CSGPrimitive2D {
36+
GDCLASS(CSGCapsule2D, CSGPrimitive2D);
37+
38+
virtual CSGBrush2D *_build_brush() override;
39+
40+
float radius = 10.0;
41+
float height = 30.0;
42+
int radial_segments = 6;
43+
44+
mutable bool vertex_cache_dirty = true;
45+
mutable Vector<Vector2> vertex_cache;
46+
Vector<Vector2> _get_vertices() const;
47+
48+
protected:
49+
static void _bind_methods();
50+
51+
public:
52+
#ifdef TOOLS_ENABLED
53+
virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const override;
54+
#endif // TOOLS_ENABLED
55+
56+
void set_radius(float p_radius);
57+
float get_radius() const;
58+
59+
void set_height(float p_height);
60+
float get_height() const;
61+
62+
void set_radial_segments(int p_segments);
63+
int get_radial_segments() const;
64+
};

0 commit comments

Comments
 (0)