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