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 | |
35 | void 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 | |
52 | void 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 | |
64 | void 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 | |
75 | void 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 | |
102 | void 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 | |
112 | void 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 | |
133 | void 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 | |
150 | void 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 | |
160 | void 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 | |
187 | void 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 | |
214 | void 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 | |
235 | void GodotCollisionObject2D::_shape_changed() { |
236 | _update_shapes(); |
237 | _shapes_changed(); |
238 | } |
239 | |
240 | GodotCollisionObject2D::GodotCollisionObject2D(Type p_type) : |
241 | pending_shape_update_list(this) { |
242 | type = p_type; |
243 | } |
244 | |