1/**************************************************************************/
2/* godot_area_3d.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_area_3d.h"
32
33#include "godot_body_3d.h"
34#include "godot_soft_body_3d.h"
35#include "godot_space_3d.h"
36
37GodotArea3D::BodyKey::BodyKey(GodotSoftBody3D *p_body, uint32_t p_body_shape, uint32_t p_area_shape) {
38 rid = p_body->get_self();
39 instance_id = p_body->get_instance_id();
40 body_shape = p_body_shape;
41 area_shape = p_area_shape;
42}
43
44GodotArea3D::BodyKey::BodyKey(GodotBody3D *p_body, uint32_t p_body_shape, uint32_t p_area_shape) {
45 rid = p_body->get_self();
46 instance_id = p_body->get_instance_id();
47 body_shape = p_body_shape;
48 area_shape = p_area_shape;
49}
50
51GodotArea3D::BodyKey::BodyKey(GodotArea3D *p_body, uint32_t p_body_shape, uint32_t p_area_shape) {
52 rid = p_body->get_self();
53 instance_id = p_body->get_instance_id();
54 body_shape = p_body_shape;
55 area_shape = p_area_shape;
56}
57
58void GodotArea3D::_shapes_changed() {
59 if (!moved_list.in_list() && get_space()) {
60 get_space()->area_add_to_moved_list(&moved_list);
61 }
62}
63
64void GodotArea3D::set_transform(const Transform3D &p_transform) {
65 if (!moved_list.in_list() && get_space()) {
66 get_space()->area_add_to_moved_list(&moved_list);
67 }
68
69 _set_transform(p_transform);
70 _set_inv_transform(p_transform.affine_inverse());
71}
72
73void GodotArea3D::set_space(GodotSpace3D *p_space) {
74 if (get_space()) {
75 if (monitor_query_list.in_list()) {
76 get_space()->area_remove_from_monitor_query_list(&monitor_query_list);
77 }
78 if (moved_list.in_list()) {
79 get_space()->area_remove_from_moved_list(&moved_list);
80 }
81 }
82
83 monitored_bodies.clear();
84 monitored_areas.clear();
85
86 _set_space(p_space);
87}
88
89void GodotArea3D::set_monitor_callback(const Callable &p_callback) {
90 ObjectID id = p_callback.get_object_id();
91 if (id == monitor_callback.get_object_id()) {
92 monitor_callback = p_callback;
93 return;
94 }
95
96 _unregister_shapes();
97
98 monitor_callback = p_callback;
99
100 monitored_bodies.clear();
101 monitored_areas.clear();
102
103 _shape_changed();
104
105 if (!moved_list.in_list() && get_space()) {
106 get_space()->area_add_to_moved_list(&moved_list);
107 }
108}
109
110void GodotArea3D::set_area_monitor_callback(const Callable &p_callback) {
111 ObjectID id = p_callback.get_object_id();
112 if (id == area_monitor_callback.get_object_id()) {
113 area_monitor_callback = p_callback;
114 return;
115 }
116
117 _unregister_shapes();
118
119 area_monitor_callback = p_callback;
120
121 monitored_bodies.clear();
122 monitored_areas.clear();
123
124 _shape_changed();
125
126 if (!moved_list.in_list() && get_space()) {
127 get_space()->area_add_to_moved_list(&moved_list);
128 }
129}
130
131void GodotArea3D::_set_space_override_mode(PhysicsServer3D::AreaSpaceOverrideMode &r_mode, PhysicsServer3D::AreaSpaceOverrideMode p_new_mode) {
132 bool do_override = p_new_mode != PhysicsServer3D::AREA_SPACE_OVERRIDE_DISABLED;
133 if (do_override == (r_mode != PhysicsServer3D::AREA_SPACE_OVERRIDE_DISABLED)) {
134 return;
135 }
136 _unregister_shapes();
137 r_mode = p_new_mode;
138 _shape_changed();
139}
140
141void GodotArea3D::set_param(PhysicsServer3D::AreaParameter p_param, const Variant &p_value) {
142 switch (p_param) {
143 case PhysicsServer3D::AREA_PARAM_GRAVITY_OVERRIDE_MODE:
144 _set_space_override_mode(gravity_override_mode, (PhysicsServer3D::AreaSpaceOverrideMode)(int)p_value);
145 break;
146 case PhysicsServer3D::AREA_PARAM_GRAVITY:
147 gravity = p_value;
148 break;
149 case PhysicsServer3D::AREA_PARAM_GRAVITY_VECTOR:
150 gravity_vector = p_value;
151 break;
152 case PhysicsServer3D::AREA_PARAM_GRAVITY_IS_POINT:
153 gravity_is_point = p_value;
154 break;
155 case PhysicsServer3D::AREA_PARAM_GRAVITY_POINT_UNIT_DISTANCE:
156 gravity_point_unit_distance = p_value;
157 break;
158 case PhysicsServer3D::AREA_PARAM_LINEAR_DAMP_OVERRIDE_MODE:
159 _set_space_override_mode(linear_damping_override_mode, (PhysicsServer3D::AreaSpaceOverrideMode)(int)p_value);
160 break;
161 case PhysicsServer3D::AREA_PARAM_LINEAR_DAMP:
162 linear_damp = p_value;
163 break;
164 case PhysicsServer3D::AREA_PARAM_ANGULAR_DAMP_OVERRIDE_MODE:
165 _set_space_override_mode(angular_damping_override_mode, (PhysicsServer3D::AreaSpaceOverrideMode)(int)p_value);
166 break;
167 case PhysicsServer3D::AREA_PARAM_ANGULAR_DAMP:
168 angular_damp = p_value;
169 break;
170 case PhysicsServer3D::AREA_PARAM_PRIORITY:
171 priority = p_value;
172 break;
173 case PhysicsServer3D::AREA_PARAM_WIND_FORCE_MAGNITUDE:
174 ERR_FAIL_COND_MSG(wind_force_magnitude < 0, "Wind force magnitude must be a non-negative real number, but a negative number was specified.");
175 wind_force_magnitude = p_value;
176 break;
177 case PhysicsServer3D::AREA_PARAM_WIND_SOURCE:
178 wind_source = p_value;
179 break;
180 case PhysicsServer3D::AREA_PARAM_WIND_DIRECTION:
181 wind_direction = p_value;
182 break;
183 case PhysicsServer3D::AREA_PARAM_WIND_ATTENUATION_FACTOR:
184 ERR_FAIL_COND_MSG(wind_attenuation_factor < 0, "Wind attenuation factor must be a non-negative real number, but a negative number was specified.");
185 wind_attenuation_factor = p_value;
186 break;
187 }
188}
189
190Variant GodotArea3D::get_param(PhysicsServer3D::AreaParameter p_param) const {
191 switch (p_param) {
192 case PhysicsServer3D::AREA_PARAM_GRAVITY_OVERRIDE_MODE:
193 return gravity_override_mode;
194 case PhysicsServer3D::AREA_PARAM_GRAVITY:
195 return gravity;
196 case PhysicsServer3D::AREA_PARAM_GRAVITY_VECTOR:
197 return gravity_vector;
198 case PhysicsServer3D::AREA_PARAM_GRAVITY_IS_POINT:
199 return gravity_is_point;
200 case PhysicsServer3D::AREA_PARAM_GRAVITY_POINT_UNIT_DISTANCE:
201 return gravity_point_unit_distance;
202 case PhysicsServer3D::AREA_PARAM_LINEAR_DAMP_OVERRIDE_MODE:
203 return linear_damping_override_mode;
204 case PhysicsServer3D::AREA_PARAM_LINEAR_DAMP:
205 return linear_damp;
206 case PhysicsServer3D::AREA_PARAM_ANGULAR_DAMP_OVERRIDE_MODE:
207 return angular_damping_override_mode;
208 case PhysicsServer3D::AREA_PARAM_ANGULAR_DAMP:
209 return angular_damp;
210 case PhysicsServer3D::AREA_PARAM_PRIORITY:
211 return priority;
212 case PhysicsServer3D::AREA_PARAM_WIND_FORCE_MAGNITUDE:
213 return wind_force_magnitude;
214 case PhysicsServer3D::AREA_PARAM_WIND_SOURCE:
215 return wind_source;
216 case PhysicsServer3D::AREA_PARAM_WIND_DIRECTION:
217 return wind_direction;
218 case PhysicsServer3D::AREA_PARAM_WIND_ATTENUATION_FACTOR:
219 return wind_attenuation_factor;
220 }
221
222 return Variant();
223}
224
225void GodotArea3D::_queue_monitor_update() {
226 ERR_FAIL_COND(!get_space());
227
228 if (!monitor_query_list.in_list()) {
229 get_space()->area_add_to_monitor_query_list(&monitor_query_list);
230 }
231}
232
233void GodotArea3D::set_monitorable(bool p_monitorable) {
234 if (monitorable == p_monitorable) {
235 return;
236 }
237
238 monitorable = p_monitorable;
239 _set_static(!monitorable);
240 _shapes_changed();
241}
242
243void GodotArea3D::call_queries() {
244 if (!monitor_callback.is_null() && !monitored_bodies.is_empty()) {
245 if (monitor_callback.is_valid()) {
246 Variant res[5];
247 Variant *resptr[5];
248 for (int i = 0; i < 5; i++) {
249 resptr[i] = &res[i];
250 }
251
252 for (HashMap<BodyKey, BodyState, BodyKey>::Iterator E = monitored_bodies.begin(); E;) {
253 if (E->value.state == 0) { // Nothing happened
254 HashMap<BodyKey, BodyState, BodyKey>::Iterator next = E;
255 ++next;
256 monitored_bodies.remove(E);
257 E = next;
258 continue;
259 }
260
261 res[0] = E->value.state > 0 ? PhysicsServer3D::AREA_BODY_ADDED : PhysicsServer3D::AREA_BODY_REMOVED;
262 res[1] = E->key.rid;
263 res[2] = E->key.instance_id;
264 res[3] = E->key.body_shape;
265 res[4] = E->key.area_shape;
266
267 HashMap<BodyKey, BodyState, BodyKey>::Iterator next = E;
268 ++next;
269 monitored_bodies.remove(E);
270 E = next;
271
272 Callable::CallError ce;
273 Variant ret;
274 monitor_callback.callp((const Variant **)resptr, 5, ret, ce);
275
276 if (ce.error != Callable::CallError::CALL_OK) {
277 ERR_PRINT_ONCE("Error calling monitor callback method " + Variant::get_callable_error_text(monitor_callback, (const Variant **)resptr, 5, ce));
278 }
279 }
280 } else {
281 monitored_bodies.clear();
282 monitor_callback = Callable();
283 }
284 }
285
286 if (!area_monitor_callback.is_null() && !monitored_areas.is_empty()) {
287 if (area_monitor_callback.is_valid()) {
288 Variant res[5];
289 Variant *resptr[5];
290 for (int i = 0; i < 5; i++) {
291 resptr[i] = &res[i];
292 }
293
294 for (HashMap<BodyKey, BodyState, BodyKey>::Iterator E = monitored_areas.begin(); E;) {
295 if (E->value.state == 0) { // Nothing happened
296 HashMap<BodyKey, BodyState, BodyKey>::Iterator next = E;
297 ++next;
298 monitored_areas.remove(E);
299 E = next;
300 continue;
301 }
302
303 res[0] = E->value.state > 0 ? PhysicsServer3D::AREA_BODY_ADDED : PhysicsServer3D::AREA_BODY_REMOVED;
304 res[1] = E->key.rid;
305 res[2] = E->key.instance_id;
306 res[3] = E->key.body_shape;
307 res[4] = E->key.area_shape;
308
309 HashMap<BodyKey, BodyState, BodyKey>::Iterator next = E;
310 ++next;
311 monitored_areas.remove(E);
312 E = next;
313
314 Callable::CallError ce;
315 Variant ret;
316 area_monitor_callback.callp((const Variant **)resptr, 5, ret, ce);
317
318 if (ce.error != Callable::CallError::CALL_OK) {
319 ERR_PRINT_ONCE("Error calling area monitor callback method " + Variant::get_callable_error_text(area_monitor_callback, (const Variant **)resptr, 5, ce));
320 }
321 }
322 } else {
323 monitored_areas.clear();
324 area_monitor_callback = Callable();
325 }
326 }
327}
328
329void GodotArea3D::compute_gravity(const Vector3 &p_position, Vector3 &r_gravity) const {
330 if (is_gravity_point()) {
331 const real_t gr_unit_dist = get_gravity_point_unit_distance();
332 Vector3 v = get_transform().xform(get_gravity_vector()) - p_position;
333 if (gr_unit_dist > 0) {
334 const real_t v_length_sq = v.length_squared();
335 if (v_length_sq > 0) {
336 const real_t gravity_strength = get_gravity() * gr_unit_dist * gr_unit_dist / v_length_sq;
337 r_gravity = v.normalized() * gravity_strength;
338 } else {
339 r_gravity = Vector3();
340 }
341 } else {
342 r_gravity = v.normalized() * get_gravity();
343 }
344 } else {
345 r_gravity = get_gravity_vector() * get_gravity();
346 }
347}
348
349GodotArea3D::GodotArea3D() :
350 GodotCollisionObject3D(TYPE_AREA),
351 monitor_query_list(this),
352 moved_list(this) {
353 _set_static(true); //areas are never active
354 set_ray_pickable(false);
355}
356
357GodotArea3D::~GodotArea3D() {
358}
359