1 | /**************************************************************************/ |
2 | /* ray_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 "ray_cast_3d.h" |
32 | |
33 | #include "collision_object_3d.h" |
34 | #include "mesh_instance_3d.h" |
35 | |
36 | void RayCast3D::set_target_position(const Vector3 &p_point) { |
37 | target_position = p_point; |
38 | update_gizmos(); |
39 | |
40 | if (Engine::get_singleton()->is_editor_hint()) { |
41 | if (is_inside_tree()) { |
42 | _update_debug_shape_vertices(); |
43 | } |
44 | } else if (debug_shape) { |
45 | _update_debug_shape(); |
46 | } |
47 | } |
48 | |
49 | Vector3 RayCast3D::get_target_position() const { |
50 | return target_position; |
51 | } |
52 | |
53 | void RayCast3D::set_collision_mask(uint32_t p_mask) { |
54 | collision_mask = p_mask; |
55 | } |
56 | |
57 | uint32_t RayCast3D::get_collision_mask() const { |
58 | return collision_mask; |
59 | } |
60 | |
61 | void RayCast3D::set_collision_mask_value(int p_layer_number, bool p_value) { |
62 | ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive." ); |
63 | ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive." ); |
64 | uint32_t mask = get_collision_mask(); |
65 | if (p_value) { |
66 | mask |= 1 << (p_layer_number - 1); |
67 | } else { |
68 | mask &= ~(1 << (p_layer_number - 1)); |
69 | } |
70 | set_collision_mask(mask); |
71 | } |
72 | |
73 | bool RayCast3D::get_collision_mask_value(int p_layer_number) const { |
74 | ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive." ); |
75 | ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive." ); |
76 | return get_collision_mask() & (1 << (p_layer_number - 1)); |
77 | } |
78 | |
79 | bool RayCast3D::is_colliding() const { |
80 | return collided; |
81 | } |
82 | |
83 | Object *RayCast3D::get_collider() const { |
84 | if (against.is_null()) { |
85 | return nullptr; |
86 | } |
87 | |
88 | return ObjectDB::get_instance(against); |
89 | } |
90 | |
91 | RID RayCast3D::get_collider_rid() const { |
92 | return against_rid; |
93 | } |
94 | |
95 | int RayCast3D::get_collider_shape() const { |
96 | return against_shape; |
97 | } |
98 | |
99 | Vector3 RayCast3D::get_collision_point() const { |
100 | return collision_point; |
101 | } |
102 | |
103 | Vector3 RayCast3D::get_collision_normal() const { |
104 | return collision_normal; |
105 | } |
106 | |
107 | int RayCast3D::get_collision_face_index() const { |
108 | return collision_face_index; |
109 | } |
110 | |
111 | void RayCast3D::set_enabled(bool p_enabled) { |
112 | enabled = p_enabled; |
113 | update_gizmos(); |
114 | |
115 | if (is_inside_tree() && !Engine::get_singleton()->is_editor_hint()) { |
116 | set_physics_process_internal(p_enabled); |
117 | } |
118 | if (!p_enabled) { |
119 | collided = false; |
120 | } |
121 | |
122 | if (is_inside_tree() && get_tree()->is_debugging_collisions_hint()) { |
123 | if (p_enabled) { |
124 | _update_debug_shape(); |
125 | } else { |
126 | _clear_debug_shape(); |
127 | } |
128 | } |
129 | } |
130 | |
131 | bool RayCast3D::is_enabled() const { |
132 | return enabled; |
133 | } |
134 | |
135 | void RayCast3D::set_exclude_parent_body(bool p_exclude_parent_body) { |
136 | if (exclude_parent_body == p_exclude_parent_body) { |
137 | return; |
138 | } |
139 | |
140 | exclude_parent_body = p_exclude_parent_body; |
141 | |
142 | if (!is_inside_tree()) { |
143 | return; |
144 | } |
145 | |
146 | if (Object::cast_to<CollisionObject3D>(get_parent())) { |
147 | if (exclude_parent_body) { |
148 | exclude.insert(Object::cast_to<CollisionObject3D>(get_parent())->get_rid()); |
149 | } else { |
150 | exclude.erase(Object::cast_to<CollisionObject3D>(get_parent())->get_rid()); |
151 | } |
152 | } |
153 | } |
154 | |
155 | bool RayCast3D::get_exclude_parent_body() const { |
156 | return exclude_parent_body; |
157 | } |
158 | |
159 | void RayCast3D::_notification(int p_what) { |
160 | switch (p_what) { |
161 | case NOTIFICATION_ENTER_TREE: { |
162 | if (Engine::get_singleton()->is_editor_hint()) { |
163 | _update_debug_shape_vertices(); |
164 | } |
165 | if (enabled && !Engine::get_singleton()->is_editor_hint()) { |
166 | set_physics_process_internal(true); |
167 | } else { |
168 | set_physics_process_internal(false); |
169 | } |
170 | |
171 | if (get_tree()->is_debugging_collisions_hint()) { |
172 | _update_debug_shape(); |
173 | } |
174 | |
175 | if (Object::cast_to<CollisionObject3D>(get_parent())) { |
176 | if (exclude_parent_body) { |
177 | exclude.insert(Object::cast_to<CollisionObject3D>(get_parent())->get_rid()); |
178 | } else { |
179 | exclude.erase(Object::cast_to<CollisionObject3D>(get_parent())->get_rid()); |
180 | } |
181 | } |
182 | } break; |
183 | |
184 | case NOTIFICATION_EXIT_TREE: { |
185 | if (enabled) { |
186 | set_physics_process_internal(false); |
187 | } |
188 | |
189 | if (debug_shape) { |
190 | _clear_debug_shape(); |
191 | } |
192 | } break; |
193 | |
194 | case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { |
195 | if (!enabled) { |
196 | break; |
197 | } |
198 | |
199 | bool prev_collision_state = collided; |
200 | _update_raycast_state(); |
201 | if (prev_collision_state != collided && get_tree()->is_debugging_collisions_hint()) { |
202 | _update_debug_shape_material(true); |
203 | } |
204 | } break; |
205 | } |
206 | } |
207 | |
208 | void RayCast3D::_update_raycast_state() { |
209 | Ref<World3D> w3d = get_world_3d(); |
210 | ERR_FAIL_COND(w3d.is_null()); |
211 | |
212 | PhysicsDirectSpaceState3D *dss = PhysicsServer3D::get_singleton()->space_get_direct_state(w3d->get_space()); |
213 | ERR_FAIL_NULL(dss); |
214 | |
215 | Transform3D gt = get_global_transform(); |
216 | |
217 | Vector3 to = target_position; |
218 | if (to == Vector3()) { |
219 | to = Vector3(0, 0.01, 0); |
220 | } |
221 | |
222 | PhysicsDirectSpaceState3D::RayParameters ray_params; |
223 | ray_params.from = gt.get_origin(); |
224 | ray_params.to = gt.xform(to); |
225 | ray_params.exclude = exclude; |
226 | ray_params.collision_mask = collision_mask; |
227 | ray_params.collide_with_bodies = collide_with_bodies; |
228 | ray_params.collide_with_areas = collide_with_areas; |
229 | ray_params.hit_from_inside = hit_from_inside; |
230 | ray_params.hit_back_faces = hit_back_faces; |
231 | |
232 | PhysicsDirectSpaceState3D::RayResult rr; |
233 | if (dss->intersect_ray(ray_params, rr)) { |
234 | collided = true; |
235 | against = rr.collider_id; |
236 | against_rid = rr.rid; |
237 | collision_point = rr.position; |
238 | collision_normal = rr.normal; |
239 | collision_face_index = rr.face_index; |
240 | against_shape = rr.shape; |
241 | } else { |
242 | collided = false; |
243 | against = ObjectID(); |
244 | against_rid = RID(); |
245 | against_shape = 0; |
246 | } |
247 | } |
248 | |
249 | void RayCast3D::force_raycast_update() { |
250 | _update_raycast_state(); |
251 | } |
252 | |
253 | void RayCast3D::add_exception_rid(const RID &p_rid) { |
254 | exclude.insert(p_rid); |
255 | } |
256 | |
257 | void RayCast3D::add_exception(const CollisionObject3D *p_node) { |
258 | ERR_FAIL_NULL_MSG(p_node, "The passed Node must be an instance of CollisionObject3D." ); |
259 | add_exception_rid(p_node->get_rid()); |
260 | } |
261 | |
262 | void RayCast3D::remove_exception_rid(const RID &p_rid) { |
263 | exclude.erase(p_rid); |
264 | } |
265 | |
266 | void RayCast3D::remove_exception(const CollisionObject3D *p_node) { |
267 | ERR_FAIL_NULL_MSG(p_node, "The passed Node must be an instance of CollisionObject3D." ); |
268 | remove_exception_rid(p_node->get_rid()); |
269 | } |
270 | |
271 | void RayCast3D::clear_exceptions() { |
272 | exclude.clear(); |
273 | |
274 | if (exclude_parent_body && is_inside_tree()) { |
275 | CollisionObject3D *parent = Object::cast_to<CollisionObject3D>(get_parent()); |
276 | if (parent) { |
277 | exclude.insert(parent->get_rid()); |
278 | } |
279 | } |
280 | } |
281 | |
282 | void RayCast3D::set_collide_with_areas(bool p_enabled) { |
283 | collide_with_areas = p_enabled; |
284 | } |
285 | |
286 | bool RayCast3D::is_collide_with_areas_enabled() const { |
287 | return collide_with_areas; |
288 | } |
289 | |
290 | void RayCast3D::set_collide_with_bodies(bool p_enabled) { |
291 | collide_with_bodies = p_enabled; |
292 | } |
293 | |
294 | bool RayCast3D::is_collide_with_bodies_enabled() const { |
295 | return collide_with_bodies; |
296 | } |
297 | |
298 | void RayCast3D::set_hit_from_inside(bool p_enabled) { |
299 | hit_from_inside = p_enabled; |
300 | } |
301 | |
302 | bool RayCast3D::is_hit_from_inside_enabled() const { |
303 | return hit_from_inside; |
304 | } |
305 | |
306 | void RayCast3D::set_hit_back_faces(bool p_enabled) { |
307 | hit_back_faces = p_enabled; |
308 | } |
309 | |
310 | bool RayCast3D::is_hit_back_faces_enabled() const { |
311 | return hit_back_faces; |
312 | } |
313 | |
314 | void RayCast3D::_bind_methods() { |
315 | ClassDB::bind_method(D_METHOD("set_enabled" , "enabled" ), &RayCast3D::set_enabled); |
316 | ClassDB::bind_method(D_METHOD("is_enabled" ), &RayCast3D::is_enabled); |
317 | |
318 | ClassDB::bind_method(D_METHOD("set_target_position" , "local_point" ), &RayCast3D::set_target_position); |
319 | ClassDB::bind_method(D_METHOD("get_target_position" ), &RayCast3D::get_target_position); |
320 | |
321 | ClassDB::bind_method(D_METHOD("is_colliding" ), &RayCast3D::is_colliding); |
322 | ClassDB::bind_method(D_METHOD("force_raycast_update" ), &RayCast3D::force_raycast_update); |
323 | |
324 | ClassDB::bind_method(D_METHOD("get_collider" ), &RayCast3D::get_collider); |
325 | ClassDB::bind_method(D_METHOD("get_collider_rid" ), &RayCast3D::get_collider_rid); |
326 | ClassDB::bind_method(D_METHOD("get_collider_shape" ), &RayCast3D::get_collider_shape); |
327 | ClassDB::bind_method(D_METHOD("get_collision_point" ), &RayCast3D::get_collision_point); |
328 | ClassDB::bind_method(D_METHOD("get_collision_normal" ), &RayCast3D::get_collision_normal); |
329 | ClassDB::bind_method(D_METHOD("get_collision_face_index" ), &RayCast3D::get_collision_face_index); |
330 | |
331 | ClassDB::bind_method(D_METHOD("add_exception_rid" , "rid" ), &RayCast3D::add_exception_rid); |
332 | ClassDB::bind_method(D_METHOD("add_exception" , "node" ), &RayCast3D::add_exception); |
333 | |
334 | ClassDB::bind_method(D_METHOD("remove_exception_rid" , "rid" ), &RayCast3D::remove_exception_rid); |
335 | ClassDB::bind_method(D_METHOD("remove_exception" , "node" ), &RayCast3D::remove_exception); |
336 | |
337 | ClassDB::bind_method(D_METHOD("clear_exceptions" ), &RayCast3D::clear_exceptions); |
338 | |
339 | ClassDB::bind_method(D_METHOD("set_collision_mask" , "mask" ), &RayCast3D::set_collision_mask); |
340 | ClassDB::bind_method(D_METHOD("get_collision_mask" ), &RayCast3D::get_collision_mask); |
341 | |
342 | ClassDB::bind_method(D_METHOD("set_collision_mask_value" , "layer_number" , "value" ), &RayCast3D::set_collision_mask_value); |
343 | ClassDB::bind_method(D_METHOD("get_collision_mask_value" , "layer_number" ), &RayCast3D::get_collision_mask_value); |
344 | |
345 | ClassDB::bind_method(D_METHOD("set_exclude_parent_body" , "mask" ), &RayCast3D::set_exclude_parent_body); |
346 | ClassDB::bind_method(D_METHOD("get_exclude_parent_body" ), &RayCast3D::get_exclude_parent_body); |
347 | |
348 | ClassDB::bind_method(D_METHOD("set_collide_with_areas" , "enable" ), &RayCast3D::set_collide_with_areas); |
349 | ClassDB::bind_method(D_METHOD("is_collide_with_areas_enabled" ), &RayCast3D::is_collide_with_areas_enabled); |
350 | |
351 | ClassDB::bind_method(D_METHOD("set_collide_with_bodies" , "enable" ), &RayCast3D::set_collide_with_bodies); |
352 | ClassDB::bind_method(D_METHOD("is_collide_with_bodies_enabled" ), &RayCast3D::is_collide_with_bodies_enabled); |
353 | |
354 | ClassDB::bind_method(D_METHOD("set_hit_from_inside" , "enable" ), &RayCast3D::set_hit_from_inside); |
355 | ClassDB::bind_method(D_METHOD("is_hit_from_inside_enabled" ), &RayCast3D::is_hit_from_inside_enabled); |
356 | |
357 | ClassDB::bind_method(D_METHOD("set_hit_back_faces" , "enable" ), &RayCast3D::set_hit_back_faces); |
358 | ClassDB::bind_method(D_METHOD("is_hit_back_faces_enabled" ), &RayCast3D::is_hit_back_faces_enabled); |
359 | |
360 | ClassDB::bind_method(D_METHOD("set_debug_shape_custom_color" , "debug_shape_custom_color" ), &RayCast3D::set_debug_shape_custom_color); |
361 | ClassDB::bind_method(D_METHOD("get_debug_shape_custom_color" ), &RayCast3D::get_debug_shape_custom_color); |
362 | |
363 | ClassDB::bind_method(D_METHOD("set_debug_shape_thickness" , "debug_shape_thickness" ), &RayCast3D::set_debug_shape_thickness); |
364 | ClassDB::bind_method(D_METHOD("get_debug_shape_thickness" ), &RayCast3D::get_debug_shape_thickness); |
365 | |
366 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled" ), "set_enabled" , "is_enabled" ); |
367 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "exclude_parent" ), "set_exclude_parent_body" , "get_exclude_parent_body" ); |
368 | ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "target_position" , PROPERTY_HINT_NONE, "suffix:m" ), "set_target_position" , "get_target_position" ); |
369 | ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask" , PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask" , "get_collision_mask" ); |
370 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hit_from_inside" ), "set_hit_from_inside" , "is_hit_from_inside_enabled" ); |
371 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hit_back_faces" ), "set_hit_back_faces" , "is_hit_back_faces_enabled" ); |
372 | |
373 | ADD_GROUP("Collide With" , "collide_with" ); |
374 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with_areas" , PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collide_with_areas" , "is_collide_with_areas_enabled" ); |
375 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with_bodies" , PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collide_with_bodies" , "is_collide_with_bodies_enabled" ); |
376 | |
377 | ADD_GROUP("Debug Shape" , "debug_shape" ); |
378 | ADD_PROPERTY(PropertyInfo(Variant::COLOR, "debug_shape_custom_color" ), "set_debug_shape_custom_color" , "get_debug_shape_custom_color" ); |
379 | ADD_PROPERTY(PropertyInfo(Variant::INT, "debug_shape_thickness" , PROPERTY_HINT_RANGE, "1,5" ), "set_debug_shape_thickness" , "get_debug_shape_thickness" ); |
380 | } |
381 | |
382 | int RayCast3D::get_debug_shape_thickness() const { |
383 | return debug_shape_thickness; |
384 | } |
385 | |
386 | void RayCast3D::_update_debug_shape_vertices() { |
387 | debug_shape_vertices.clear(); |
388 | debug_line_vertices.clear(); |
389 | |
390 | if (target_position == Vector3()) { |
391 | return; |
392 | } |
393 | |
394 | debug_line_vertices.push_back(Vector3()); |
395 | debug_line_vertices.push_back(target_position); |
396 | |
397 | if (debug_shape_thickness > 1) { |
398 | float scale_factor = 100.0; |
399 | Vector3 dir = Vector3(target_position).normalized(); |
400 | // Draw truncated pyramid |
401 | Vector3 normal = (fabs(dir.x) + fabs(dir.y) > CMP_EPSILON) ? Vector3(-dir.y, dir.x, 0).normalized() : Vector3(0, -dir.z, dir.y).normalized(); |
402 | normal *= debug_shape_thickness / scale_factor; |
403 | int vertices_strip_order[14] = { 4, 5, 0, 1, 2, 5, 6, 4, 7, 0, 3, 2, 7, 6 }; |
404 | for (int v = 0; v < 14; v++) { |
405 | Vector3 vertex = vertices_strip_order[v] < 4 ? normal : normal / 3.0 + target_position; |
406 | debug_shape_vertices.push_back(vertex.rotated(dir, Math_PI * (0.5 * (vertices_strip_order[v] % 4) + 0.25))); |
407 | } |
408 | } |
409 | } |
410 | |
411 | void RayCast3D::set_debug_shape_thickness(const int p_debug_shape_thickness) { |
412 | debug_shape_thickness = p_debug_shape_thickness; |
413 | update_gizmos(); |
414 | |
415 | if (Engine::get_singleton()->is_editor_hint()) { |
416 | if (is_inside_tree()) { |
417 | _update_debug_shape_vertices(); |
418 | } |
419 | } else if (debug_shape) { |
420 | _update_debug_shape(); |
421 | } |
422 | } |
423 | |
424 | const Vector<Vector3> &RayCast3D::get_debug_shape_vertices() const { |
425 | return debug_shape_vertices; |
426 | } |
427 | |
428 | const Vector<Vector3> &RayCast3D::get_debug_line_vertices() const { |
429 | return debug_line_vertices; |
430 | } |
431 | |
432 | void RayCast3D::set_debug_shape_custom_color(const Color &p_color) { |
433 | debug_shape_custom_color = p_color; |
434 | if (debug_material.is_valid()) { |
435 | _update_debug_shape_material(); |
436 | } |
437 | } |
438 | |
439 | Ref<StandardMaterial3D> RayCast3D::get_debug_material() { |
440 | _update_debug_shape_material(); |
441 | return debug_material; |
442 | } |
443 | |
444 | const Color &RayCast3D::get_debug_shape_custom_color() const { |
445 | return debug_shape_custom_color; |
446 | } |
447 | |
448 | void RayCast3D::_create_debug_shape() { |
449 | _update_debug_shape_material(); |
450 | |
451 | Ref<ArrayMesh> mesh = memnew(ArrayMesh); |
452 | |
453 | MeshInstance3D *mi = memnew(MeshInstance3D); |
454 | mi->set_mesh(mesh); |
455 | |
456 | add_child(mi); |
457 | debug_shape = mi; |
458 | } |
459 | |
460 | void RayCast3D::_update_debug_shape_material(bool p_check_collision) { |
461 | if (!debug_material.is_valid()) { |
462 | Ref<StandardMaterial3D> material = memnew(StandardMaterial3D); |
463 | debug_material = material; |
464 | |
465 | material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); |
466 | // Use double-sided rendering so that the RayCast can be seen if the camera is inside. |
467 | material->set_cull_mode(BaseMaterial3D::CULL_DISABLED); |
468 | material->set_transparency(BaseMaterial3D::TRANSPARENCY_ALPHA); |
469 | } |
470 | |
471 | Color color = debug_shape_custom_color; |
472 | if (color == Color(0.0, 0.0, 0.0)) { |
473 | // Use the default debug shape color defined in the Project Settings. |
474 | color = get_tree()->get_debug_collisions_color(); |
475 | } |
476 | |
477 | if (p_check_collision && collided) { |
478 | if ((color.get_h() < 0.055 || color.get_h() > 0.945) && color.get_s() > 0.5 && color.get_v() > 0.5) { |
479 | // If base color is already quite reddish, highlight collision with green color |
480 | color = Color(0.0, 1.0, 0.0, color.a); |
481 | } else { |
482 | // Else, highlight collision with red color |
483 | color = Color(1.0, 0, 0, color.a); |
484 | } |
485 | } |
486 | |
487 | Ref<StandardMaterial3D> material = static_cast<Ref<StandardMaterial3D>>(debug_material); |
488 | material->set_albedo(color); |
489 | } |
490 | |
491 | void RayCast3D::_update_debug_shape() { |
492 | if (!enabled) { |
493 | return; |
494 | } |
495 | |
496 | if (!debug_shape) { |
497 | _create_debug_shape(); |
498 | } |
499 | |
500 | MeshInstance3D *mi = static_cast<MeshInstance3D *>(debug_shape); |
501 | Ref<ArrayMesh> mesh = mi->get_mesh(); |
502 | if (!mesh.is_valid()) { |
503 | return; |
504 | } |
505 | |
506 | _update_debug_shape_vertices(); |
507 | |
508 | mesh->clear_surfaces(); |
509 | |
510 | Array a; |
511 | a.resize(Mesh::ARRAY_MAX); |
512 | |
513 | uint32_t flags = 0; |
514 | int surface_count = 0; |
515 | |
516 | if (!debug_line_vertices.is_empty()) { |
517 | a[Mesh::ARRAY_VERTEX] = debug_line_vertices; |
518 | mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, a, Array(), Dictionary(), flags); |
519 | mesh->surface_set_material(surface_count, debug_material); |
520 | ++surface_count; |
521 | } |
522 | |
523 | if (!debug_shape_vertices.is_empty()) { |
524 | a[Mesh::ARRAY_VERTEX] = debug_shape_vertices; |
525 | mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLE_STRIP, a, Array(), Dictionary(), flags); |
526 | mesh->surface_set_material(surface_count, debug_material); |
527 | ++surface_count; |
528 | } |
529 | } |
530 | |
531 | void RayCast3D::_clear_debug_shape() { |
532 | if (!debug_shape) { |
533 | return; |
534 | } |
535 | |
536 | MeshInstance3D *mi = static_cast<MeshInstance3D *>(debug_shape); |
537 | if (mi->is_inside_tree()) { |
538 | mi->queue_free(); |
539 | } else { |
540 | memdelete(mi); |
541 | } |
542 | |
543 | debug_shape = nullptr; |
544 | } |
545 | |
546 | RayCast3D::RayCast3D() { |
547 | } |
548 | |