1/**************************************************************************/
2/* collision_shape_3d_gizmo_plugin.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 "collision_shape_3d_gizmo_plugin.h"
32
33#include "core/math/convex_hull.h"
34#include "core/math/geometry_3d.h"
35#include "editor/editor_settings.h"
36#include "editor/editor_undo_redo_manager.h"
37#include "editor/plugins/gizmos/gizmo_3d_helper.h"
38#include "editor/plugins/node_3d_editor_plugin.h"
39#include "scene/3d/collision_shape_3d.h"
40#include "scene/resources/box_shape_3d.h"
41#include "scene/resources/capsule_shape_3d.h"
42#include "scene/resources/concave_polygon_shape_3d.h"
43#include "scene/resources/convex_polygon_shape_3d.h"
44#include "scene/resources/cylinder_shape_3d.h"
45#include "scene/resources/height_map_shape_3d.h"
46#include "scene/resources/separation_ray_shape_3d.h"
47#include "scene/resources/sphere_shape_3d.h"
48#include "scene/resources/world_boundary_shape_3d.h"
49
50CollisionShape3DGizmoPlugin::CollisionShape3DGizmoPlugin() {
51 helper.instantiate();
52 const Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/shape");
53 create_material("shape_material", gizmo_color);
54 const float gizmo_value = gizmo_color.get_v();
55 const Color gizmo_color_disabled = Color(gizmo_value, gizmo_value, gizmo_value, 0.65);
56 create_material("shape_material_disabled", gizmo_color_disabled);
57 create_handle_material("handles");
58}
59
60CollisionShape3DGizmoPlugin::~CollisionShape3DGizmoPlugin() {
61}
62
63bool CollisionShape3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
64 return Object::cast_to<CollisionShape3D>(p_spatial) != nullptr;
65}
66
67String CollisionShape3DGizmoPlugin::get_gizmo_name() const {
68 return "CollisionShape3D";
69}
70
71int CollisionShape3DGizmoPlugin::get_priority() const {
72 return -1;
73}
74
75String CollisionShape3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
76 const CollisionShape3D *cs = Object::cast_to<CollisionShape3D>(p_gizmo->get_node_3d());
77
78 Ref<Shape3D> s = cs->get_shape();
79 if (s.is_null()) {
80 return "";
81 }
82
83 if (Object::cast_to<SphereShape3D>(*s)) {
84 return "Radius";
85 }
86
87 if (Object::cast_to<BoxShape3D>(*s)) {
88 return helper->box_get_handle_name(p_id);
89 }
90
91 if (Object::cast_to<CapsuleShape3D>(*s)) {
92 return p_id == 0 ? "Radius" : "Height";
93 }
94
95 if (Object::cast_to<CylinderShape3D>(*s)) {
96 return p_id == 0 ? "Radius" : "Height";
97 }
98
99 if (Object::cast_to<SeparationRayShape3D>(*s)) {
100 return "Length";
101 }
102
103 return "";
104}
105
106Variant CollisionShape3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
107 CollisionShape3D *cs = Object::cast_to<CollisionShape3D>(p_gizmo->get_node_3d());
108
109 Ref<Shape3D> s = cs->get_shape();
110 if (s.is_null()) {
111 return Variant();
112 }
113
114 if (Object::cast_to<SphereShape3D>(*s)) {
115 Ref<SphereShape3D> ss = s;
116 return ss->get_radius();
117 }
118
119 if (Object::cast_to<BoxShape3D>(*s)) {
120 Ref<BoxShape3D> bs = s;
121 return bs->get_size();
122 }
123
124 if (Object::cast_to<CapsuleShape3D>(*s)) {
125 Ref<CapsuleShape3D> cs2 = s;
126 return Vector2(cs2->get_radius(), cs2->get_height());
127 }
128
129 if (Object::cast_to<CylinderShape3D>(*s)) {
130 Ref<CylinderShape3D> cs2 = s;
131 return p_id == 0 ? cs2->get_radius() : cs2->get_height();
132 }
133
134 if (Object::cast_to<SeparationRayShape3D>(*s)) {
135 Ref<SeparationRayShape3D> cs2 = s;
136 return cs2->get_length();
137 }
138
139 return Variant();
140}
141
142void CollisionShape3DGizmoPlugin::begin_handle_action(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) {
143 helper->initialize_handle_action(get_handle_value(p_gizmo, p_id, p_secondary), p_gizmo->get_node_3d()->get_global_transform());
144}
145
146void CollisionShape3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) {
147 CollisionShape3D *cs = Object::cast_to<CollisionShape3D>(p_gizmo->get_node_3d());
148
149 Ref<Shape3D> s = cs->get_shape();
150 if (s.is_null()) {
151 return;
152 }
153
154 Vector3 sg[2];
155 helper->get_segment(p_camera, p_point, sg);
156
157 if (Object::cast_to<SphereShape3D>(*s)) {
158 Ref<SphereShape3D> ss = s;
159 Vector3 ra, rb;
160 Geometry3D::get_closest_points_between_segments(Vector3(), Vector3(4096, 0, 0), sg[0], sg[1], ra, rb);
161 float d = ra.x;
162 if (Node3DEditor::get_singleton()->is_snap_enabled()) {
163 d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());
164 }
165
166 if (d < 0.001) {
167 d = 0.001;
168 }
169
170 ss->set_radius(d);
171 }
172
173 if (Object::cast_to<SeparationRayShape3D>(*s)) {
174 Ref<SeparationRayShape3D> rs = s;
175 Vector3 ra, rb;
176 Geometry3D::get_closest_points_between_segments(Vector3(), Vector3(0, 0, 4096), sg[0], sg[1], ra, rb);
177 float d = ra.z;
178 if (Node3DEditor::get_singleton()->is_snap_enabled()) {
179 d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());
180 }
181
182 if (d < 0.001) {
183 d = 0.001;
184 }
185
186 rs->set_length(d);
187 }
188
189 if (Object::cast_to<BoxShape3D>(*s)) {
190 Ref<BoxShape3D> bs = s;
191 Vector3 size = bs->get_size();
192 Vector3 position;
193 helper->box_set_handle(sg, p_id, size, position);
194 bs->set_size(size);
195 cs->set_global_position(position);
196 }
197
198 if (Object::cast_to<CapsuleShape3D>(*s)) {
199 Vector3 axis;
200 axis[p_id == 0 ? 0 : 1] = 1.0;
201 Ref<CapsuleShape3D> cs2 = s;
202 Vector3 ra, rb;
203 Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb);
204 float d = axis.dot(ra);
205
206 if (Node3DEditor::get_singleton()->is_snap_enabled()) {
207 d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());
208 }
209
210 if (d < 0.001) {
211 d = 0.001;
212 }
213
214 if (p_id == 0) {
215 cs2->set_radius(d);
216 } else if (p_id == 1) {
217 cs2->set_height(d * 2.0);
218 }
219 }
220
221 if (Object::cast_to<CylinderShape3D>(*s)) {
222 Vector3 axis;
223 axis[p_id == 0 ? 0 : 1] = 1.0;
224 Ref<CylinderShape3D> cs2 = s;
225 Vector3 ra, rb;
226 Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb);
227 float d = axis.dot(ra);
228 if (Node3DEditor::get_singleton()->is_snap_enabled()) {
229 d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());
230 }
231
232 if (d < 0.001) {
233 d = 0.001;
234 }
235
236 if (p_id == 0) {
237 cs2->set_radius(d);
238 } else if (p_id == 1) {
239 cs2->set_height(d * 2.0);
240 }
241 }
242}
243
244void CollisionShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel) {
245 CollisionShape3D *cs = Object::cast_to<CollisionShape3D>(p_gizmo->get_node_3d());
246
247 Ref<Shape3D> s = cs->get_shape();
248 if (s.is_null()) {
249 return;
250 }
251
252 if (Object::cast_to<SphereShape3D>(*s)) {
253 Ref<SphereShape3D> ss = s;
254 if (p_cancel) {
255 ss->set_radius(p_restore);
256 return;
257 }
258
259 EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
260 ur->create_action(TTR("Change Sphere Shape Radius"));
261 ur->add_do_method(ss.ptr(), "set_radius", ss->get_radius());
262 ur->add_undo_method(ss.ptr(), "set_radius", p_restore);
263 ur->commit_action();
264 }
265
266 if (Object::cast_to<BoxShape3D>(*s)) {
267 helper->box_commit_handle(TTR("Change Box Shape Size"), p_cancel, cs, s.ptr());
268 }
269
270 if (Object::cast_to<CapsuleShape3D>(*s)) {
271 Ref<CapsuleShape3D> ss = s;
272 Vector2 values = p_restore;
273
274 if (p_cancel) {
275 ss->set_radius(values[0]);
276 ss->set_height(values[1]);
277 return;
278 }
279
280 EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
281 if (p_id == 0) {
282 ur->create_action(TTR("Change Capsule Shape Radius"));
283 ur->add_do_method(ss.ptr(), "set_radius", ss->get_radius());
284 } else {
285 ur->create_action(TTR("Change Capsule Shape Height"));
286 ur->add_do_method(ss.ptr(), "set_height", ss->get_height());
287 }
288 ur->add_undo_method(ss.ptr(), "set_radius", values[0]);
289 ur->add_undo_method(ss.ptr(), "set_height", values[1]);
290
291 ur->commit_action();
292 }
293
294 if (Object::cast_to<CylinderShape3D>(*s)) {
295 Ref<CylinderShape3D> ss = s;
296 if (p_cancel) {
297 if (p_id == 0) {
298 ss->set_radius(p_restore);
299 } else {
300 ss->set_height(p_restore);
301 }
302 return;
303 }
304
305 EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
306 if (p_id == 0) {
307 ur->create_action(TTR("Change Cylinder Shape Radius"));
308 ur->add_do_method(ss.ptr(), "set_radius", ss->get_radius());
309 ur->add_undo_method(ss.ptr(), "set_radius", p_restore);
310 } else {
311 ur->create_action(
312 ///
313
314 ////////
315 TTR("Change Cylinder Shape Height"));
316 ur->add_do_method(ss.ptr(), "set_height", ss->get_height());
317 ur->add_undo_method(ss.ptr(), "set_height", p_restore);
318 }
319
320 ur->commit_action();
321 }
322
323 if (Object::cast_to<SeparationRayShape3D>(*s)) {
324 Ref<SeparationRayShape3D> ss = s;
325 if (p_cancel) {
326 ss->set_length(p_restore);
327 return;
328 }
329
330 EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
331 ur->create_action(TTR("Change Separation Ray Shape Length"));
332 ur->add_do_method(ss.ptr(), "set_length", ss->get_length());
333 ur->add_undo_method(ss.ptr(), "set_length", p_restore);
334 ur->commit_action();
335 }
336}
337
338void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
339 CollisionShape3D *cs = Object::cast_to<CollisionShape3D>(p_gizmo->get_node_3d());
340
341 p_gizmo->clear();
342
343 Ref<Shape3D> s = cs->get_shape();
344 if (s.is_null()) {
345 return;
346 }
347
348 const Ref<Material> material =
349 get_material(!cs->is_disabled() ? "shape_material" : "shape_material_disabled", p_gizmo);
350 Ref<Material> handles_material = get_material("handles");
351
352 if (Object::cast_to<SphereShape3D>(*s)) {
353 Ref<SphereShape3D> sp = s;
354 float r = sp->get_radius();
355
356 Vector<Vector3> points;
357
358 for (int i = 0; i <= 360; i++) {
359 float ra = Math::deg_to_rad((float)i);
360 float rb = Math::deg_to_rad((float)i + 1);
361 Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r;
362 Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r;
363
364 points.push_back(Vector3(a.x, 0, a.y));
365 points.push_back(Vector3(b.x, 0, b.y));
366 points.push_back(Vector3(0, a.x, a.y));
367 points.push_back(Vector3(0, b.x, b.y));
368 points.push_back(Vector3(a.x, a.y, 0));
369 points.push_back(Vector3(b.x, b.y, 0));
370 }
371
372 Vector<Vector3> collision_segments;
373
374 for (int i = 0; i < 64; i++) {
375 float ra = i * (Math_TAU / 64.0);
376 float rb = (i + 1) * (Math_TAU / 64.0);
377 Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r;
378 Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r;
379
380 collision_segments.push_back(Vector3(a.x, 0, a.y));
381 collision_segments.push_back(Vector3(b.x, 0, b.y));
382 collision_segments.push_back(Vector3(0, a.x, a.y));
383 collision_segments.push_back(Vector3(0, b.x, b.y));
384 collision_segments.push_back(Vector3(a.x, a.y, 0));
385 collision_segments.push_back(Vector3(b.x, b.y, 0));
386 }
387
388 p_gizmo->add_lines(points, material);
389 p_gizmo->add_collision_segments(collision_segments);
390 Vector<Vector3> handles;
391 handles.push_back(Vector3(r, 0, 0));
392 p_gizmo->add_handles(handles, handles_material);
393 }
394
395 if (Object::cast_to<BoxShape3D>(*s)) {
396 Ref<BoxShape3D> bs = s;
397 Vector<Vector3> lines;
398 AABB aabb;
399 aabb.position = -bs->get_size() / 2;
400 aabb.size = bs->get_size();
401
402 for (int i = 0; i < 12; i++) {
403 Vector3 a, b;
404 aabb.get_edge(i, a, b);
405 lines.push_back(a);
406 lines.push_back(b);
407 }
408
409 const Vector<Vector3> handles = helper->box_get_handles(bs->get_size());
410
411 p_gizmo->add_lines(lines, material);
412 p_gizmo->add_collision_segments(lines);
413 p_gizmo->add_handles(handles, handles_material);
414 }
415
416 if (Object::cast_to<CapsuleShape3D>(*s)) {
417 Ref<CapsuleShape3D> cs2 = s;
418 float radius = cs2->get_radius();
419 float height = cs2->get_height();
420
421 Vector<Vector3> points;
422
423 Vector3 d(0, height * 0.5 - radius, 0);
424 for (int i = 0; i < 360; i++) {
425 float ra = Math::deg_to_rad((float)i);
426 float rb = Math::deg_to_rad((float)i + 1);
427 Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius;
428 Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius;
429
430 points.push_back(Vector3(a.x, 0, a.y) + d);
431 points.push_back(Vector3(b.x, 0, b.y) + d);
432
433 points.push_back(Vector3(a.x, 0, a.y) - d);
434 points.push_back(Vector3(b.x, 0, b.y) - d);
435
436 if (i % 90 == 0) {
437 points.push_back(Vector3(a.x, 0, a.y) + d);
438 points.push_back(Vector3(a.x, 0, a.y) - d);
439 }
440
441 Vector3 dud = i < 180 ? d : -d;
442
443 points.push_back(Vector3(0, a.x, a.y) + dud);
444 points.push_back(Vector3(0, b.x, b.y) + dud);
445 points.push_back(Vector3(a.y, a.x, 0) + dud);
446 points.push_back(Vector3(b.y, b.x, 0) + dud);
447 }
448
449 p_gizmo->add_lines(points, material);
450
451 Vector<Vector3> collision_segments;
452
453 for (int i = 0; i < 64; i++) {
454 float ra = i * (Math_TAU / 64.0);
455 float rb = (i + 1) * (Math_TAU / 64.0);
456 Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius;
457 Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius;
458
459 collision_segments.push_back(Vector3(a.x, 0, a.y) + d);
460 collision_segments.push_back(Vector3(b.x, 0, b.y) + d);
461
462 collision_segments.push_back(Vector3(a.x, 0, a.y) - d);
463 collision_segments.push_back(Vector3(b.x, 0, b.y) - d);
464
465 if (i % 16 == 0) {
466 collision_segments.push_back(Vector3(a.x, 0, a.y) + d);
467 collision_segments.push_back(Vector3(a.x, 0, a.y) - d);
468 }
469
470 Vector3 dud = i < 32 ? d : -d;
471
472 collision_segments.push_back(Vector3(0, a.x, a.y) + dud);
473 collision_segments.push_back(Vector3(0, b.x, b.y) + dud);
474 collision_segments.push_back(Vector3(a.y, a.x, 0) + dud);
475 collision_segments.push_back(Vector3(b.y, b.x, 0) + dud);
476 }
477
478 p_gizmo->add_collision_segments(collision_segments);
479
480 Vector<Vector3> handles = {
481 Vector3(cs2->get_radius(), 0, 0),
482 Vector3(0, cs2->get_height() * 0.5, 0)
483 };
484 p_gizmo->add_handles(handles, handles_material);
485 }
486
487 if (Object::cast_to<CylinderShape3D>(*s)) {
488 Ref<CylinderShape3D> cs2 = s;
489 float radius = cs2->get_radius();
490 float height = cs2->get_height();
491
492 Vector<Vector3> points;
493
494 Vector3 d(0, height * 0.5, 0);
495 for (int i = 0; i < 360; i++) {
496 float ra = Math::deg_to_rad((float)i);
497 float rb = Math::deg_to_rad((float)i + 1);
498 Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius;
499 Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius;
500
501 points.push_back(Vector3(a.x, 0, a.y) + d);
502 points.push_back(Vector3(b.x, 0, b.y) + d);
503
504 points.push_back(Vector3(a.x, 0, a.y) - d);
505 points.push_back(Vector3(b.x, 0, b.y) - d);
506
507 if (i % 90 == 0) {
508 points.push_back(Vector3(a.x, 0, a.y) + d);
509 points.push_back(Vector3(a.x, 0, a.y) - d);
510 }
511 }
512
513 p_gizmo->add_lines(points, material);
514
515 Vector<Vector3> collision_segments;
516
517 for (int i = 0; i < 64; i++) {
518 float ra = i * (Math_TAU / 64.0);
519 float rb = (i + 1) * (Math_TAU / 64.0);
520 Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius;
521 Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius;
522
523 collision_segments.push_back(Vector3(a.x, 0, a.y) + d);
524 collision_segments.push_back(Vector3(b.x, 0, b.y) + d);
525
526 collision_segments.push_back(Vector3(a.x, 0, a.y) - d);
527 collision_segments.push_back(Vector3(b.x, 0, b.y) - d);
528
529 if (i % 16 == 0) {
530 collision_segments.push_back(Vector3(a.x, 0, a.y) + d);
531 collision_segments.push_back(Vector3(a.x, 0, a.y) - d);
532 }
533 }
534
535 p_gizmo->add_collision_segments(collision_segments);
536
537 Vector<Vector3> handles = {
538 Vector3(cs2->get_radius(), 0, 0),
539 Vector3(0, cs2->get_height() * 0.5, 0)
540 };
541 p_gizmo->add_handles(handles, handles_material);
542 }
543
544 if (Object::cast_to<WorldBoundaryShape3D>(*s)) {
545 Ref<WorldBoundaryShape3D> wbs = s;
546 const Plane &p = wbs->get_plane();
547
548 Vector3 n1 = p.get_any_perpendicular_normal();
549 Vector3 n2 = p.normal.cross(n1).normalized();
550
551 Vector3 pface[4] = {
552 p.normal * p.d + n1 * 10.0 + n2 * 10.0,
553 p.normal * p.d + n1 * 10.0 + n2 * -10.0,
554 p.normal * p.d + n1 * -10.0 + n2 * -10.0,
555 p.normal * p.d + n1 * -10.0 + n2 * 10.0,
556 };
557
558 Vector<Vector3> points = {
559 pface[0],
560 pface[1],
561 pface[1],
562 pface[2],
563 pface[2],
564 pface[3],
565 pface[3],
566 pface[0],
567 p.normal * p.d,
568 p.normal * p.d + p.normal * 3
569 };
570
571 p_gizmo->add_lines(points, material);
572 p_gizmo->add_collision_segments(points);
573 }
574
575 if (Object::cast_to<ConvexPolygonShape3D>(*s)) {
576 Vector<Vector3> points = Object::cast_to<ConvexPolygonShape3D>(*s)->get_points();
577
578 if (points.size() > 3) {
579 Vector<Vector3> varr = Variant(points);
580 Geometry3D::MeshData md;
581 Error err = ConvexHullComputer::convex_hull(varr, md);
582 if (err == OK) {
583 Vector<Vector3> points2;
584 points2.resize(md.edges.size() * 2);
585 for (uint32_t i = 0; i < md.edges.size(); i++) {
586 points2.write[i * 2 + 0] = md.vertices[md.edges[i].vertex_a];
587 points2.write[i * 2 + 1] = md.vertices[md.edges[i].vertex_b];
588 }
589
590 p_gizmo->add_lines(points2, material);
591 p_gizmo->add_collision_segments(points2);
592 }
593 }
594 }
595
596 if (Object::cast_to<ConcavePolygonShape3D>(*s)) {
597 Ref<ConcavePolygonShape3D> cs2 = s;
598 Ref<ArrayMesh> mesh = cs2->get_debug_mesh();
599 p_gizmo->add_mesh(mesh, material);
600 p_gizmo->add_collision_segments(cs2->get_debug_mesh_lines());
601 }
602
603 if (Object::cast_to<SeparationRayShape3D>(*s)) {
604 Ref<SeparationRayShape3D> rs = s;
605
606 Vector<Vector3> points = {
607 Vector3(),
608 Vector3(0, 0, rs->get_length())
609 };
610 p_gizmo->add_lines(points, material);
611 p_gizmo->add_collision_segments(points);
612 Vector<Vector3> handles;
613 handles.push_back(Vector3(0, 0, rs->get_length()));
614 p_gizmo->add_handles(handles, handles_material);
615 }
616
617 if (Object::cast_to<HeightMapShape3D>(*s)) {
618 Ref<HeightMapShape3D> hms = s;
619
620 Ref<ArrayMesh> mesh = hms->get_debug_mesh();
621 p_gizmo->add_mesh(mesh, material);
622 }
623}
624