1 | /**************************************************************************/ |
2 | /* shape_cast_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 "shape_cast_3d.h" |
32 | |
33 | #include "scene/3d/collision_object_3d.h" |
34 | #include "scene/3d/mesh_instance_3d.h" |
35 | #include "scene/resources/concave_polygon_shape_3d.h" |
36 | |
37 | void ShapeCast3D::_notification(int p_what) { |
38 | switch (p_what) { |
39 | case NOTIFICATION_ENTER_TREE: { |
40 | if (Engine::get_singleton()->is_editor_hint()) { |
41 | _update_debug_shape_vertices(); |
42 | } |
43 | if (enabled && !Engine::get_singleton()->is_editor_hint()) { |
44 | set_physics_process_internal(true); |
45 | } else { |
46 | set_physics_process_internal(false); |
47 | } |
48 | |
49 | if (get_tree()->is_debugging_collisions_hint()) { |
50 | _update_debug_shape(); |
51 | } |
52 | |
53 | if (Object::cast_to<CollisionObject3D>(get_parent())) { |
54 | if (exclude_parent_body) { |
55 | exclude.insert(Object::cast_to<CollisionObject3D>(get_parent())->get_rid()); |
56 | } else { |
57 | exclude.erase(Object::cast_to<CollisionObject3D>(get_parent())->get_rid()); |
58 | } |
59 | } |
60 | } break; |
61 | |
62 | case NOTIFICATION_EXIT_TREE: { |
63 | if (enabled) { |
64 | set_physics_process_internal(false); |
65 | } |
66 | |
67 | if (debug_shape) { |
68 | _clear_debug_shape(); |
69 | } |
70 | } break; |
71 | |
72 | case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { |
73 | if (!enabled) { |
74 | break; |
75 | } |
76 | |
77 | bool prev_collision_state = collided; |
78 | _update_shapecast_state(); |
79 | if (get_tree()->is_debugging_collisions_hint()) { |
80 | if (prev_collision_state != collided) { |
81 | _update_debug_shape_material(true); |
82 | } |
83 | if (collided) { |
84 | _update_debug_shape(); |
85 | } |
86 | if (prev_collision_state == collided && !collided) { |
87 | _update_debug_shape(); |
88 | } |
89 | } |
90 | } break; |
91 | } |
92 | } |
93 | |
94 | void ShapeCast3D::_bind_methods() { |
95 | #ifndef DISABLE_DEPRECATED |
96 | ClassDB::bind_method(D_METHOD("resource_changed" , "resource" ), &ShapeCast3D::resource_changed); |
97 | #endif |
98 | |
99 | ClassDB::bind_method(D_METHOD("set_enabled" , "enabled" ), &ShapeCast3D::set_enabled); |
100 | ClassDB::bind_method(D_METHOD("is_enabled" ), &ShapeCast3D::is_enabled); |
101 | |
102 | ClassDB::bind_method(D_METHOD("set_shape" , "shape" ), &ShapeCast3D::set_shape); |
103 | ClassDB::bind_method(D_METHOD("get_shape" ), &ShapeCast3D::get_shape); |
104 | |
105 | ClassDB::bind_method(D_METHOD("set_target_position" , "local_point" ), &ShapeCast3D::set_target_position); |
106 | ClassDB::bind_method(D_METHOD("get_target_position" ), &ShapeCast3D::get_target_position); |
107 | |
108 | ClassDB::bind_method(D_METHOD("set_margin" , "margin" ), &ShapeCast3D::set_margin); |
109 | ClassDB::bind_method(D_METHOD("get_margin" ), &ShapeCast3D::get_margin); |
110 | |
111 | ClassDB::bind_method(D_METHOD("set_max_results" , "max_results" ), &ShapeCast3D::set_max_results); |
112 | ClassDB::bind_method(D_METHOD("get_max_results" ), &ShapeCast3D::get_max_results); |
113 | |
114 | ClassDB::bind_method(D_METHOD("is_colliding" ), &ShapeCast3D::is_colliding); |
115 | ClassDB::bind_method(D_METHOD("get_collision_count" ), &ShapeCast3D::get_collision_count); |
116 | |
117 | ClassDB::bind_method(D_METHOD("force_shapecast_update" ), &ShapeCast3D::force_shapecast_update); |
118 | |
119 | ClassDB::bind_method(D_METHOD("get_collider" , "index" ), &ShapeCast3D::get_collider); |
120 | ClassDB::bind_method(D_METHOD("get_collider_rid" , "index" ), &ShapeCast3D::get_collider_rid); |
121 | ClassDB::bind_method(D_METHOD("get_collider_shape" , "index" ), &ShapeCast3D::get_collider_shape); |
122 | ClassDB::bind_method(D_METHOD("get_collision_point" , "index" ), &ShapeCast3D::get_collision_point); |
123 | ClassDB::bind_method(D_METHOD("get_collision_normal" , "index" ), &ShapeCast3D::get_collision_normal); |
124 | |
125 | ClassDB::bind_method(D_METHOD("get_closest_collision_safe_fraction" ), &ShapeCast3D::get_closest_collision_safe_fraction); |
126 | ClassDB::bind_method(D_METHOD("get_closest_collision_unsafe_fraction" ), &ShapeCast3D::get_closest_collision_unsafe_fraction); |
127 | |
128 | ClassDB::bind_method(D_METHOD("add_exception_rid" , "rid" ), &ShapeCast3D::add_exception_rid); |
129 | ClassDB::bind_method(D_METHOD("add_exception" , "node" ), &ShapeCast3D::add_exception); |
130 | |
131 | ClassDB::bind_method(D_METHOD("remove_exception_rid" , "rid" ), &ShapeCast3D::remove_exception_rid); |
132 | ClassDB::bind_method(D_METHOD("remove_exception" , "node" ), &ShapeCast3D::remove_exception); |
133 | |
134 | ClassDB::bind_method(D_METHOD("clear_exceptions" ), &ShapeCast3D::clear_exceptions); |
135 | |
136 | ClassDB::bind_method(D_METHOD("set_collision_mask" , "mask" ), &ShapeCast3D::set_collision_mask); |
137 | ClassDB::bind_method(D_METHOD("get_collision_mask" ), &ShapeCast3D::get_collision_mask); |
138 | |
139 | ClassDB::bind_method(D_METHOD("set_collision_mask_value" , "layer_number" , "value" ), &ShapeCast3D::set_collision_mask_value); |
140 | ClassDB::bind_method(D_METHOD("get_collision_mask_value" , "layer_number" ), &ShapeCast3D::get_collision_mask_value); |
141 | |
142 | ClassDB::bind_method(D_METHOD("set_exclude_parent_body" , "mask" ), &ShapeCast3D::set_exclude_parent_body); |
143 | ClassDB::bind_method(D_METHOD("get_exclude_parent_body" ), &ShapeCast3D::get_exclude_parent_body); |
144 | |
145 | ClassDB::bind_method(D_METHOD("set_collide_with_areas" , "enable" ), &ShapeCast3D::set_collide_with_areas); |
146 | ClassDB::bind_method(D_METHOD("is_collide_with_areas_enabled" ), &ShapeCast3D::is_collide_with_areas_enabled); |
147 | |
148 | ClassDB::bind_method(D_METHOD("set_collide_with_bodies" , "enable" ), &ShapeCast3D::set_collide_with_bodies); |
149 | ClassDB::bind_method(D_METHOD("is_collide_with_bodies_enabled" ), &ShapeCast3D::is_collide_with_bodies_enabled); |
150 | |
151 | ClassDB::bind_method(D_METHOD("_get_collision_result" ), &ShapeCast3D::_get_collision_result); |
152 | |
153 | ClassDB::bind_method(D_METHOD("set_debug_shape_custom_color" , "debug_shape_custom_color" ), &ShapeCast3D::set_debug_shape_custom_color); |
154 | ClassDB::bind_method(D_METHOD("get_debug_shape_custom_color" ), &ShapeCast3D::get_debug_shape_custom_color); |
155 | |
156 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled" ), "set_enabled" , "is_enabled" ); |
157 | ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shape" , PROPERTY_HINT_RESOURCE_TYPE, "Shape3D" ), "set_shape" , "get_shape" ); |
158 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "exclude_parent" ), "set_exclude_parent_body" , "get_exclude_parent_body" ); |
159 | ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "target_position" , PROPERTY_HINT_NONE, "suffix:m" ), "set_target_position" , "get_target_position" ); |
160 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "margin" , PROPERTY_HINT_RANGE, "0,100,0.01,suffix:m" ), "set_margin" , "get_margin" ); |
161 | ADD_PROPERTY(PropertyInfo(Variant::INT, "max_results" ), "set_max_results" , "get_max_results" ); |
162 | ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask" , PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask" , "get_collision_mask" ); |
163 | ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "collision_result" , PROPERTY_HINT_NONE, "" , PROPERTY_USAGE_NO_EDITOR), "" , "_get_collision_result" ); |
164 | |
165 | ADD_GROUP("Collide With" , "collide_with" ); |
166 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with_areas" , PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collide_with_areas" , "is_collide_with_areas_enabled" ); |
167 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with_bodies" , PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collide_with_bodies" , "is_collide_with_bodies_enabled" ); |
168 | |
169 | ADD_GROUP("Debug Shape" , "debug_shape" ); |
170 | ADD_PROPERTY(PropertyInfo(Variant::COLOR, "debug_shape_custom_color" ), "set_debug_shape_custom_color" , "get_debug_shape_custom_color" ); |
171 | } |
172 | |
173 | PackedStringArray ShapeCast3D::get_configuration_warnings() const { |
174 | PackedStringArray warnings = Node3D::get_configuration_warnings(); |
175 | |
176 | if (shape.is_null()) { |
177 | warnings.push_back(RTR("This node cannot interact with other objects unless a Shape3D is assigned." )); |
178 | } |
179 | if (shape.is_valid() && Object::cast_to<ConcavePolygonShape3D>(*shape)) { |
180 | warnings.push_back(RTR("ShapeCast3D does not support ConcavePolygonShape3Ds. Collisions will not be reported." )); |
181 | } |
182 | return warnings; |
183 | } |
184 | |
185 | void ShapeCast3D::set_enabled(bool p_enabled) { |
186 | enabled = p_enabled; |
187 | update_gizmos(); |
188 | |
189 | if (is_inside_tree() && !Engine::get_singleton()->is_editor_hint()) { |
190 | set_physics_process_internal(p_enabled); |
191 | } |
192 | if (!p_enabled) { |
193 | collided = false; |
194 | } |
195 | |
196 | if (is_inside_tree() && get_tree()->is_debugging_collisions_hint()) { |
197 | if (p_enabled) { |
198 | _update_debug_shape(); |
199 | } else { |
200 | _clear_debug_shape(); |
201 | } |
202 | } |
203 | } |
204 | |
205 | bool ShapeCast3D::is_enabled() const { |
206 | return enabled; |
207 | } |
208 | |
209 | void ShapeCast3D::set_target_position(const Vector3 &p_point) { |
210 | target_position = p_point; |
211 | if (is_inside_tree() && get_tree()->is_debugging_collisions_hint()) { |
212 | _update_debug_shape(); |
213 | } |
214 | update_gizmos(); |
215 | |
216 | if (Engine::get_singleton()->is_editor_hint()) { |
217 | if (is_inside_tree()) { |
218 | _update_debug_shape_vertices(); |
219 | } |
220 | } else if (debug_shape) { |
221 | _update_debug_shape(); |
222 | } |
223 | } |
224 | |
225 | Vector3 ShapeCast3D::get_target_position() const { |
226 | return target_position; |
227 | } |
228 | |
229 | void ShapeCast3D::set_margin(real_t p_margin) { |
230 | margin = p_margin; |
231 | } |
232 | |
233 | real_t ShapeCast3D::get_margin() const { |
234 | return margin; |
235 | } |
236 | |
237 | void ShapeCast3D::set_max_results(int p_max_results) { |
238 | max_results = p_max_results; |
239 | } |
240 | |
241 | int ShapeCast3D::get_max_results() const { |
242 | return max_results; |
243 | } |
244 | |
245 | void ShapeCast3D::set_collision_mask(uint32_t p_mask) { |
246 | collision_mask = p_mask; |
247 | } |
248 | |
249 | uint32_t ShapeCast3D::get_collision_mask() const { |
250 | return collision_mask; |
251 | } |
252 | |
253 | void ShapeCast3D::set_collision_mask_value(int p_layer_number, bool p_value) { |
254 | ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive." ); |
255 | ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive." ); |
256 | uint32_t mask = get_collision_mask(); |
257 | if (p_value) { |
258 | mask |= 1 << (p_layer_number - 1); |
259 | } else { |
260 | mask &= ~(1 << (p_layer_number - 1)); |
261 | } |
262 | set_collision_mask(mask); |
263 | } |
264 | |
265 | bool ShapeCast3D::get_collision_mask_value(int p_layer_number) const { |
266 | ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive." ); |
267 | ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive." ); |
268 | return get_collision_mask() & (1 << (p_layer_number - 1)); |
269 | } |
270 | |
271 | int ShapeCast3D::get_collision_count() const { |
272 | return result.size(); |
273 | } |
274 | |
275 | bool ShapeCast3D::is_colliding() const { |
276 | return collided; |
277 | } |
278 | |
279 | Object *ShapeCast3D::get_collider(int p_idx) const { |
280 | ERR_FAIL_INDEX_V_MSG(p_idx, result.size(), nullptr, "No collider found." ); |
281 | |
282 | if (result[p_idx].collider_id.is_null()) { |
283 | return nullptr; |
284 | } |
285 | return ObjectDB::get_instance(result[p_idx].collider_id); |
286 | } |
287 | |
288 | RID ShapeCast3D::get_collider_rid(int p_idx) const { |
289 | ERR_FAIL_INDEX_V_MSG(p_idx, result.size(), RID(), "No collider RID found." ); |
290 | return result[p_idx].rid; |
291 | } |
292 | |
293 | int ShapeCast3D::get_collider_shape(int p_idx) const { |
294 | ERR_FAIL_INDEX_V_MSG(p_idx, result.size(), -1, "No collider shape found." ); |
295 | return result[p_idx].shape; |
296 | } |
297 | |
298 | Vector3 ShapeCast3D::get_collision_point(int p_idx) const { |
299 | ERR_FAIL_INDEX_V_MSG(p_idx, result.size(), Vector3(), "No collision point found." ); |
300 | return result[p_idx].point; |
301 | } |
302 | |
303 | Vector3 ShapeCast3D::get_collision_normal(int p_idx) const { |
304 | ERR_FAIL_INDEX_V_MSG(p_idx, result.size(), Vector3(), "No collision normal found." ); |
305 | return result[p_idx].normal; |
306 | } |
307 | |
308 | real_t ShapeCast3D::get_closest_collision_safe_fraction() const { |
309 | return collision_safe_fraction; |
310 | } |
311 | |
312 | real_t ShapeCast3D::get_closest_collision_unsafe_fraction() const { |
313 | return collision_unsafe_fraction; |
314 | } |
315 | |
316 | #ifndef DISABLE_DEPRECATED |
317 | void ShapeCast3D::resource_changed(Ref<Resource> p_res) { |
318 | } |
319 | #endif |
320 | |
321 | void ShapeCast3D::_shape_changed() { |
322 | update_gizmos(); |
323 | bool is_editor = Engine::get_singleton()->is_editor_hint(); |
324 | if (is_inside_tree() && (is_editor || get_tree()->is_debugging_collisions_hint())) { |
325 | _update_debug_shape(); |
326 | } |
327 | } |
328 | |
329 | void ShapeCast3D::set_shape(const Ref<Shape3D> &p_shape) { |
330 | if (p_shape == shape) { |
331 | return; |
332 | } |
333 | if (shape.is_valid()) { |
334 | shape->disconnect_changed(callable_mp(this, &ShapeCast3D::_shape_changed)); |
335 | } |
336 | shape = p_shape; |
337 | if (shape.is_valid()) { |
338 | shape->connect_changed(callable_mp(this, &ShapeCast3D::_shape_changed)); |
339 | shape_rid = shape->get_rid(); |
340 | } |
341 | |
342 | bool is_editor = Engine::get_singleton()->is_editor_hint(); |
343 | if (is_inside_tree() && (is_editor || get_tree()->is_debugging_collisions_hint())) { |
344 | _update_debug_shape(); |
345 | } |
346 | update_gizmos(); |
347 | update_configuration_warnings(); |
348 | } |
349 | |
350 | Ref<Shape3D> ShapeCast3D::get_shape() const { |
351 | return shape; |
352 | } |
353 | |
354 | void ShapeCast3D::set_exclude_parent_body(bool p_exclude_parent_body) { |
355 | if (exclude_parent_body == p_exclude_parent_body) { |
356 | return; |
357 | } |
358 | exclude_parent_body = p_exclude_parent_body; |
359 | |
360 | if (!is_inside_tree()) { |
361 | return; |
362 | } |
363 | if (Object::cast_to<CollisionObject3D>(get_parent())) { |
364 | if (exclude_parent_body) { |
365 | exclude.insert(Object::cast_to<CollisionObject3D>(get_parent())->get_rid()); |
366 | } else { |
367 | exclude.erase(Object::cast_to<CollisionObject3D>(get_parent())->get_rid()); |
368 | } |
369 | } |
370 | } |
371 | |
372 | bool ShapeCast3D::get_exclude_parent_body() const { |
373 | return exclude_parent_body; |
374 | } |
375 | |
376 | void ShapeCast3D::_update_shapecast_state() { |
377 | result.clear(); |
378 | |
379 | ERR_FAIL_COND_MSG(shape.is_null(), "Null reference to shape. ShapeCast3D requires a Shape3D to sweep for collisions." ); |
380 | |
381 | Ref<World3D> w3d = get_world_3d(); |
382 | ERR_FAIL_COND(w3d.is_null()); |
383 | |
384 | PhysicsDirectSpaceState3D *dss = PhysicsServer3D::get_singleton()->space_get_direct_state(w3d->get_space()); |
385 | ERR_FAIL_NULL(dss); |
386 | |
387 | Transform3D gt = get_global_transform(); |
388 | |
389 | PhysicsDirectSpaceState3D::ShapeParameters params; |
390 | params.shape_rid = shape_rid; |
391 | params.transform = gt; |
392 | params.motion = gt.basis.xform(target_position); |
393 | params.margin = margin; |
394 | params.exclude = exclude; |
395 | params.collision_mask = collision_mask; |
396 | params.collide_with_bodies = collide_with_bodies; |
397 | params.collide_with_areas = collide_with_areas; |
398 | |
399 | collision_safe_fraction = 0.0; |
400 | collision_unsafe_fraction = 0.0; |
401 | |
402 | if (target_position != Vector3()) { |
403 | dss->cast_motion(params, collision_safe_fraction, collision_unsafe_fraction); |
404 | if (collision_unsafe_fraction < 1.0) { |
405 | // Move shape transform to the point of impact, |
406 | // so we can collect contact info at that point. |
407 | gt.set_origin(gt.get_origin() + params.motion * (collision_unsafe_fraction + CMP_EPSILON)); |
408 | params.transform = gt; |
409 | } |
410 | } |
411 | // Regardless of whether the shape is stuck or it's moved along |
412 | // the motion vector, we'll only consider static collisions from now on. |
413 | params.motion = Vector3(); |
414 | |
415 | bool intersected = true; |
416 | while (intersected && result.size() < max_results) { |
417 | PhysicsDirectSpaceState3D::ShapeRestInfo info; |
418 | intersected = dss->rest_info(params, &info); |
419 | if (intersected) { |
420 | result.push_back(info); |
421 | params.exclude.insert(info.rid); |
422 | } |
423 | } |
424 | collided = !result.is_empty(); |
425 | } |
426 | |
427 | void ShapeCast3D::force_shapecast_update() { |
428 | _update_shapecast_state(); |
429 | } |
430 | |
431 | void ShapeCast3D::add_exception_rid(const RID &p_rid) { |
432 | exclude.insert(p_rid); |
433 | } |
434 | |
435 | void ShapeCast3D::add_exception(const CollisionObject3D *p_node) { |
436 | ERR_FAIL_NULL_MSG(p_node, "The passed Node must be an instance of CollisionObject3D." ); |
437 | add_exception_rid(p_node->get_rid()); |
438 | } |
439 | |
440 | void ShapeCast3D::remove_exception_rid(const RID &p_rid) { |
441 | exclude.erase(p_rid); |
442 | } |
443 | |
444 | void ShapeCast3D::remove_exception(const CollisionObject3D *p_node) { |
445 | ERR_FAIL_NULL_MSG(p_node, "The passed Node must be an instance of CollisionObject3D." ); |
446 | remove_exception_rid(p_node->get_rid()); |
447 | } |
448 | |
449 | void ShapeCast3D::clear_exceptions() { |
450 | exclude.clear(); |
451 | } |
452 | |
453 | void ShapeCast3D::set_collide_with_areas(bool p_clip) { |
454 | collide_with_areas = p_clip; |
455 | } |
456 | |
457 | bool ShapeCast3D::is_collide_with_areas_enabled() const { |
458 | return collide_with_areas; |
459 | } |
460 | |
461 | void ShapeCast3D::set_collide_with_bodies(bool p_clip) { |
462 | collide_with_bodies = p_clip; |
463 | } |
464 | |
465 | bool ShapeCast3D::is_collide_with_bodies_enabled() const { |
466 | return collide_with_bodies; |
467 | } |
468 | |
469 | Array ShapeCast3D::_get_collision_result() const { |
470 | Array ret; |
471 | |
472 | for (int i = 0; i < result.size(); ++i) { |
473 | const PhysicsDirectSpaceState3D::ShapeRestInfo &sri = result[i]; |
474 | |
475 | Dictionary col; |
476 | col["point" ] = sri.point; |
477 | col["normal" ] = sri.normal; |
478 | col["rid" ] = sri.rid; |
479 | col["collider" ] = ObjectDB::get_instance(sri.collider_id); |
480 | col["collider_id" ] = sri.collider_id; |
481 | col["shape" ] = sri.shape; |
482 | col["linear_velocity" ] = sri.linear_velocity; |
483 | |
484 | ret.push_back(col); |
485 | } |
486 | return ret; |
487 | } |
488 | |
489 | void ShapeCast3D::_update_debug_shape_vertices() { |
490 | debug_shape_vertices.clear(); |
491 | debug_line_vertices.clear(); |
492 | |
493 | if (!shape.is_null()) { |
494 | debug_shape_vertices.append_array(shape->get_debug_mesh_lines()); |
495 | for (int i = 0; i < debug_shape_vertices.size(); i++) { |
496 | debug_shape_vertices.set(i, debug_shape_vertices[i] + Vector3(target_position * get_closest_collision_safe_fraction())); |
497 | } |
498 | } |
499 | |
500 | if (target_position == Vector3()) { |
501 | return; |
502 | } |
503 | |
504 | debug_line_vertices.push_back(Vector3()); |
505 | debug_line_vertices.push_back(target_position); |
506 | } |
507 | |
508 | const Vector<Vector3> &ShapeCast3D::get_debug_shape_vertices() const { |
509 | return debug_shape_vertices; |
510 | } |
511 | |
512 | const Vector<Vector3> &ShapeCast3D::get_debug_line_vertices() const { |
513 | return debug_line_vertices; |
514 | } |
515 | |
516 | void ShapeCast3D::set_debug_shape_custom_color(const Color &p_color) { |
517 | debug_shape_custom_color = p_color; |
518 | if (debug_material.is_valid()) { |
519 | _update_debug_shape_material(); |
520 | } |
521 | } |
522 | |
523 | Ref<StandardMaterial3D> ShapeCast3D::get_debug_material() { |
524 | _update_debug_shape_material(); |
525 | return debug_material; |
526 | } |
527 | |
528 | const Color &ShapeCast3D::get_debug_shape_custom_color() const { |
529 | return debug_shape_custom_color; |
530 | } |
531 | |
532 | void ShapeCast3D::_create_debug_shape() { |
533 | _update_debug_shape_material(); |
534 | |
535 | Ref<ArrayMesh> mesh = memnew(ArrayMesh); |
536 | |
537 | MeshInstance3D *mi = memnew(MeshInstance3D); |
538 | mi->set_mesh(mesh); |
539 | |
540 | add_child(mi); |
541 | debug_shape = mi; |
542 | } |
543 | |
544 | void ShapeCast3D::_update_debug_shape_material(bool p_check_collision) { |
545 | if (!debug_material.is_valid()) { |
546 | Ref<StandardMaterial3D> material = memnew(StandardMaterial3D); |
547 | debug_material = material; |
548 | |
549 | material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); |
550 | // Use double-sided rendering so that the RayCast can be seen if the camera is inside. |
551 | material->set_cull_mode(BaseMaterial3D::CULL_DISABLED); |
552 | material->set_transparency(BaseMaterial3D::TRANSPARENCY_ALPHA); |
553 | } |
554 | |
555 | Color color = debug_shape_custom_color; |
556 | if (color == Color(0.0, 0.0, 0.0)) { |
557 | // Use the default debug shape color defined in the Project Settings. |
558 | color = get_tree()->get_debug_collisions_color(); |
559 | } |
560 | |
561 | if (p_check_collision && collided) { |
562 | if ((color.get_h() < 0.055 || color.get_h() > 0.945) && color.get_s() > 0.5 && color.get_v() > 0.5) { |
563 | // If base color is already quite reddish, highlight collision with green color |
564 | color = Color(0.0, 1.0, 0.0, color.a); |
565 | } else { |
566 | // Else, highlight collision with red color |
567 | color = Color(1.0, 0, 0, color.a); |
568 | } |
569 | } |
570 | |
571 | Ref<StandardMaterial3D> material = static_cast<Ref<StandardMaterial3D>>(debug_material); |
572 | material->set_albedo(color); |
573 | } |
574 | |
575 | void ShapeCast3D::_update_debug_shape() { |
576 | if (!enabled) { |
577 | return; |
578 | } |
579 | |
580 | if (!debug_shape) { |
581 | _create_debug_shape(); |
582 | } |
583 | |
584 | _update_debug_shape_vertices(); |
585 | |
586 | if (Engine::get_singleton()->is_editor_hint()) { |
587 | return; |
588 | } |
589 | |
590 | MeshInstance3D *mi = static_cast<MeshInstance3D *>(debug_shape); |
591 | Ref<ArrayMesh> mesh = mi->get_mesh(); |
592 | if (!mesh.is_valid()) { |
593 | return; |
594 | } |
595 | |
596 | mesh->clear_surfaces(); |
597 | |
598 | Array a; |
599 | a.resize(Mesh::ARRAY_MAX); |
600 | |
601 | uint32_t flags = 0; |
602 | int surface_count = 0; |
603 | |
604 | if (!debug_shape_vertices.is_empty()) { |
605 | a[Mesh::ARRAY_VERTEX] = debug_shape_vertices; |
606 | mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, a, Array(), Dictionary(), flags); |
607 | mesh->surface_set_material(surface_count, debug_material); |
608 | ++surface_count; |
609 | } |
610 | |
611 | if (!debug_line_vertices.is_empty()) { |
612 | a[Mesh::ARRAY_VERTEX] = debug_line_vertices; |
613 | mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, a, Array(), Dictionary(), flags); |
614 | mesh->surface_set_material(surface_count, debug_material); |
615 | ++surface_count; |
616 | } |
617 | } |
618 | |
619 | void ShapeCast3D::_clear_debug_shape() { |
620 | if (!debug_shape) { |
621 | return; |
622 | } |
623 | |
624 | MeshInstance3D *mi = static_cast<MeshInstance3D *>(debug_shape); |
625 | if (mi->is_inside_tree()) { |
626 | mi->queue_free(); |
627 | } else { |
628 | memdelete(mi); |
629 | } |
630 | |
631 | debug_shape = nullptr; |
632 | } |
633 | |