1 /*************************************************************************/
2 /*  collision_object_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_object_2d.h"
31 #include "scene/scene_string_names.h"
32 #include "servers/physics_2d_server.h"
33 
_update_shapes_from_children()34 void CollisionObject2D::_update_shapes_from_children() {
35 
36 	shapes.clear();
37 	for (int i = 0; i < get_child_count(); i++) {
38 
39 		Node *n = get_child(i);
40 		n->call("_add_to_collision_object", this);
41 	}
42 
43 	_update_shapes();
44 }
45 
_notification(int p_what)46 void CollisionObject2D::_notification(int p_what) {
47 
48 	switch (p_what) {
49 
50 		case NOTIFICATION_ENTER_TREE: {
51 
52 			if (area)
53 				Physics2DServer::get_singleton()->area_set_transform(rid, get_global_transform());
54 			else
55 				Physics2DServer::get_singleton()->body_set_state(rid, Physics2DServer::BODY_STATE_TRANSFORM, get_global_transform());
56 
57 			RID space = get_world_2d()->get_space();
58 			if (area) {
59 				Physics2DServer::get_singleton()->area_set_space(rid, space);
60 			} else
61 				Physics2DServer::get_singleton()->body_set_space(rid, space);
62 
63 			_update_pickable();
64 
65 			//get space
66 		}
67 
68 		case NOTIFICATION_VISIBILITY_CHANGED: {
69 
70 			_update_pickable();
71 		} break;
72 		case NOTIFICATION_TRANSFORM_CHANGED: {
73 
74 			if (area)
75 				Physics2DServer::get_singleton()->area_set_transform(rid, get_global_transform());
76 			else
77 				Physics2DServer::get_singleton()->body_set_state(rid, Physics2DServer::BODY_STATE_TRANSFORM, get_global_transform());
78 
79 		} break;
80 		case NOTIFICATION_EXIT_TREE: {
81 
82 			if (area) {
83 				Physics2DServer::get_singleton()->area_set_space(rid, RID());
84 			} else
85 				Physics2DServer::get_singleton()->body_set_space(rid, RID());
86 
87 		} break;
88 	}
89 }
90 
_update_shapes()91 void CollisionObject2D::_update_shapes() {
92 
93 	if (!rid.is_valid())
94 		return;
95 
96 	if (area)
97 		Physics2DServer::get_singleton()->area_clear_shapes(rid);
98 	else
99 		Physics2DServer::get_singleton()->body_clear_shapes(rid);
100 
101 	for (int i = 0; i < shapes.size(); i++) {
102 
103 		if (shapes[i].shape.is_null())
104 			continue;
105 		if (area)
106 			Physics2DServer::get_singleton()->area_add_shape(rid, shapes[i].shape->get_rid(), shapes[i].xform);
107 		else {
108 			Physics2DServer::get_singleton()->body_add_shape(rid, shapes[i].shape->get_rid(), shapes[i].xform);
109 			if (shapes[i].trigger)
110 				Physics2DServer::get_singleton()->body_set_shape_as_trigger(rid, i, shapes[i].trigger);
111 		}
112 	}
113 }
114 
_set(const StringName & p_name,const Variant & p_value)115 bool CollisionObject2D::_set(const StringName &p_name, const Variant &p_value) {
116 	String name = p_name;
117 
118 	if (name.begins_with("shapes/")) {
119 
120 		int idx = name.get_slicec('/', 1).to_int();
121 		String what = name.get_slicec('/', 2);
122 		if (what == "shape") {
123 			if (idx >= shapes.size())
124 				add_shape(RefPtr(p_value));
125 			else
126 				set_shape(idx, RefPtr(p_value));
127 		} else if (what == "transform")
128 			set_shape_transform(idx, p_value);
129 		else if (what == "trigger")
130 			set_shape_as_trigger(idx, p_value);
131 	} else
132 		return false;
133 
134 	return true;
135 }
136 
_get(const StringName & p_name,Variant & r_ret) const137 bool CollisionObject2D::_get(const StringName &p_name, Variant &r_ret) const {
138 
139 	String name = p_name;
140 
141 	if (name.begins_with("shapes/")) {
142 
143 		int idx = name.get_slicec('/', 1).to_int();
144 		String what = name.get_slicec('/', 2);
145 		if (what == "shape")
146 			r_ret = get_shape(idx);
147 		else if (what == "transform")
148 			r_ret = get_shape_transform(idx);
149 		else if (what == "trigger")
150 			r_ret = is_shape_set_as_trigger(idx);
151 	} else
152 		return false;
153 
154 	return true;
155 }
156 
_get_property_list(List<PropertyInfo> * p_list) const157 void CollisionObject2D::_get_property_list(List<PropertyInfo> *p_list) const {
158 
159 	//p_list->push_back( PropertyInfo(Variant::INT,"shape_count",PROPERTY_HINT_RANGE,"0,256,1",PROPERTY_USAGE_NOEDITOR|PROPERTY_USAGE_NO_INSTANCE_STATE) );
160 
161 	for (int i = 0; i < shapes.size(); i++) {
162 		String path = "shapes/" + itos(i) + "/";
163 		p_list->push_back(PropertyInfo(Variant::OBJECT, path + "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape2D", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_NO_INSTANCE_STATE));
164 		p_list->push_back(PropertyInfo(Variant::TRANSFORM, path + "transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_NO_INSTANCE_STATE));
165 		p_list->push_back(PropertyInfo(Variant::BOOL, path + "trigger", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_NO_INSTANCE_STATE));
166 	}
167 }
168 
set_pickable(bool p_enabled)169 void CollisionObject2D::set_pickable(bool p_enabled) {
170 
171 	if (pickable == p_enabled)
172 		return;
173 
174 	pickable = p_enabled;
175 	_update_pickable();
176 }
177 
is_pickable() const178 bool CollisionObject2D::is_pickable() const {
179 
180 	return pickable;
181 }
182 
_input_event(Node * p_viewport,const InputEvent & p_input_event,int p_shape)183 void CollisionObject2D::_input_event(Node *p_viewport, const InputEvent &p_input_event, int p_shape) {
184 
185 	if (get_script_instance()) {
186 		get_script_instance()->call(SceneStringNames::get_singleton()->_input_event, p_viewport, p_input_event, p_shape);
187 	}
188 	emit_signal(SceneStringNames::get_singleton()->input_event, p_viewport, p_input_event, p_shape);
189 }
190 
_mouse_enter()191 void CollisionObject2D::_mouse_enter() {
192 
193 	if (get_script_instance()) {
194 		get_script_instance()->call(SceneStringNames::get_singleton()->_mouse_enter);
195 	}
196 	emit_signal(SceneStringNames::get_singleton()->mouse_enter);
197 }
198 
_mouse_exit()199 void CollisionObject2D::_mouse_exit() {
200 
201 	if (get_script_instance()) {
202 		get_script_instance()->call(SceneStringNames::get_singleton()->_mouse_exit);
203 	}
204 	emit_signal(SceneStringNames::get_singleton()->mouse_exit);
205 }
206 
_update_pickable()207 void CollisionObject2D::_update_pickable() {
208 	if (!is_inside_tree())
209 		return;
210 	bool pickable = this->pickable && is_inside_tree() && is_visible();
211 	if (area)
212 		Physics2DServer::get_singleton()->area_set_pickable(rid, pickable);
213 	else
214 		Physics2DServer::get_singleton()->body_set_pickable(rid, pickable);
215 }
216 
_bind_methods()217 void CollisionObject2D::_bind_methods() {
218 
219 	ObjectTypeDB::bind_method(_MD("add_shape", "shape:Shape2D", "transform"), &CollisionObject2D::add_shape, DEFVAL(Matrix32()));
220 	ObjectTypeDB::bind_method(_MD("get_shape_count"), &CollisionObject2D::get_shape_count);
221 	ObjectTypeDB::bind_method(_MD("set_shape", "shape_idx", "shape:Shape"), &CollisionObject2D::set_shape);
222 	ObjectTypeDB::bind_method(_MD("set_shape_transform", "shape_idx", "transform"), &CollisionObject2D::set_shape_transform);
223 	ObjectTypeDB::bind_method(_MD("set_shape_as_trigger", "shape_idx", "enable"), &CollisionObject2D::set_shape_as_trigger);
224 	ObjectTypeDB::bind_method(_MD("get_shape:Shape2D", "shape_idx"), &CollisionObject2D::get_shape);
225 	ObjectTypeDB::bind_method(_MD("get_shape_transform", "shape_idx"), &CollisionObject2D::get_shape_transform);
226 	ObjectTypeDB::bind_method(_MD("is_shape_set_as_trigger", "shape_idx"), &CollisionObject2D::is_shape_set_as_trigger);
227 	ObjectTypeDB::bind_method(_MD("remove_shape", "shape_idx"), &CollisionObject2D::remove_shape);
228 	ObjectTypeDB::bind_method(_MD("clear_shapes"), &CollisionObject2D::clear_shapes);
229 	ObjectTypeDB::bind_method(_MD("get_rid"), &CollisionObject2D::get_rid);
230 
231 	ObjectTypeDB::bind_method(_MD("set_pickable", "enabled"), &CollisionObject2D::set_pickable);
232 	ObjectTypeDB::bind_method(_MD("is_pickable"), &CollisionObject2D::is_pickable);
233 
234 	BIND_VMETHOD(MethodInfo("_input_event", PropertyInfo(Variant::OBJECT, "viewport"), PropertyInfo(Variant::INPUT_EVENT, "event"), PropertyInfo(Variant::INT, "shape_idx")));
235 
236 	ADD_SIGNAL(MethodInfo("input_event", PropertyInfo(Variant::OBJECT, "viewport"), PropertyInfo(Variant::INPUT_EVENT, "event"), PropertyInfo(Variant::INT, "shape_idx")));
237 	ADD_SIGNAL(MethodInfo("mouse_enter"));
238 	ADD_SIGNAL(MethodInfo("mouse_exit"));
239 
240 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "input/pickable"), _SCS("set_pickable"), _SCS("is_pickable"));
241 }
242 
add_shape(const Ref<Shape2D> & p_shape,const Matrix32 & p_transform)243 void CollisionObject2D::add_shape(const Ref<Shape2D> &p_shape, const Matrix32 &p_transform) {
244 
245 	ERR_FAIL_COND(p_shape.is_null());
246 
247 	ShapeData sdata;
248 	sdata.shape = p_shape;
249 	sdata.xform = p_transform;
250 	sdata.trigger = false;
251 
252 	if (area)
253 		Physics2DServer::get_singleton()->area_add_shape(get_rid(), p_shape->get_rid(), p_transform);
254 	else
255 		Physics2DServer::get_singleton()->body_add_shape(get_rid(), p_shape->get_rid(), p_transform);
256 
257 	shapes.push_back(sdata);
258 }
get_shape_count() const259 int CollisionObject2D::get_shape_count() const {
260 
261 	return shapes.size();
262 }
set_shape(int p_shape_idx,const Ref<Shape2D> & p_shape)263 void CollisionObject2D::set_shape(int p_shape_idx, const Ref<Shape2D> &p_shape) {
264 
265 	ERR_FAIL_INDEX(p_shape_idx, shapes.size());
266 	ERR_FAIL_COND(p_shape.is_null());
267 
268 	shapes[p_shape_idx].shape = p_shape;
269 	if (area)
270 		Physics2DServer::get_singleton()->area_set_shape(get_rid(), p_shape_idx, p_shape->get_rid());
271 	else
272 		Physics2DServer::get_singleton()->body_set_shape(get_rid(), p_shape_idx, p_shape->get_rid());
273 
274 	//	_update_shapes();
275 }
276 
set_shape_transform(int p_shape_idx,const Matrix32 & p_transform)277 void CollisionObject2D::set_shape_transform(int p_shape_idx, const Matrix32 &p_transform) {
278 
279 	ERR_FAIL_INDEX(p_shape_idx, shapes.size());
280 	shapes[p_shape_idx].xform = p_transform;
281 
282 	if (area)
283 		Physics2DServer::get_singleton()->area_set_shape_transform(get_rid(), p_shape_idx, p_transform);
284 	else
285 		Physics2DServer::get_singleton()->body_set_shape_transform(get_rid(), p_shape_idx, p_transform);
286 
287 	//	_update_shapes();
288 }
289 
get_shape(int p_shape_idx) const290 Ref<Shape2D> CollisionObject2D::get_shape(int p_shape_idx) const {
291 
292 	ERR_FAIL_INDEX_V(p_shape_idx, shapes.size(), Ref<Shape2D>());
293 	return shapes[p_shape_idx].shape;
294 }
get_shape_transform(int p_shape_idx) const295 Matrix32 CollisionObject2D::get_shape_transform(int p_shape_idx) const {
296 
297 	ERR_FAIL_INDEX_V(p_shape_idx, shapes.size(), Matrix32());
298 	return shapes[p_shape_idx].xform;
299 }
remove_shape(int p_shape_idx)300 void CollisionObject2D::remove_shape(int p_shape_idx) {
301 
302 	ERR_FAIL_INDEX(p_shape_idx, shapes.size());
303 	shapes.remove(p_shape_idx);
304 
305 	_update_shapes();
306 }
307 
set_shape_as_trigger(int p_shape_idx,bool p_trigger)308 void CollisionObject2D::set_shape_as_trigger(int p_shape_idx, bool p_trigger) {
309 
310 	ERR_FAIL_INDEX(p_shape_idx, shapes.size());
311 	shapes[p_shape_idx].trigger = p_trigger;
312 	if (!area && rid.is_valid()) {
313 
314 		Physics2DServer::get_singleton()->body_set_shape_as_trigger(rid, p_shape_idx, p_trigger);
315 	}
316 }
317 
is_shape_set_as_trigger(int p_shape_idx) const318 bool CollisionObject2D::is_shape_set_as_trigger(int p_shape_idx) const {
319 
320 	ERR_FAIL_INDEX_V(p_shape_idx, shapes.size(), false);
321 	return shapes[p_shape_idx].trigger;
322 }
323 
clear_shapes()324 void CollisionObject2D::clear_shapes() {
325 
326 	shapes.clear();
327 
328 	_update_shapes();
329 }
330 
CollisionObject2D(RID p_rid,bool p_area)331 CollisionObject2D::CollisionObject2D(RID p_rid, bool p_area) {
332 
333 	rid = p_rid;
334 	area = p_area;
335 	pickable = true;
336 	if (p_area) {
337 
338 		Physics2DServer::get_singleton()->area_attach_object_instance_ID(rid, get_instance_ID());
339 	} else {
340 		Physics2DServer::get_singleton()->body_attach_object_instance_ID(rid, get_instance_ID());
341 	}
342 }
343 
CollisionObject2D()344 CollisionObject2D::CollisionObject2D() {
345 
346 	//owner=
347 }
348 
~CollisionObject2D()349 CollisionObject2D::~CollisionObject2D() {
350 
351 	Physics2DServer::get_singleton()->free(rid);
352 }
353