1 /*************************************************************************/
2 /* collision_polygon_2d.cpp */
3 /*************************************************************************/
4 /* This file is part of: */
5 /* GODOT ENGINE */
6 /* https://godotengine.org */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
9 /* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
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 #include "collision_polygon_2d.h"
31
32 #include "collision_object_2d.h"
33 #include "scene/resources/concave_polygon_shape_2d.h"
34 #include "scene/resources/convex_polygon_shape_2d.h"
35
36 #include "thirdparty/misc/triangulator.h"
37
_add_to_collision_object(Object * p_obj)38 void CollisionPolygon2D::_add_to_collision_object(Object *p_obj) {
39
40 if (unparenting || !can_update_body)
41 return;
42
43 CollisionObject2D *co = p_obj->cast_to<CollisionObject2D>();
44 ERR_FAIL_COND(!co);
45
46 if (polygon.size() == 0)
47 return;
48
49 bool solids = build_mode == BUILD_SOLIDS;
50
51 if (solids) {
52
53 //here comes the sun, lalalala
54 //decompose concave into multiple convex polygons and add them
55 Vector<Vector<Vector2> > decomp = _decompose_in_convex();
56 shape_from = co->get_shape_count();
57 for (int i = 0; i < decomp.size(); i++) {
58 Ref<ConvexPolygonShape2D> convex = memnew(ConvexPolygonShape2D);
59 convex->set_points(decomp[i]);
60 co->add_shape(convex, get_transform());
61 if (trigger)
62 co->set_shape_as_trigger(co->get_shape_count() - 1, true);
63 }
64 shape_to = co->get_shape_count() - 1;
65 if (shape_to < shape_from) {
66 shape_from = -1;
67 shape_to = -1;
68 }
69
70 } else {
71
72 Ref<ConcavePolygonShape2D> concave = memnew(ConcavePolygonShape2D);
73
74 DVector<Vector2> segments;
75 segments.resize(polygon.size() * 2);
76 DVector<Vector2>::Write w = segments.write();
77
78 for (int i = 0; i < polygon.size(); i++) {
79 w[(i << 1) + 0] = polygon[i];
80 w[(i << 1) + 1] = polygon[(i + 1) % polygon.size()];
81 }
82
83 w = DVector<Vector2>::Write();
84 concave->set_segments(segments);
85
86 co->add_shape(concave, get_transform());
87 if (trigger)
88 co->set_shape_as_trigger(co->get_shape_count() - 1, true);
89
90 shape_from = co->get_shape_count() - 1;
91 shape_to = co->get_shape_count() - 1;
92 }
93
94 //co->add_shape(shape,get_transform());
95 }
96
_update_xform_in_parent()97 void CollisionPolygon2D::_update_xform_in_parent() {
98
99 if (shape_from >= 0 && shape_to >= 0) {
100 CollisionObject2D *co = get_parent()->cast_to<CollisionObject2D>();
101 if (co) {
102 for (int i = shape_from; i <= shape_to; i++) {
103 co->set_shape_transform(i, get_transform());
104 }
105 }
106 }
107 }
108
_update_parent()109 void CollisionPolygon2D::_update_parent() {
110
111 if (!can_update_body)
112 return;
113 Node *parent = get_parent();
114 if (!parent)
115 return;
116 CollisionObject2D *co = parent->cast_to<CollisionObject2D>();
117 if (!co)
118 return;
119 co->_update_shapes_from_children();
120 }
121
_decompose_in_convex()122 Vector<Vector<Vector2> > CollisionPolygon2D::_decompose_in_convex() {
123
124 Vector<Vector<Vector2> > decomp;
125 #if 0
126 //fast but imprecise triangulator, gave us problems
127 decomp = Geometry::decompose_polygon(polygon);
128 #else
129
130 List<TriangulatorPoly> in_poly, out_poly;
131
132 TriangulatorPoly inp;
133 inp.Init(polygon.size());
134 for (int i = 0; i < polygon.size(); i++) {
135 inp.GetPoint(i) = polygon[i];
136 }
137 inp.SetOrientation(TRIANGULATOR_CCW);
138 in_poly.push_back(inp);
139 TriangulatorPartition tpart;
140 if (tpart.ConvexPartition_HM(&in_poly, &out_poly) == 0) { //failed!
141 ERR_PRINT("Convex decomposing failed!");
142 return decomp;
143 }
144
145 decomp.resize(out_poly.size());
146 int idx = 0;
147
148 for (List<TriangulatorPoly>::Element *I = out_poly.front(); I; I = I->next()) {
149
150 TriangulatorPoly &tp = I->get();
151
152 decomp[idx].resize(tp.GetNumPoints());
153
154 for (int i = 0; i < tp.GetNumPoints(); i++) {
155
156 decomp[idx][i] = tp.GetPoint(i);
157 }
158
159 idx++;
160 }
161
162 #endif
163
164 return decomp;
165 }
166
_notification(int p_what)167 void CollisionPolygon2D::_notification(int p_what) {
168
169 switch (p_what) {
170 case NOTIFICATION_ENTER_TREE: {
171 unparenting = false;
172 can_update_body = get_tree()->is_editor_hint();
173 if (!get_tree()->is_editor_hint()) {
174 _update_xform_in_parent();
175 //display above all else
176 set_z_as_relative(false);
177 set_z(VS::CANVAS_ITEM_Z_MAX - 1);
178 }
179
180 } break;
181 case NOTIFICATION_EXIT_TREE: {
182 can_update_body = false;
183 } break;
184 case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
185
186 if (!is_inside_tree())
187 break;
188 if (can_update_body) {
189 _update_parent();
190 } else {
191 _update_xform_in_parent();
192 }
193
194 } break;
195
196 case NOTIFICATION_DRAW: {
197
198 if (!get_tree()->is_editor_hint() && !get_tree()->is_debugging_collisions_hint()) {
199 break;
200 }
201
202 for (int i = 0; i < polygon.size(); i++) {
203
204 Vector2 p = polygon[i];
205 Vector2 n = polygon[(i + 1) % polygon.size()];
206 draw_line(p, n, Color(0.9, 0.2, 0.0, 0.8), 3);
207 }
208 #define DEBUG_DECOMPOSE
209 #if defined(TOOLS_ENABLED) && defined(DEBUG_DECOMPOSE)
210
211 Vector<Vector<Vector2> > decomp = _decompose_in_convex();
212
213 Color c(0.4, 0.9, 0.1);
214 for (int i = 0; i < decomp.size(); i++) {
215
216 c.set_hsv(Math::fmod(c.get_h() + 0.738, 1), c.get_s(), c.get_v(), 0.5);
217 draw_colored_polygon(decomp[i], c);
218 }
219 #else
220 draw_colored_polygon(polygon, get_tree()->get_debug_collisions_color());
221 #endif
222
223 } break;
224 case NOTIFICATION_UNPARENTED: {
225 unparenting = true;
226 _update_parent();
227 } break;
228 }
229 }
230
set_polygon(const Vector<Point2> & p_polygon)231 void CollisionPolygon2D::set_polygon(const Vector<Point2> &p_polygon) {
232
233 polygon = p_polygon;
234
235 if (can_update_body) {
236 for (int i = 0; i < polygon.size(); i++) {
237 if (i == 0)
238 aabb = Rect2(polygon[i], Size2());
239 else
240 aabb.expand_to(polygon[i]);
241 }
242 if (aabb == Rect2()) {
243
244 aabb = Rect2(-10, -10, 20, 20);
245 } else {
246 aabb.pos -= aabb.size * 0.3;
247 aabb.size += aabb.size * 0.6;
248 }
249 _update_parent();
250 }
251 update();
252 update_configuration_warning();
253 }
254
get_polygon() const255 Vector<Point2> CollisionPolygon2D::get_polygon() const {
256
257 return polygon;
258 }
259
set_build_mode(BuildMode p_mode)260 void CollisionPolygon2D::set_build_mode(BuildMode p_mode) {
261
262 ERR_FAIL_INDEX(p_mode, 2);
263 build_mode = p_mode;
264 _update_parent();
265 }
266
get_build_mode() const267 CollisionPolygon2D::BuildMode CollisionPolygon2D::get_build_mode() const {
268
269 return build_mode;
270 }
271
get_item_rect() const272 Rect2 CollisionPolygon2D::get_item_rect() const {
273
274 return aabb;
275 }
276
set_trigger(bool p_trigger)277 void CollisionPolygon2D::set_trigger(bool p_trigger) {
278
279 trigger = p_trigger;
280 _update_parent();
281 if (!can_update_body && is_inside_tree() && shape_from >= 0 && shape_to >= 0) {
282 CollisionObject2D *co = get_parent()->cast_to<CollisionObject2D>();
283 for (int i = shape_from; i <= shape_to; i++) {
284 co->set_shape_as_trigger(i, p_trigger);
285 }
286 }
287 }
288
is_trigger() const289 bool CollisionPolygon2D::is_trigger() const {
290
291 return trigger;
292 }
293
_set_shape_range(const Vector2 & p_range)294 void CollisionPolygon2D::_set_shape_range(const Vector2 &p_range) {
295
296 shape_from = p_range.x;
297 shape_to = p_range.y;
298 }
299
_get_shape_range() const300 Vector2 CollisionPolygon2D::_get_shape_range() const {
301
302 return Vector2(shape_from, shape_to);
303 }
304
get_configuration_warning() const305 String CollisionPolygon2D::get_configuration_warning() const {
306
307 if (!get_parent()->cast_to<CollisionObject2D>()) {
308 return TTR("CollisionPolygon2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidBody2D, KinematicBody2D, etc. to give them a shape.");
309 }
310
311 if (polygon.empty()) {
312 return TTR("An empty CollisionPolygon2D has no effect on collision.");
313 }
314
315 return String();
316 }
317
_bind_methods()318 void CollisionPolygon2D::_bind_methods() {
319
320 ObjectTypeDB::bind_method(_MD("_add_to_collision_object"), &CollisionPolygon2D::_add_to_collision_object);
321 ObjectTypeDB::bind_method(_MD("set_polygon", "polygon"), &CollisionPolygon2D::set_polygon);
322 ObjectTypeDB::bind_method(_MD("get_polygon"), &CollisionPolygon2D::get_polygon);
323
324 ObjectTypeDB::bind_method(_MD("set_build_mode", "build_mode"), &CollisionPolygon2D::set_build_mode);
325 ObjectTypeDB::bind_method(_MD("get_build_mode"), &CollisionPolygon2D::get_build_mode);
326
327 ObjectTypeDB::bind_method(_MD("set_trigger", "trigger"), &CollisionPolygon2D::set_trigger);
328 ObjectTypeDB::bind_method(_MD("is_trigger"), &CollisionPolygon2D::is_trigger);
329
330 ObjectTypeDB::bind_method(_MD("_set_shape_range", "shape_range"), &CollisionPolygon2D::_set_shape_range);
331 ObjectTypeDB::bind_method(_MD("_get_shape_range"), &CollisionPolygon2D::_get_shape_range);
332
333 ObjectTypeDB::bind_method(_MD("get_collision_object_first_shape"), &CollisionPolygon2D::get_collision_object_first_shape);
334 ObjectTypeDB::bind_method(_MD("get_collision_object_last_shape"), &CollisionPolygon2D::get_collision_object_last_shape);
335
336 ADD_PROPERTY(PropertyInfo(Variant::INT, "build_mode", PROPERTY_HINT_ENUM, "Solids,Segments"), _SCS("set_build_mode"), _SCS("get_build_mode"));
337 ADD_PROPERTY(PropertyInfo(Variant::VECTOR2_ARRAY, "polygon"), _SCS("set_polygon"), _SCS("get_polygon"));
338 ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "shape_range", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), _SCS("_set_shape_range"), _SCS("_get_shape_range"));
339 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "trigger"), _SCS("set_trigger"), _SCS("is_trigger"));
340 }
341
CollisionPolygon2D()342 CollisionPolygon2D::CollisionPolygon2D() {
343
344 aabb = Rect2(-10, -10, 20, 20);
345 build_mode = BUILD_SOLIDS;
346 trigger = false;
347 unparenting = false;
348 shape_from = -1;
349 shape_to = -1;
350 can_update_body = false;
351 set_notify_local_transform(true);
352 }
353