1/**************************************************************************/
2/* godot_collision_object_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 "godot_collision_object_2d.h"
32#include "godot_physics_server_2d.h"
33#include "godot_space_2d.h"
34
35void GodotCollisionObject2D::add_shape(GodotShape2D *p_shape, const Transform2D &p_transform, bool p_disabled) {
36 Shape s;
37 s.shape = p_shape;
38 s.xform = p_transform;
39 s.xform_inv = s.xform.affine_inverse();
40 s.bpid = 0; //needs update
41 s.disabled = p_disabled;
42 s.one_way_collision = false;
43 s.one_way_collision_margin = 0;
44 shapes.push_back(s);
45 p_shape->add_owner(this);
46
47 if (!pending_shape_update_list.in_list()) {
48 GodotPhysicsServer2D::godot_singleton->pending_shape_update_list.add(&pending_shape_update_list);
49 }
50}
51
52void GodotCollisionObject2D::set_shape(int p_index, GodotShape2D *p_shape) {
53 ERR_FAIL_INDEX(p_index, shapes.size());
54 shapes[p_index].shape->remove_owner(this);
55 shapes.write[p_index].shape = p_shape;
56
57 p_shape->add_owner(this);
58
59 if (!pending_shape_update_list.in_list()) {
60 GodotPhysicsServer2D::godot_singleton->pending_shape_update_list.add(&pending_shape_update_list);
61 }
62}
63
64void GodotCollisionObject2D::set_shape_transform(int p_index, const Transform2D &p_transform) {
65 ERR_FAIL_INDEX(p_index, shapes.size());
66
67 shapes.write[p_index].xform = p_transform;
68 shapes.write[p_index].xform_inv = p_transform.affine_inverse();
69
70 if (!pending_shape_update_list.in_list()) {
71 GodotPhysicsServer2D::godot_singleton->pending_shape_update_list.add(&pending_shape_update_list);
72 }
73}
74
75void GodotCollisionObject2D::set_shape_disabled(int p_idx, bool p_disabled) {
76 ERR_FAIL_INDEX(p_idx, shapes.size());
77
78 GodotCollisionObject2D::Shape &shape = shapes.write[p_idx];
79 if (shape.disabled == p_disabled) {
80 return;
81 }
82
83 shape.disabled = p_disabled;
84
85 if (!space) {
86 return;
87 }
88
89 if (p_disabled && shape.bpid != 0) {
90 space->get_broadphase()->remove(shape.bpid);
91 shape.bpid = 0;
92 if (!pending_shape_update_list.in_list()) {
93 GodotPhysicsServer2D::godot_singleton->pending_shape_update_list.add(&pending_shape_update_list);
94 }
95 } else if (!p_disabled && shape.bpid == 0) {
96 if (!pending_shape_update_list.in_list()) {
97 GodotPhysicsServer2D::godot_singleton->pending_shape_update_list.add(&pending_shape_update_list);
98 }
99 }
100}
101
102void GodotCollisionObject2D::remove_shape(GodotShape2D *p_shape) {
103 //remove a shape, all the times it appears
104 for (int i = 0; i < shapes.size(); i++) {
105 if (shapes[i].shape == p_shape) {
106 remove_shape(i);
107 i--;
108 }
109 }
110}
111
112void GodotCollisionObject2D::remove_shape(int p_index) {
113 //remove anything from shape to be erased to end, so subindices don't change
114 ERR_FAIL_INDEX(p_index, shapes.size());
115 for (int i = p_index; i < shapes.size(); i++) {
116 if (shapes[i].bpid == 0) {
117 continue;
118 }
119 //should never get here with a null owner
120 space->get_broadphase()->remove(shapes[i].bpid);
121 shapes.write[i].bpid = 0;
122 }
123 shapes[p_index].shape->remove_owner(this);
124 shapes.remove_at(p_index);
125
126 if (!pending_shape_update_list.in_list()) {
127 GodotPhysicsServer2D::godot_singleton->pending_shape_update_list.add(&pending_shape_update_list);
128 }
129 // _update_shapes();
130 // _shapes_changed();
131}
132
133void GodotCollisionObject2D::_set_static(bool p_static) {
134 if (_static == p_static) {
135 return;
136 }
137 _static = p_static;
138
139 if (!space) {
140 return;
141 }
142 for (int i = 0; i < get_shape_count(); i++) {
143 const Shape &s = shapes[i];
144 if (s.bpid > 0) {
145 space->get_broadphase()->set_static(s.bpid, _static);
146 }
147 }
148}
149
150void GodotCollisionObject2D::_unregister_shapes() {
151 for (int i = 0; i < shapes.size(); i++) {
152 Shape &s = shapes.write[i];
153 if (s.bpid > 0) {
154 space->get_broadphase()->remove(s.bpid);
155 s.bpid = 0;
156 }
157 }
158}
159
160void GodotCollisionObject2D::_update_shapes() {
161 if (!space) {
162 return;
163 }
164
165 for (int i = 0; i < shapes.size(); i++) {
166 Shape &s = shapes.write[i];
167 if (s.disabled) {
168 continue;
169 }
170
171 //not quite correct, should compute the next matrix..
172 Rect2 shape_aabb = s.shape->get_aabb();
173 Transform2D xform = transform * s.xform;
174 shape_aabb = xform.xform(shape_aabb);
175 shape_aabb.grow_by((s.aabb_cache.size.x + s.aabb_cache.size.y) * 0.5 * 0.05);
176 s.aabb_cache = shape_aabb;
177
178 if (s.bpid == 0) {
179 s.bpid = space->get_broadphase()->create(this, i, shape_aabb, _static);
180 space->get_broadphase()->set_static(s.bpid, _static);
181 }
182
183 space->get_broadphase()->move(s.bpid, shape_aabb);
184 }
185}
186
187void GodotCollisionObject2D::_update_shapes_with_motion(const Vector2 &p_motion) {
188 if (!space) {
189 return;
190 }
191
192 for (int i = 0; i < shapes.size(); i++) {
193 Shape &s = shapes.write[i];
194 if (s.disabled) {
195 continue;
196 }
197
198 //not quite correct, should compute the next matrix..
199 Rect2 shape_aabb = s.shape->get_aabb();
200 Transform2D xform = transform * s.xform;
201 shape_aabb = xform.xform(shape_aabb);
202 shape_aabb = shape_aabb.merge(Rect2(shape_aabb.position + p_motion, shape_aabb.size)); //use motion
203 s.aabb_cache = shape_aabb;
204
205 if (s.bpid == 0) {
206 s.bpid = space->get_broadphase()->create(this, i, shape_aabb, _static);
207 space->get_broadphase()->set_static(s.bpid, _static);
208 }
209
210 space->get_broadphase()->move(s.bpid, shape_aabb);
211 }
212}
213
214void GodotCollisionObject2D::_set_space(GodotSpace2D *p_space) {
215 if (space) {
216 space->remove_object(this);
217
218 for (int i = 0; i < shapes.size(); i++) {
219 Shape &s = shapes.write[i];
220 if (s.bpid) {
221 space->get_broadphase()->remove(s.bpid);
222 s.bpid = 0;
223 }
224 }
225 }
226
227 space = p_space;
228
229 if (space) {
230 space->add_object(this);
231 _update_shapes();
232 }
233}
234
235void GodotCollisionObject2D::_shape_changed() {
236 _update_shapes();
237 _shapes_changed();
238}
239
240GodotCollisionObject2D::GodotCollisionObject2D(Type p_type) :
241 pending_shape_update_list(this) {
242 type = p_type;
243}
244