1/**************************************************************************/
2/* csg_shape.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 "csg_shape.h"
32
33#include "core/math/geometry_2d.h"
34
35void CSGShape3D::set_use_collision(bool p_enable) {
36 if (use_collision == p_enable) {
37 return;
38 }
39
40 use_collision = p_enable;
41
42 if (!is_inside_tree() || !is_root_shape()) {
43 return;
44 }
45
46 if (use_collision) {
47 root_collision_shape.instantiate();
48 root_collision_instance = PhysicsServer3D::get_singleton()->body_create();
49 PhysicsServer3D::get_singleton()->body_set_mode(root_collision_instance, PhysicsServer3D::BODY_MODE_STATIC);
50 PhysicsServer3D::get_singleton()->body_set_state(root_collision_instance, PhysicsServer3D::BODY_STATE_TRANSFORM, get_global_transform());
51 PhysicsServer3D::get_singleton()->body_add_shape(root_collision_instance, root_collision_shape->get_rid());
52 PhysicsServer3D::get_singleton()->body_set_space(root_collision_instance, get_world_3d()->get_space());
53 PhysicsServer3D::get_singleton()->body_attach_object_instance_id(root_collision_instance, get_instance_id());
54 set_collision_layer(collision_layer);
55 set_collision_mask(collision_mask);
56 set_collision_priority(collision_priority);
57 _make_dirty(); //force update
58 } else {
59 PhysicsServer3D::get_singleton()->free(root_collision_instance);
60 root_collision_instance = RID();
61 root_collision_shape.unref();
62 }
63 notify_property_list_changed();
64}
65
66bool CSGShape3D::is_using_collision() const {
67 return use_collision;
68}
69
70void CSGShape3D::set_collision_layer(uint32_t p_layer) {
71 collision_layer = p_layer;
72 if (root_collision_instance.is_valid()) {
73 PhysicsServer3D::get_singleton()->body_set_collision_layer(root_collision_instance, p_layer);
74 }
75}
76
77uint32_t CSGShape3D::get_collision_layer() const {
78 return collision_layer;
79}
80
81void CSGShape3D::set_collision_mask(uint32_t p_mask) {
82 collision_mask = p_mask;
83 if (root_collision_instance.is_valid()) {
84 PhysicsServer3D::get_singleton()->body_set_collision_mask(root_collision_instance, p_mask);
85 }
86}
87
88uint32_t CSGShape3D::get_collision_mask() const {
89 return collision_mask;
90}
91
92void CSGShape3D::set_collision_layer_value(int p_layer_number, bool p_value) {
93 ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive.");
94 ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive.");
95 uint32_t layer = get_collision_layer();
96 if (p_value) {
97 layer |= 1 << (p_layer_number - 1);
98 } else {
99 layer &= ~(1 << (p_layer_number - 1));
100 }
101 set_collision_layer(layer);
102}
103
104bool CSGShape3D::get_collision_layer_value(int p_layer_number) const {
105 ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive.");
106 ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive.");
107 return get_collision_layer() & (1 << (p_layer_number - 1));
108}
109
110void CSGShape3D::set_collision_mask_value(int p_layer_number, bool p_value) {
111 ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive.");
112 ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive.");
113 uint32_t mask = get_collision_mask();
114 if (p_value) {
115 mask |= 1 << (p_layer_number - 1);
116 } else {
117 mask &= ~(1 << (p_layer_number - 1));
118 }
119 set_collision_mask(mask);
120}
121
122bool CSGShape3D::get_collision_mask_value(int p_layer_number) const {
123 ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive.");
124 ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive.");
125 return get_collision_mask() & (1 << (p_layer_number - 1));
126}
127
128void CSGShape3D::set_collision_priority(real_t p_priority) {
129 collision_priority = p_priority;
130 if (root_collision_instance.is_valid()) {
131 PhysicsServer3D::get_singleton()->body_set_collision_priority(root_collision_instance, p_priority);
132 }
133}
134
135real_t CSGShape3D::get_collision_priority() const {
136 return collision_priority;
137}
138
139bool CSGShape3D::is_root_shape() const {
140 return !parent_shape;
141}
142
143void CSGShape3D::set_snap(float p_snap) {
144 snap = p_snap;
145}
146
147float CSGShape3D::get_snap() const {
148 return snap;
149}
150
151void CSGShape3D::_make_dirty(bool p_parent_removing) {
152 if ((p_parent_removing || is_root_shape()) && !dirty) {
153 call_deferred(SNAME("_update_shape")); // Must be deferred; otherwise, is_root_shape() will use the previous parent
154 }
155
156 if (!is_root_shape()) {
157 parent_shape->_make_dirty();
158 } else if (!dirty) {
159 call_deferred(SNAME("_update_shape"));
160 }
161
162 dirty = true;
163}
164
165CSGBrush *CSGShape3D::_get_brush() {
166 if (dirty) {
167 if (brush) {
168 memdelete(brush);
169 }
170 brush = nullptr;
171
172 CSGBrush *n = _build_brush();
173
174 for (int i = 0; i < get_child_count(); i++) {
175 CSGShape3D *child = Object::cast_to<CSGShape3D>(get_child(i));
176 if (!child) {
177 continue;
178 }
179 if (!child->is_visible()) {
180 continue;
181 }
182
183 CSGBrush *n2 = child->_get_brush();
184 if (!n2) {
185 continue;
186 }
187 if (!n) {
188 n = memnew(CSGBrush);
189
190 n->copy_from(*n2, child->get_transform());
191
192 } else {
193 CSGBrush *nn = memnew(CSGBrush);
194 CSGBrush *nn2 = memnew(CSGBrush);
195 nn2->copy_from(*n2, child->get_transform());
196
197 CSGBrushOperation bop;
198
199 switch (child->get_operation()) {
200 case CSGShape3D::OPERATION_UNION:
201 bop.merge_brushes(CSGBrushOperation::OPERATION_UNION, *n, *nn2, *nn, snap);
202 break;
203 case CSGShape3D::OPERATION_INTERSECTION:
204 bop.merge_brushes(CSGBrushOperation::OPERATION_INTERSECTION, *n, *nn2, *nn, snap);
205 break;
206 case CSGShape3D::OPERATION_SUBTRACTION:
207 bop.merge_brushes(CSGBrushOperation::OPERATION_SUBTRACTION, *n, *nn2, *nn, snap);
208 break;
209 }
210 memdelete(n);
211 memdelete(nn2);
212 n = nn;
213 }
214 }
215
216 if (n) {
217 AABB aabb;
218 for (int i = 0; i < n->faces.size(); i++) {
219 for (int j = 0; j < 3; j++) {
220 if (i == 0 && j == 0) {
221 aabb.position = n->faces[i].vertices[j];
222 } else {
223 aabb.expand_to(n->faces[i].vertices[j]);
224 }
225 }
226 }
227 node_aabb = aabb;
228 } else {
229 node_aabb = AABB();
230 }
231
232 brush = n;
233
234 dirty = false;
235 }
236
237 return brush;
238}
239
240int CSGShape3D::mikktGetNumFaces(const SMikkTSpaceContext *pContext) {
241 ShapeUpdateSurface &surface = *((ShapeUpdateSurface *)pContext->m_pUserData);
242
243 return surface.vertices.size() / 3;
244}
245
246int CSGShape3D::mikktGetNumVerticesOfFace(const SMikkTSpaceContext *pContext, const int iFace) {
247 // always 3
248 return 3;
249}
250
251void CSGShape3D::mikktGetPosition(const SMikkTSpaceContext *pContext, float fvPosOut[], const int iFace, const int iVert) {
252 ShapeUpdateSurface &surface = *((ShapeUpdateSurface *)pContext->m_pUserData);
253
254 Vector3 v = surface.verticesw[iFace * 3 + iVert];
255 fvPosOut[0] = v.x;
256 fvPosOut[1] = v.y;
257 fvPosOut[2] = v.z;
258}
259
260void CSGShape3D::mikktGetNormal(const SMikkTSpaceContext *pContext, float fvNormOut[], const int iFace, const int iVert) {
261 ShapeUpdateSurface &surface = *((ShapeUpdateSurface *)pContext->m_pUserData);
262
263 Vector3 n = surface.normalsw[iFace * 3 + iVert];
264 fvNormOut[0] = n.x;
265 fvNormOut[1] = n.y;
266 fvNormOut[2] = n.z;
267}
268
269void CSGShape3D::mikktGetTexCoord(const SMikkTSpaceContext *pContext, float fvTexcOut[], const int iFace, const int iVert) {
270 ShapeUpdateSurface &surface = *((ShapeUpdateSurface *)pContext->m_pUserData);
271
272 Vector2 t = surface.uvsw[iFace * 3 + iVert];
273 fvTexcOut[0] = t.x;
274 fvTexcOut[1] = t.y;
275}
276
277void CSGShape3D::mikktSetTSpaceDefault(const SMikkTSpaceContext *pContext, const float fvTangent[], const float fvBiTangent[], const float fMagS, const float fMagT,
278 const tbool bIsOrientationPreserving, const int iFace, const int iVert) {
279 ShapeUpdateSurface &surface = *((ShapeUpdateSurface *)pContext->m_pUserData);
280
281 int i = iFace * 3 + iVert;
282 Vector3 normal = surface.normalsw[i];
283 Vector3 tangent = Vector3(fvTangent[0], fvTangent[1], fvTangent[2]);
284 Vector3 bitangent = Vector3(-fvBiTangent[0], -fvBiTangent[1], -fvBiTangent[2]); // for some reason these are reversed, something with the coordinate system in Godot
285 float d = bitangent.dot(normal.cross(tangent));
286
287 i *= 4;
288 surface.tansw[i++] = tangent.x;
289 surface.tansw[i++] = tangent.y;
290 surface.tansw[i++] = tangent.z;
291 surface.tansw[i++] = d < 0 ? -1 : 1;
292}
293
294void CSGShape3D::_update_shape() {
295 if (!is_root_shape()) {
296 return;
297 }
298
299 set_base(RID());
300 root_mesh.unref(); //byebye root mesh
301
302 CSGBrush *n = _get_brush();
303 ERR_FAIL_COND_MSG(!n, "Cannot get CSGBrush.");
304
305 OAHashMap<Vector3, Vector3> vec_map;
306
307 Vector<int> face_count;
308 face_count.resize(n->materials.size() + 1);
309 for (int i = 0; i < face_count.size(); i++) {
310 face_count.write[i] = 0;
311 }
312
313 for (int i = 0; i < n->faces.size(); i++) {
314 int mat = n->faces[i].material;
315 ERR_CONTINUE(mat < -1 || mat >= face_count.size());
316 int idx = mat == -1 ? face_count.size() - 1 : mat;
317
318 if (n->faces[i].smooth) {
319 Plane p(n->faces[i].vertices[0], n->faces[i].vertices[1], n->faces[i].vertices[2]);
320
321 for (int j = 0; j < 3; j++) {
322 Vector3 v = n->faces[i].vertices[j];
323 Vector3 add;
324 if (vec_map.lookup(v, add)) {
325 add += p.normal;
326 } else {
327 add = p.normal;
328 }
329 vec_map.set(v, add);
330 }
331 }
332
333 face_count.write[idx]++;
334 }
335
336 Vector<ShapeUpdateSurface> surfaces;
337
338 surfaces.resize(face_count.size());
339
340 //create arrays
341 for (int i = 0; i < surfaces.size(); i++) {
342 surfaces.write[i].vertices.resize(face_count[i] * 3);
343 surfaces.write[i].normals.resize(face_count[i] * 3);
344 surfaces.write[i].uvs.resize(face_count[i] * 3);
345 if (calculate_tangents) {
346 surfaces.write[i].tans.resize(face_count[i] * 3 * 4);
347 }
348 surfaces.write[i].last_added = 0;
349
350 if (i != surfaces.size() - 1) {
351 surfaces.write[i].material = n->materials[i];
352 }
353
354 surfaces.write[i].verticesw = surfaces.write[i].vertices.ptrw();
355 surfaces.write[i].normalsw = surfaces.write[i].normals.ptrw();
356 surfaces.write[i].uvsw = surfaces.write[i].uvs.ptrw();
357 if (calculate_tangents) {
358 surfaces.write[i].tansw = surfaces.write[i].tans.ptrw();
359 }
360 }
361
362 //fill arrays
363 {
364 for (int i = 0; i < n->faces.size(); i++) {
365 int order[3] = { 0, 1, 2 };
366
367 if (n->faces[i].invert) {
368 SWAP(order[1], order[2]);
369 }
370
371 int mat = n->faces[i].material;
372 ERR_CONTINUE(mat < -1 || mat >= face_count.size());
373 int idx = mat == -1 ? face_count.size() - 1 : mat;
374
375 int last = surfaces[idx].last_added;
376
377 Plane p(n->faces[i].vertices[0], n->faces[i].vertices[1], n->faces[i].vertices[2]);
378
379 for (int j = 0; j < 3; j++) {
380 Vector3 v = n->faces[i].vertices[j];
381
382 Vector3 normal = p.normal;
383
384 if (n->faces[i].smooth && vec_map.lookup(v, normal)) {
385 normal.normalize();
386 }
387
388 if (n->faces[i].invert) {
389 normal = -normal;
390 }
391
392 int k = last + order[j];
393 surfaces[idx].verticesw[k] = v;
394 surfaces[idx].uvsw[k] = n->faces[i].uvs[j];
395 surfaces[idx].normalsw[k] = normal;
396
397 if (calculate_tangents) {
398 // zero out our tangents for now
399 k *= 4;
400 surfaces[idx].tansw[k++] = 0.0;
401 surfaces[idx].tansw[k++] = 0.0;
402 surfaces[idx].tansw[k++] = 0.0;
403 surfaces[idx].tansw[k++] = 0.0;
404 }
405 }
406
407 surfaces.write[idx].last_added += 3;
408 }
409 }
410
411 root_mesh.instantiate();
412 //create surfaces
413
414 for (int i = 0; i < surfaces.size(); i++) {
415 // calculate tangents for this surface
416 bool have_tangents = calculate_tangents;
417 if (have_tangents) {
418 SMikkTSpaceInterface mkif;
419 mkif.m_getNormal = mikktGetNormal;
420 mkif.m_getNumFaces = mikktGetNumFaces;
421 mkif.m_getNumVerticesOfFace = mikktGetNumVerticesOfFace;
422 mkif.m_getPosition = mikktGetPosition;
423 mkif.m_getTexCoord = mikktGetTexCoord;
424 mkif.m_setTSpace = mikktSetTSpaceDefault;
425 mkif.m_setTSpaceBasic = nullptr;
426
427 SMikkTSpaceContext msc;
428 msc.m_pInterface = &mkif;
429 msc.m_pUserData = &surfaces.write[i];
430 have_tangents = genTangSpaceDefault(&msc);
431 }
432
433 if (surfaces[i].last_added == 0) {
434 continue;
435 }
436
437 // and convert to surface array
438 Array array;
439 array.resize(Mesh::ARRAY_MAX);
440
441 array[Mesh::ARRAY_VERTEX] = surfaces[i].vertices;
442 array[Mesh::ARRAY_NORMAL] = surfaces[i].normals;
443 array[Mesh::ARRAY_TEX_UV] = surfaces[i].uvs;
444 if (have_tangents) {
445 array[Mesh::ARRAY_TANGENT] = surfaces[i].tans;
446 }
447
448 int idx = root_mesh->get_surface_count();
449 root_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, array);
450 root_mesh->surface_set_material(idx, surfaces[i].material);
451 }
452
453 set_base(root_mesh->get_rid());
454
455 _update_collision_faces();
456}
457
458void CSGShape3D::_update_collision_faces() {
459 if (use_collision && is_root_shape() && root_collision_shape.is_valid()) {
460 CSGBrush *n = _get_brush();
461 ERR_FAIL_COND_MSG(!n, "Cannot get CSGBrush.");
462 Vector<Vector3> physics_faces;
463 physics_faces.resize(n->faces.size() * 3);
464 Vector3 *physicsw = physics_faces.ptrw();
465
466 for (int i = 0; i < n->faces.size(); i++) {
467 int order[3] = { 0, 1, 2 };
468
469 if (n->faces[i].invert) {
470 SWAP(order[1], order[2]);
471 }
472
473 physicsw[i * 3 + 0] = n->faces[i].vertices[order[0]];
474 physicsw[i * 3 + 1] = n->faces[i].vertices[order[1]];
475 physicsw[i * 3 + 2] = n->faces[i].vertices[order[2]];
476 }
477
478 root_collision_shape->set_faces(physics_faces);
479
480 if (_is_debug_collision_shape_visible()) {
481 _update_debug_collision_shape();
482 }
483 }
484}
485
486bool CSGShape3D::_is_debug_collision_shape_visible() {
487 return is_inside_tree() && (get_tree()->is_debugging_collisions_hint() || Engine::get_singleton()->is_editor_hint());
488}
489
490void CSGShape3D::_update_debug_collision_shape() {
491 // NOTE: This is called only for the root shape with collision, when root_collision_shape is valid.
492
493 ERR_FAIL_NULL(RenderingServer::get_singleton());
494
495 if (root_collision_debug_instance.is_null()) {
496 root_collision_debug_instance = RS::get_singleton()->instance_create();
497 }
498
499 Ref<Mesh> debug_mesh = root_collision_shape->get_debug_mesh();
500 RS::get_singleton()->instance_set_scenario(root_collision_debug_instance, get_world_3d()->get_scenario());
501 RS::get_singleton()->instance_set_base(root_collision_debug_instance, debug_mesh->get_rid());
502 RS::get_singleton()->instance_set_transform(root_collision_debug_instance, get_global_transform());
503}
504
505void CSGShape3D::_clear_debug_collision_shape() {
506 if (root_collision_debug_instance.is_valid()) {
507 RS::get_singleton()->free(root_collision_debug_instance);
508 root_collision_debug_instance = RID();
509 }
510}
511
512void CSGShape3D::_on_transform_changed() {
513 if (root_collision_debug_instance.is_valid() && !debug_shape_old_transform.is_equal_approx(get_global_transform())) {
514 debug_shape_old_transform = get_global_transform();
515 RS::get_singleton()->instance_set_transform(root_collision_debug_instance, debug_shape_old_transform);
516 }
517}
518
519AABB CSGShape3D::get_aabb() const {
520 return node_aabb;
521}
522
523Vector<Vector3> CSGShape3D::get_brush_faces() {
524 ERR_FAIL_COND_V(!is_inside_tree(), Vector<Vector3>());
525 CSGBrush *b = _get_brush();
526 if (!b) {
527 return Vector<Vector3>();
528 }
529
530 Vector<Vector3> faces;
531 int fc = b->faces.size();
532 faces.resize(fc * 3);
533 {
534 Vector3 *w = faces.ptrw();
535 for (int i = 0; i < fc; i++) {
536 w[i * 3 + 0] = b->faces[i].vertices[0];
537 w[i * 3 + 1] = b->faces[i].vertices[1];
538 w[i * 3 + 2] = b->faces[i].vertices[2];
539 }
540 }
541
542 return faces;
543}
544
545void CSGShape3D::_notification(int p_what) {
546 switch (p_what) {
547 case NOTIFICATION_PARENTED: {
548 Node *parentn = get_parent();
549 if (parentn) {
550 parent_shape = Object::cast_to<CSGShape3D>(parentn);
551 if (parent_shape) {
552 set_base(RID());
553 root_mesh.unref();
554 }
555 }
556 if (!brush || parent_shape) {
557 // Update this node if uninitialized, or both this node and its new parent if it gets added to another CSG shape
558 _make_dirty();
559 }
560 last_visible = is_visible();
561 } break;
562
563 case NOTIFICATION_UNPARENTED: {
564 if (!is_root_shape()) {
565 // Update this node and its previous parent only if it's currently being removed from another CSG shape
566 _make_dirty(true); // Must be forced since is_root_shape() uses the previous parent
567 }
568 parent_shape = nullptr;
569 } break;
570
571 case NOTIFICATION_VISIBILITY_CHANGED: {
572 if (!is_root_shape() && last_visible != is_visible()) {
573 // Update this node's parent only if its own visibility has changed, not the visibility of parent nodes
574 parent_shape->_make_dirty();
575 }
576 last_visible = is_visible();
577 } break;
578
579 case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
580 if (!is_root_shape()) {
581 // Update this node's parent only if its own transformation has changed, not the transformation of parent nodes
582 parent_shape->_make_dirty();
583 }
584 } break;
585
586 case NOTIFICATION_ENTER_TREE: {
587 if (use_collision && is_root_shape()) {
588 root_collision_shape.instantiate();
589 root_collision_instance = PhysicsServer3D::get_singleton()->body_create();
590 PhysicsServer3D::get_singleton()->body_set_mode(root_collision_instance, PhysicsServer3D::BODY_MODE_STATIC);
591 PhysicsServer3D::get_singleton()->body_set_state(root_collision_instance, PhysicsServer3D::BODY_STATE_TRANSFORM, get_global_transform());
592 PhysicsServer3D::get_singleton()->body_add_shape(root_collision_instance, root_collision_shape->get_rid());
593 PhysicsServer3D::get_singleton()->body_set_space(root_collision_instance, get_world_3d()->get_space());
594 PhysicsServer3D::get_singleton()->body_attach_object_instance_id(root_collision_instance, get_instance_id());
595 set_collision_layer(collision_layer);
596 set_collision_mask(collision_mask);
597 set_collision_priority(collision_priority);
598 debug_shape_old_transform = get_global_transform();
599 _make_dirty();
600 }
601 } break;
602
603 case NOTIFICATION_EXIT_TREE: {
604 if (use_collision && is_root_shape() && root_collision_instance.is_valid()) {
605 PhysicsServer3D::get_singleton()->free(root_collision_instance);
606 root_collision_instance = RID();
607 root_collision_shape.unref();
608 _clear_debug_collision_shape();
609 }
610 } break;
611
612 case NOTIFICATION_TRANSFORM_CHANGED: {
613 if (use_collision && is_root_shape() && root_collision_instance.is_valid()) {
614 PhysicsServer3D::get_singleton()->body_set_state(root_collision_instance, PhysicsServer3D::BODY_STATE_TRANSFORM, get_global_transform());
615 }
616 _on_transform_changed();
617 } break;
618 }
619}
620
621void CSGShape3D::set_operation(Operation p_operation) {
622 operation = p_operation;
623 _make_dirty();
624 update_gizmos();
625}
626
627CSGShape3D::Operation CSGShape3D::get_operation() const {
628 return operation;
629}
630
631void CSGShape3D::set_calculate_tangents(bool p_calculate_tangents) {
632 calculate_tangents = p_calculate_tangents;
633 _make_dirty();
634}
635
636bool CSGShape3D::is_calculating_tangents() const {
637 return calculate_tangents;
638}
639
640void CSGShape3D::_validate_property(PropertyInfo &p_property) const {
641 bool is_collision_prefixed = p_property.name.begins_with("collision_");
642 if ((is_collision_prefixed || p_property.name.begins_with("use_collision")) && is_inside_tree() && !is_root_shape()) {
643 //hide collision if not root
644 p_property.usage = PROPERTY_USAGE_NO_EDITOR;
645 } else if (is_collision_prefixed && !bool(get("use_collision"))) {
646 p_property.usage = PROPERTY_USAGE_NO_EDITOR;
647 }
648}
649
650Array CSGShape3D::get_meshes() const {
651 if (root_mesh.is_valid()) {
652 Array arr;
653 arr.resize(2);
654 arr[0] = Transform3D();
655 arr[1] = root_mesh;
656 return arr;
657 }
658
659 return Array();
660}
661
662void CSGShape3D::_bind_methods() {
663 ClassDB::bind_method(D_METHOD("_update_shape"), &CSGShape3D::_update_shape);
664 ClassDB::bind_method(D_METHOD("is_root_shape"), &CSGShape3D::is_root_shape);
665
666 ClassDB::bind_method(D_METHOD("set_operation", "operation"), &CSGShape3D::set_operation);
667 ClassDB::bind_method(D_METHOD("get_operation"), &CSGShape3D::get_operation);
668
669 ClassDB::bind_method(D_METHOD("set_snap", "snap"), &CSGShape3D::set_snap);
670 ClassDB::bind_method(D_METHOD("get_snap"), &CSGShape3D::get_snap);
671
672 ClassDB::bind_method(D_METHOD("set_use_collision", "operation"), &CSGShape3D::set_use_collision);
673 ClassDB::bind_method(D_METHOD("is_using_collision"), &CSGShape3D::is_using_collision);
674
675 ClassDB::bind_method(D_METHOD("set_collision_layer", "layer"), &CSGShape3D::set_collision_layer);
676 ClassDB::bind_method(D_METHOD("get_collision_layer"), &CSGShape3D::get_collision_layer);
677
678 ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &CSGShape3D::set_collision_mask);
679 ClassDB::bind_method(D_METHOD("get_collision_mask"), &CSGShape3D::get_collision_mask);
680
681 ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &CSGShape3D::set_collision_mask_value);
682 ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &CSGShape3D::get_collision_mask_value);
683
684 ClassDB::bind_method(D_METHOD("set_collision_layer_value", "layer_number", "value"), &CSGShape3D::set_collision_layer_value);
685 ClassDB::bind_method(D_METHOD("get_collision_layer_value", "layer_number"), &CSGShape3D::get_collision_layer_value);
686
687 ClassDB::bind_method(D_METHOD("set_collision_priority", "priority"), &CSGShape3D::set_collision_priority);
688 ClassDB::bind_method(D_METHOD("get_collision_priority"), &CSGShape3D::get_collision_priority);
689
690 ClassDB::bind_method(D_METHOD("set_calculate_tangents", "enabled"), &CSGShape3D::set_calculate_tangents);
691 ClassDB::bind_method(D_METHOD("is_calculating_tangents"), &CSGShape3D::is_calculating_tangents);
692
693 ClassDB::bind_method(D_METHOD("get_meshes"), &CSGShape3D::get_meshes);
694
695 ADD_PROPERTY(PropertyInfo(Variant::INT, "operation", PROPERTY_HINT_ENUM, "Union,Intersection,Subtraction"), "set_operation", "get_operation");
696 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "snap", PROPERTY_HINT_RANGE, "0.000001,1,0.000001,suffix:m"), "set_snap", "get_snap");
697 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "calculate_tangents"), "set_calculate_tangents", "is_calculating_tangents");
698
699 ADD_GROUP("Collision", "collision_");
700 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_collision"), "set_use_collision", "is_using_collision");
701 ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_layer", "get_collision_layer");
702 ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask");
703 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision_priority"), "set_collision_priority", "get_collision_priority");
704
705 BIND_ENUM_CONSTANT(OPERATION_UNION);
706 BIND_ENUM_CONSTANT(OPERATION_INTERSECTION);
707 BIND_ENUM_CONSTANT(OPERATION_SUBTRACTION);
708}
709
710CSGShape3D::CSGShape3D() {
711 set_notify_local_transform(true);
712}
713
714CSGShape3D::~CSGShape3D() {
715 if (brush) {
716 memdelete(brush);
717 brush = nullptr;
718 }
719}
720
721//////////////////////////////////
722
723CSGBrush *CSGCombiner3D::_build_brush() {
724 return memnew(CSGBrush); //does not build anything
725}
726
727CSGCombiner3D::CSGCombiner3D() {
728}
729
730/////////////////////
731
732CSGBrush *CSGPrimitive3D::_create_brush_from_arrays(const Vector<Vector3> &p_vertices, const Vector<Vector2> &p_uv, const Vector<bool> &p_smooth, const Vector<Ref<Material>> &p_materials) {
733 CSGBrush *new_brush = memnew(CSGBrush);
734
735 Vector<bool> invert;
736 invert.resize(p_vertices.size() / 3);
737 {
738 int ic = invert.size();
739 bool *w = invert.ptrw();
740 for (int i = 0; i < ic; i++) {
741 w[i] = flip_faces;
742 }
743 }
744 new_brush->build_from_faces(p_vertices, p_uv, p_smooth, p_materials, invert);
745
746 return new_brush;
747}
748
749void CSGPrimitive3D::_bind_methods() {
750 ClassDB::bind_method(D_METHOD("set_flip_faces", "flip_faces"), &CSGPrimitive3D::set_flip_faces);
751 ClassDB::bind_method(D_METHOD("get_flip_faces"), &CSGPrimitive3D::get_flip_faces);
752
753 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_faces"), "set_flip_faces", "get_flip_faces");
754}
755
756void CSGPrimitive3D::set_flip_faces(bool p_invert) {
757 if (flip_faces == p_invert) {
758 return;
759 }
760
761 flip_faces = p_invert;
762
763 _make_dirty();
764}
765
766bool CSGPrimitive3D::get_flip_faces() {
767 return flip_faces;
768}
769
770CSGPrimitive3D::CSGPrimitive3D() {
771 flip_faces = false;
772}
773
774/////////////////////
775
776CSGBrush *CSGMesh3D::_build_brush() {
777 if (!mesh.is_valid()) {
778 return memnew(CSGBrush);
779 }
780
781 Vector<Vector3> vertices;
782 Vector<bool> smooth;
783 Vector<Ref<Material>> materials;
784 Vector<Vector2> uvs;
785 Ref<Material> base_material = get_material();
786
787 for (int i = 0; i < mesh->get_surface_count(); i++) {
788 if (mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) {
789 continue;
790 }
791
792 Array arrays = mesh->surface_get_arrays(i);
793
794 if (arrays.size() == 0) {
795 _make_dirty();
796 ERR_FAIL_COND_V(arrays.size() == 0, memnew(CSGBrush));
797 }
798
799 Vector<Vector3> avertices = arrays[Mesh::ARRAY_VERTEX];
800 if (avertices.size() == 0) {
801 continue;
802 }
803
804 const Vector3 *vr = avertices.ptr();
805
806 Vector<Vector3> anormals = arrays[Mesh::ARRAY_NORMAL];
807 const Vector3 *nr = nullptr;
808 if (anormals.size()) {
809 nr = anormals.ptr();
810 }
811
812 Vector<Vector2> auvs = arrays[Mesh::ARRAY_TEX_UV];
813 const Vector2 *uvr = nullptr;
814 if (auvs.size()) {
815 uvr = auvs.ptr();
816 }
817
818 Ref<Material> mat;
819 if (base_material.is_valid()) {
820 mat = base_material;
821 } else {
822 mat = mesh->surface_get_material(i);
823 }
824
825 Vector<int> aindices = arrays[Mesh::ARRAY_INDEX];
826 if (aindices.size()) {
827 int as = vertices.size();
828 int is = aindices.size();
829
830 vertices.resize(as + is);
831 smooth.resize((as + is) / 3);
832 materials.resize((as + is) / 3);
833 uvs.resize(as + is);
834
835 Vector3 *vw = vertices.ptrw();
836 bool *sw = smooth.ptrw();
837 Vector2 *uvw = uvs.ptrw();
838 Ref<Material> *mw = materials.ptrw();
839
840 const int *ir = aindices.ptr();
841
842 for (int j = 0; j < is; j += 3) {
843 Vector3 vertex[3];
844 Vector3 normal[3];
845 Vector2 uv[3];
846
847 for (int k = 0; k < 3; k++) {
848 int idx = ir[j + k];
849 vertex[k] = vr[idx];
850 if (nr) {
851 normal[k] = nr[idx];
852 }
853 if (uvr) {
854 uv[k] = uvr[idx];
855 }
856 }
857
858 bool flat = normal[0].is_equal_approx(normal[1]) && normal[0].is_equal_approx(normal[2]);
859
860 vw[as + j + 0] = vertex[0];
861 vw[as + j + 1] = vertex[1];
862 vw[as + j + 2] = vertex[2];
863
864 uvw[as + j + 0] = uv[0];
865 uvw[as + j + 1] = uv[1];
866 uvw[as + j + 2] = uv[2];
867
868 sw[(as + j) / 3] = !flat;
869 mw[(as + j) / 3] = mat;
870 }
871 } else {
872 int as = vertices.size();
873 int is = avertices.size();
874
875 vertices.resize(as + is);
876 smooth.resize((as + is) / 3);
877 uvs.resize(as + is);
878 materials.resize((as + is) / 3);
879
880 Vector3 *vw = vertices.ptrw();
881 bool *sw = smooth.ptrw();
882 Vector2 *uvw = uvs.ptrw();
883 Ref<Material> *mw = materials.ptrw();
884
885 for (int j = 0; j < is; j += 3) {
886 Vector3 vertex[3];
887 Vector3 normal[3];
888 Vector2 uv[3];
889
890 for (int k = 0; k < 3; k++) {
891 vertex[k] = vr[j + k];
892 if (nr) {
893 normal[k] = nr[j + k];
894 }
895 if (uvr) {
896 uv[k] = uvr[j + k];
897 }
898 }
899
900 bool flat = normal[0].is_equal_approx(normal[1]) && normal[0].is_equal_approx(normal[2]);
901
902 vw[as + j + 0] = vertex[0];
903 vw[as + j + 1] = vertex[1];
904 vw[as + j + 2] = vertex[2];
905
906 uvw[as + j + 0] = uv[0];
907 uvw[as + j + 1] = uv[1];
908 uvw[as + j + 2] = uv[2];
909
910 sw[(as + j) / 3] = !flat;
911 mw[(as + j) / 3] = mat;
912 }
913 }
914 }
915
916 if (vertices.size() == 0) {
917 return memnew(CSGBrush);
918 }
919
920 return _create_brush_from_arrays(vertices, uvs, smooth, materials);
921}
922
923void CSGMesh3D::_mesh_changed() {
924 _make_dirty();
925 update_gizmos();
926}
927
928void CSGMesh3D::set_material(const Ref<Material> &p_material) {
929 if (material == p_material) {
930 return;
931 }
932 material = p_material;
933 _make_dirty();
934}
935
936Ref<Material> CSGMesh3D::get_material() const {
937 return material;
938}
939
940void CSGMesh3D::_bind_methods() {
941 ClassDB::bind_method(D_METHOD("set_mesh", "mesh"), &CSGMesh3D::set_mesh);
942 ClassDB::bind_method(D_METHOD("get_mesh"), &CSGMesh3D::get_mesh);
943
944 ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGMesh3D::set_material);
945 ClassDB::bind_method(D_METHOD("get_material"), &CSGMesh3D::get_material);
946
947 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh");
948 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial"), "set_material", "get_material");
949}
950
951void CSGMesh3D::set_mesh(const Ref<Mesh> &p_mesh) {
952 if (mesh == p_mesh) {
953 return;
954 }
955 if (mesh.is_valid()) {
956 mesh->disconnect_changed(callable_mp(this, &CSGMesh3D::_mesh_changed));
957 }
958 mesh = p_mesh;
959
960 if (mesh.is_valid()) {
961 mesh->connect_changed(callable_mp(this, &CSGMesh3D::_mesh_changed));
962 }
963
964 _mesh_changed();
965}
966
967Ref<Mesh> CSGMesh3D::get_mesh() {
968 return mesh;
969}
970
971////////////////////////////////
972
973CSGBrush *CSGSphere3D::_build_brush() {
974 // set our bounding box
975
976 CSGBrush *new_brush = memnew(CSGBrush);
977
978 int face_count = rings * radial_segments * 2 - radial_segments * 2;
979
980 bool invert_val = get_flip_faces();
981 Ref<Material> base_material = get_material();
982
983 Vector<Vector3> faces;
984 Vector<Vector2> uvs;
985 Vector<bool> smooth;
986 Vector<Ref<Material>> materials;
987 Vector<bool> invert;
988
989 faces.resize(face_count * 3);
990 uvs.resize(face_count * 3);
991
992 smooth.resize(face_count);
993 materials.resize(face_count);
994 invert.resize(face_count);
995
996 {
997 Vector3 *facesw = faces.ptrw();
998 Vector2 *uvsw = uvs.ptrw();
999 bool *smoothw = smooth.ptrw();
1000 Ref<Material> *materialsw = materials.ptrw();
1001 bool *invertw = invert.ptrw();
1002
1003 // We want to follow an order that's convenient for UVs.
1004 // For latitude step we start at the top and move down like in an image.
1005 const double latitude_step = -Math_PI / rings;
1006 const double longitude_step = Math_TAU / radial_segments;
1007 int face = 0;
1008 for (int i = 0; i < rings; i++) {
1009 double latitude0 = latitude_step * i + Math_TAU / 4;
1010 double cos0 = Math::cos(latitude0);
1011 double sin0 = Math::sin(latitude0);
1012 double v0 = double(i) / rings;
1013
1014 double latitude1 = latitude_step * (i + 1) + Math_TAU / 4;
1015 double cos1 = Math::cos(latitude1);
1016 double sin1 = Math::sin(latitude1);
1017 double v1 = double(i + 1) / rings;
1018
1019 for (int j = 0; j < radial_segments; j++) {
1020 double longitude0 = longitude_step * j;
1021 // We give sin to X and cos to Z on purpose.
1022 // This allows UVs to be CCW on +X so it maps to images well.
1023 double x0 = Math::sin(longitude0);
1024 double z0 = Math::cos(longitude0);
1025 double u0 = double(j) / radial_segments;
1026
1027 double longitude1 = longitude_step * (j + 1);
1028 if (j == radial_segments - 1) {
1029 longitude1 = 0;
1030 }
1031
1032 double x1 = Math::sin(longitude1);
1033 double z1 = Math::cos(longitude1);
1034 double u1 = double(j + 1) / radial_segments;
1035
1036 Vector3 v[4] = {
1037 Vector3(x0 * cos0, sin0, z0 * cos0) * radius,
1038 Vector3(x1 * cos0, sin0, z1 * cos0) * radius,
1039 Vector3(x1 * cos1, sin1, z1 * cos1) * radius,
1040 Vector3(x0 * cos1, sin1, z0 * cos1) * radius,
1041 };
1042
1043 Vector2 u[4] = {
1044 Vector2(u0, v0),
1045 Vector2(u1, v0),
1046 Vector2(u1, v1),
1047 Vector2(u0, v1),
1048 };
1049
1050 // Draw the first face, but skip this at the north pole (i == 0).
1051 if (i > 0) {
1052 facesw[face * 3 + 0] = v[0];
1053 facesw[face * 3 + 1] = v[1];
1054 facesw[face * 3 + 2] = v[2];
1055
1056 uvsw[face * 3 + 0] = u[0];
1057 uvsw[face * 3 + 1] = u[1];
1058 uvsw[face * 3 + 2] = u[2];
1059
1060 smoothw[face] = smooth_faces;
1061 invertw[face] = invert_val;
1062 materialsw[face] = base_material;
1063
1064 face++;
1065 }
1066
1067 // Draw the second face, but skip this at the south pole (i == rings - 1).
1068 if (i < rings - 1) {
1069 facesw[face * 3 + 0] = v[2];
1070 facesw[face * 3 + 1] = v[3];
1071 facesw[face * 3 + 2] = v[0];
1072
1073 uvsw[face * 3 + 0] = u[2];
1074 uvsw[face * 3 + 1] = u[3];
1075 uvsw[face * 3 + 2] = u[0];
1076
1077 smoothw[face] = smooth_faces;
1078 invertw[face] = invert_val;
1079 materialsw[face] = base_material;
1080
1081 face++;
1082 }
1083 }
1084 }
1085
1086 if (face != face_count) {
1087 ERR_PRINT("Face mismatch bug! fix code");
1088 }
1089 }
1090
1091 new_brush->build_from_faces(faces, uvs, smooth, materials, invert);
1092
1093 return new_brush;
1094}
1095
1096void CSGSphere3D::_bind_methods() {
1097 ClassDB::bind_method(D_METHOD("set_radius", "radius"), &CSGSphere3D::set_radius);
1098 ClassDB::bind_method(D_METHOD("get_radius"), &CSGSphere3D::get_radius);
1099
1100 ClassDB::bind_method(D_METHOD("set_radial_segments", "radial_segments"), &CSGSphere3D::set_radial_segments);
1101 ClassDB::bind_method(D_METHOD("get_radial_segments"), &CSGSphere3D::get_radial_segments);
1102 ClassDB::bind_method(D_METHOD("set_rings", "rings"), &CSGSphere3D::set_rings);
1103 ClassDB::bind_method(D_METHOD("get_rings"), &CSGSphere3D::get_rings);
1104
1105 ClassDB::bind_method(D_METHOD("set_smooth_faces", "smooth_faces"), &CSGSphere3D::set_smooth_faces);
1106 ClassDB::bind_method(D_METHOD("get_smooth_faces"), &CSGSphere3D::get_smooth_faces);
1107
1108 ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGSphere3D::set_material);
1109 ClassDB::bind_method(D_METHOD("get_material"), &CSGSphere3D::get_material);
1110
1111 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,suffix:m"), "set_radius", "get_radius");
1112 ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_segments", PROPERTY_HINT_RANGE, "1,100,1"), "set_radial_segments", "get_radial_segments");
1113 ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "1,100,1"), "set_rings", "get_rings");
1114 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces");
1115 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial"), "set_material", "get_material");
1116}
1117
1118void CSGSphere3D::set_radius(const float p_radius) {
1119 ERR_FAIL_COND(p_radius <= 0);
1120 radius = p_radius;
1121 _make_dirty();
1122 update_gizmos();
1123}
1124
1125float CSGSphere3D::get_radius() const {
1126 return radius;
1127}
1128
1129void CSGSphere3D::set_radial_segments(const int p_radial_segments) {
1130 radial_segments = p_radial_segments > 4 ? p_radial_segments : 4;
1131 _make_dirty();
1132 update_gizmos();
1133}
1134
1135int CSGSphere3D::get_radial_segments() const {
1136 return radial_segments;
1137}
1138
1139void CSGSphere3D::set_rings(const int p_rings) {
1140 rings = p_rings > 1 ? p_rings : 1;
1141 _make_dirty();
1142 update_gizmos();
1143}
1144
1145int CSGSphere3D::get_rings() const {
1146 return rings;
1147}
1148
1149void CSGSphere3D::set_smooth_faces(const bool p_smooth_faces) {
1150 smooth_faces = p_smooth_faces;
1151 _make_dirty();
1152}
1153
1154bool CSGSphere3D::get_smooth_faces() const {
1155 return smooth_faces;
1156}
1157
1158void CSGSphere3D::set_material(const Ref<Material> &p_material) {
1159 material = p_material;
1160 _make_dirty();
1161}
1162
1163Ref<Material> CSGSphere3D::get_material() const {
1164 return material;
1165}
1166
1167CSGSphere3D::CSGSphere3D() {
1168 // defaults
1169 radius = 0.5;
1170 radial_segments = 12;
1171 rings = 6;
1172 smooth_faces = true;
1173}
1174
1175///////////////
1176
1177CSGBrush *CSGBox3D::_build_brush() {
1178 // set our bounding box
1179
1180 CSGBrush *new_brush = memnew(CSGBrush);
1181
1182 int face_count = 12; //it's a cube..
1183
1184 bool invert_val = get_flip_faces();
1185 Ref<Material> base_material = get_material();
1186
1187 Vector<Vector3> faces;
1188 Vector<Vector2> uvs;
1189 Vector<bool> smooth;
1190 Vector<Ref<Material>> materials;
1191 Vector<bool> invert;
1192
1193 faces.resize(face_count * 3);
1194 uvs.resize(face_count * 3);
1195
1196 smooth.resize(face_count);
1197 materials.resize(face_count);
1198 invert.resize(face_count);
1199
1200 {
1201 Vector3 *facesw = faces.ptrw();
1202 Vector2 *uvsw = uvs.ptrw();
1203 bool *smoothw = smooth.ptrw();
1204 Ref<Material> *materialsw = materials.ptrw();
1205 bool *invertw = invert.ptrw();
1206
1207 int face = 0;
1208
1209 Vector3 vertex_mul = size / 2;
1210
1211 {
1212 for (int i = 0; i < 6; i++) {
1213 Vector3 face_points[4];
1214 float uv_points[8] = { 0, 0, 0, 1, 1, 1, 1, 0 };
1215
1216 for (int j = 0; j < 4; j++) {
1217 float v[3];
1218 v[0] = 1.0;
1219 v[1] = 1 - 2 * ((j >> 1) & 1);
1220 v[2] = v[1] * (1 - 2 * (j & 1));
1221
1222 for (int k = 0; k < 3; k++) {
1223 if (i < 3) {
1224 face_points[j][(i + k) % 3] = v[k];
1225 } else {
1226 face_points[3 - j][(i + k) % 3] = -v[k];
1227 }
1228 }
1229 }
1230
1231 Vector2 u[4];
1232 for (int j = 0; j < 4; j++) {
1233 u[j] = Vector2(uv_points[j * 2 + 0], uv_points[j * 2 + 1]);
1234 }
1235
1236 //face 1
1237 facesw[face * 3 + 0] = face_points[0] * vertex_mul;
1238 facesw[face * 3 + 1] = face_points[1] * vertex_mul;
1239 facesw[face * 3 + 2] = face_points[2] * vertex_mul;
1240
1241 uvsw[face * 3 + 0] = u[0];
1242 uvsw[face * 3 + 1] = u[1];
1243 uvsw[face * 3 + 2] = u[2];
1244
1245 smoothw[face] = false;
1246 invertw[face] = invert_val;
1247 materialsw[face] = base_material;
1248
1249 face++;
1250 //face 2
1251 facesw[face * 3 + 0] = face_points[2] * vertex_mul;
1252 facesw[face * 3 + 1] = face_points[3] * vertex_mul;
1253 facesw[face * 3 + 2] = face_points[0] * vertex_mul;
1254
1255 uvsw[face * 3 + 0] = u[2];
1256 uvsw[face * 3 + 1] = u[3];
1257 uvsw[face * 3 + 2] = u[0];
1258
1259 smoothw[face] = false;
1260 invertw[face] = invert_val;
1261 materialsw[face] = base_material;
1262
1263 face++;
1264 }
1265 }
1266
1267 if (face != face_count) {
1268 ERR_PRINT("Face mismatch bug! fix code");
1269 }
1270 }
1271
1272 new_brush->build_from_faces(faces, uvs, smooth, materials, invert);
1273
1274 return new_brush;
1275}
1276
1277void CSGBox3D::_bind_methods() {
1278 ClassDB::bind_method(D_METHOD("set_size", "size"), &CSGBox3D::set_size);
1279 ClassDB::bind_method(D_METHOD("get_size"), &CSGBox3D::get_size);
1280
1281 ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGBox3D::set_material);
1282 ClassDB::bind_method(D_METHOD("get_material"), &CSGBox3D::get_material);
1283
1284 ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size", PROPERTY_HINT_NONE, "suffix:m"), "set_size", "get_size");
1285 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial"), "set_material", "get_material");
1286}
1287
1288void CSGBox3D::set_size(const Vector3 &p_size) {
1289 size = p_size;
1290 _make_dirty();
1291 update_gizmos();
1292}
1293
1294Vector3 CSGBox3D::get_size() const {
1295 return size;
1296}
1297
1298#ifndef DISABLE_DEPRECATED
1299// Kept for compatibility from 3.x to 4.0.
1300bool CSGBox3D::_set(const StringName &p_name, const Variant &p_value) {
1301 if (p_name == "width") {
1302 size.x = p_value;
1303 _make_dirty();
1304 update_gizmos();
1305 return true;
1306 } else if (p_name == "height") {
1307 size.y = p_value;
1308 _make_dirty();
1309 update_gizmos();
1310 return true;
1311 } else if (p_name == "depth") {
1312 size.z = p_value;
1313 _make_dirty();
1314 update_gizmos();
1315 return true;
1316 } else {
1317 return false;
1318 }
1319}
1320#endif
1321
1322void CSGBox3D::set_material(const Ref<Material> &p_material) {
1323 material = p_material;
1324 _make_dirty();
1325 update_gizmos();
1326}
1327
1328Ref<Material> CSGBox3D::get_material() const {
1329 return material;
1330}
1331
1332///////////////
1333
1334CSGBrush *CSGCylinder3D::_build_brush() {
1335 // set our bounding box
1336
1337 CSGBrush *new_brush = memnew(CSGBrush);
1338
1339 int face_count = sides * (cone ? 1 : 2) + sides + (cone ? 0 : sides);
1340
1341 bool invert_val = get_flip_faces();
1342 Ref<Material> base_material = get_material();
1343
1344 Vector<Vector3> faces;
1345 Vector<Vector2> uvs;
1346 Vector<bool> smooth;
1347 Vector<Ref<Material>> materials;
1348 Vector<bool> invert;
1349
1350 faces.resize(face_count * 3);
1351 uvs.resize(face_count * 3);
1352
1353 smooth.resize(face_count);
1354 materials.resize(face_count);
1355 invert.resize(face_count);
1356
1357 {
1358 Vector3 *facesw = faces.ptrw();
1359 Vector2 *uvsw = uvs.ptrw();
1360 bool *smoothw = smooth.ptrw();
1361 Ref<Material> *materialsw = materials.ptrw();
1362 bool *invertw = invert.ptrw();
1363
1364 int face = 0;
1365
1366 Vector3 vertex_mul(radius, height * 0.5, radius);
1367
1368 {
1369 for (int i = 0; i < sides; i++) {
1370 float inc = float(i) / sides;
1371 float inc_n = float((i + 1)) / sides;
1372 if (i == sides - 1) {
1373 inc_n = 0;
1374 }
1375
1376 float ang = inc * Math_TAU;
1377 float ang_n = inc_n * Math_TAU;
1378
1379 Vector3 face_base(Math::cos(ang), 0, Math::sin(ang));
1380 Vector3 face_base_n(Math::cos(ang_n), 0, Math::sin(ang_n));
1381
1382 Vector3 face_points[4] = {
1383 face_base + Vector3(0, -1, 0),
1384 face_base_n + Vector3(0, -1, 0),
1385 face_base_n * (cone ? 0.0 : 1.0) + Vector3(0, 1, 0),
1386 face_base * (cone ? 0.0 : 1.0) + Vector3(0, 1, 0),
1387 };
1388
1389 Vector2 u[4] = {
1390 Vector2(inc, 0),
1391 Vector2(inc_n, 0),
1392 Vector2(inc_n, 1),
1393 Vector2(inc, 1),
1394 };
1395
1396 //side face 1
1397 facesw[face * 3 + 0] = face_points[0] * vertex_mul;
1398 facesw[face * 3 + 1] = face_points[1] * vertex_mul;
1399 facesw[face * 3 + 2] = face_points[2] * vertex_mul;
1400
1401 uvsw[face * 3 + 0] = u[0];
1402 uvsw[face * 3 + 1] = u[1];
1403 uvsw[face * 3 + 2] = u[2];
1404
1405 smoothw[face] = smooth_faces;
1406 invertw[face] = invert_val;
1407 materialsw[face] = base_material;
1408
1409 face++;
1410
1411 if (!cone) {
1412 //side face 2
1413 facesw[face * 3 + 0] = face_points[2] * vertex_mul;
1414 facesw[face * 3 + 1] = face_points[3] * vertex_mul;
1415 facesw[face * 3 + 2] = face_points[0] * vertex_mul;
1416
1417 uvsw[face * 3 + 0] = u[2];
1418 uvsw[face * 3 + 1] = u[3];
1419 uvsw[face * 3 + 2] = u[0];
1420
1421 smoothw[face] = smooth_faces;
1422 invertw[face] = invert_val;
1423 materialsw[face] = base_material;
1424 face++;
1425 }
1426
1427 //bottom face 1
1428 facesw[face * 3 + 0] = face_points[1] * vertex_mul;
1429 facesw[face * 3 + 1] = face_points[0] * vertex_mul;
1430 facesw[face * 3 + 2] = Vector3(0, -1, 0) * vertex_mul;
1431
1432 uvsw[face * 3 + 0] = Vector2(face_points[1].x, face_points[1].y) * 0.5 + Vector2(0.5, 0.5);
1433 uvsw[face * 3 + 1] = Vector2(face_points[0].x, face_points[0].y) * 0.5 + Vector2(0.5, 0.5);
1434 uvsw[face * 3 + 2] = Vector2(0.5, 0.5);
1435
1436 smoothw[face] = false;
1437 invertw[face] = invert_val;
1438 materialsw[face] = base_material;
1439 face++;
1440
1441 if (!cone) {
1442 //top face 1
1443 facesw[face * 3 + 0] = face_points[3] * vertex_mul;
1444 facesw[face * 3 + 1] = face_points[2] * vertex_mul;
1445 facesw[face * 3 + 2] = Vector3(0, 1, 0) * vertex_mul;
1446
1447 uvsw[face * 3 + 0] = Vector2(face_points[1].x, face_points[1].y) * 0.5 + Vector2(0.5, 0.5);
1448 uvsw[face * 3 + 1] = Vector2(face_points[0].x, face_points[0].y) * 0.5 + Vector2(0.5, 0.5);
1449 uvsw[face * 3 + 2] = Vector2(0.5, 0.5);
1450
1451 smoothw[face] = false;
1452 invertw[face] = invert_val;
1453 materialsw[face] = base_material;
1454 face++;
1455 }
1456 }
1457 }
1458
1459 if (face != face_count) {
1460 ERR_PRINT("Face mismatch bug! fix code");
1461 }
1462 }
1463
1464 new_brush->build_from_faces(faces, uvs, smooth, materials, invert);
1465
1466 return new_brush;
1467}
1468
1469void CSGCylinder3D::_bind_methods() {
1470 ClassDB::bind_method(D_METHOD("set_radius", "radius"), &CSGCylinder3D::set_radius);
1471 ClassDB::bind_method(D_METHOD("get_radius"), &CSGCylinder3D::get_radius);
1472
1473 ClassDB::bind_method(D_METHOD("set_height", "height"), &CSGCylinder3D::set_height);
1474 ClassDB::bind_method(D_METHOD("get_height"), &CSGCylinder3D::get_height);
1475
1476 ClassDB::bind_method(D_METHOD("set_sides", "sides"), &CSGCylinder3D::set_sides);
1477 ClassDB::bind_method(D_METHOD("get_sides"), &CSGCylinder3D::get_sides);
1478
1479 ClassDB::bind_method(D_METHOD("set_cone", "cone"), &CSGCylinder3D::set_cone);
1480 ClassDB::bind_method(D_METHOD("is_cone"), &CSGCylinder3D::is_cone);
1481
1482 ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGCylinder3D::set_material);
1483 ClassDB::bind_method(D_METHOD("get_material"), &CSGCylinder3D::get_material);
1484
1485 ClassDB::bind_method(D_METHOD("set_smooth_faces", "smooth_faces"), &CSGCylinder3D::set_smooth_faces);
1486 ClassDB::bind_method(D_METHOD("get_smooth_faces"), &CSGCylinder3D::get_smooth_faces);
1487
1488 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp,suffix:m"), "set_radius", "get_radius");
1489 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp,suffix:m"), "set_height", "get_height");
1490 ADD_PROPERTY(PropertyInfo(Variant::INT, "sides", PROPERTY_HINT_RANGE, "3,64,1"), "set_sides", "get_sides");
1491 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cone"), "set_cone", "is_cone");
1492 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces");
1493 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial"), "set_material", "get_material");
1494}
1495
1496void CSGCylinder3D::set_radius(const float p_radius) {
1497 radius = p_radius;
1498 _make_dirty();
1499 update_gizmos();
1500}
1501
1502float CSGCylinder3D::get_radius() const {
1503 return radius;
1504}
1505
1506void CSGCylinder3D::set_height(const float p_height) {
1507 height = p_height;
1508 _make_dirty();
1509 update_gizmos();
1510}
1511
1512float CSGCylinder3D::get_height() const {
1513 return height;
1514}
1515
1516void CSGCylinder3D::set_sides(const int p_sides) {
1517 ERR_FAIL_COND(p_sides < 3);
1518 sides = p_sides;
1519 _make_dirty();
1520 update_gizmos();
1521}
1522
1523int CSGCylinder3D::get_sides() const {
1524 return sides;
1525}
1526
1527void CSGCylinder3D::set_cone(const bool p_cone) {
1528 cone = p_cone;
1529 _make_dirty();
1530 update_gizmos();
1531}
1532
1533bool CSGCylinder3D::is_cone() const {
1534 return cone;
1535}
1536
1537void CSGCylinder3D::set_smooth_faces(const bool p_smooth_faces) {
1538 smooth_faces = p_smooth_faces;
1539 _make_dirty();
1540}
1541
1542bool CSGCylinder3D::get_smooth_faces() const {
1543 return smooth_faces;
1544}
1545
1546void CSGCylinder3D::set_material(const Ref<Material> &p_material) {
1547 material = p_material;
1548 _make_dirty();
1549}
1550
1551Ref<Material> CSGCylinder3D::get_material() const {
1552 return material;
1553}
1554
1555CSGCylinder3D::CSGCylinder3D() {
1556 // defaults
1557 radius = 0.5;
1558 height = 2.0;
1559 sides = 8;
1560 cone = false;
1561 smooth_faces = true;
1562}
1563
1564///////////////
1565
1566CSGBrush *CSGTorus3D::_build_brush() {
1567 // set our bounding box
1568
1569 float min_radius = inner_radius;
1570 float max_radius = outer_radius;
1571
1572 if (min_radius == max_radius) {
1573 return memnew(CSGBrush); //sorry, can't
1574 }
1575
1576 if (min_radius > max_radius) {
1577 SWAP(min_radius, max_radius);
1578 }
1579
1580 float radius = (max_radius - min_radius) * 0.5;
1581
1582 CSGBrush *new_brush = memnew(CSGBrush);
1583
1584 int face_count = ring_sides * sides * 2;
1585
1586 bool invert_val = get_flip_faces();
1587 Ref<Material> base_material = get_material();
1588
1589 Vector<Vector3> faces;
1590 Vector<Vector2> uvs;
1591 Vector<bool> smooth;
1592 Vector<Ref<Material>> materials;
1593 Vector<bool> invert;
1594
1595 faces.resize(face_count * 3);
1596 uvs.resize(face_count * 3);
1597
1598 smooth.resize(face_count);
1599 materials.resize(face_count);
1600 invert.resize(face_count);
1601
1602 {
1603 Vector3 *facesw = faces.ptrw();
1604 Vector2 *uvsw = uvs.ptrw();
1605 bool *smoothw = smooth.ptrw();
1606 Ref<Material> *materialsw = materials.ptrw();
1607 bool *invertw = invert.ptrw();
1608
1609 int face = 0;
1610
1611 {
1612 for (int i = 0; i < sides; i++) {
1613 float inci = float(i) / sides;
1614 float inci_n = float((i + 1)) / sides;
1615 if (i == sides - 1) {
1616 inci_n = 0;
1617 }
1618
1619 float angi = inci * Math_TAU;
1620 float angi_n = inci_n * Math_TAU;
1621
1622 Vector3 normali = Vector3(Math::cos(angi), 0, Math::sin(angi));
1623 Vector3 normali_n = Vector3(Math::cos(angi_n), 0, Math::sin(angi_n));
1624
1625 for (int j = 0; j < ring_sides; j++) {
1626 float incj = float(j) / ring_sides;
1627 float incj_n = float((j + 1)) / ring_sides;
1628 if (j == ring_sides - 1) {
1629 incj_n = 0;
1630 }
1631
1632 float angj = incj * Math_TAU;
1633 float angj_n = incj_n * Math_TAU;
1634
1635 Vector2 normalj = Vector2(Math::cos(angj), Math::sin(angj)) * radius + Vector2(min_radius + radius, 0);
1636 Vector2 normalj_n = Vector2(Math::cos(angj_n), Math::sin(angj_n)) * radius + Vector2(min_radius + radius, 0);
1637
1638 Vector3 face_points[4] = {
1639 Vector3(normali.x * normalj.x, normalj.y, normali.z * normalj.x),
1640 Vector3(normali.x * normalj_n.x, normalj_n.y, normali.z * normalj_n.x),
1641 Vector3(normali_n.x * normalj_n.x, normalj_n.y, normali_n.z * normalj_n.x),
1642 Vector3(normali_n.x * normalj.x, normalj.y, normali_n.z * normalj.x)
1643 };
1644
1645 Vector2 u[4] = {
1646 Vector2(inci, incj),
1647 Vector2(inci, incj_n),
1648 Vector2(inci_n, incj_n),
1649 Vector2(inci_n, incj),
1650 };
1651
1652 // face 1
1653 facesw[face * 3 + 0] = face_points[0];
1654 facesw[face * 3 + 1] = face_points[2];
1655 facesw[face * 3 + 2] = face_points[1];
1656
1657 uvsw[face * 3 + 0] = u[0];
1658 uvsw[face * 3 + 1] = u[2];
1659 uvsw[face * 3 + 2] = u[1];
1660
1661 smoothw[face] = smooth_faces;
1662 invertw[face] = invert_val;
1663 materialsw[face] = base_material;
1664
1665 face++;
1666
1667 //face 2
1668 facesw[face * 3 + 0] = face_points[3];
1669 facesw[face * 3 + 1] = face_points[2];
1670 facesw[face * 3 + 2] = face_points[0];
1671
1672 uvsw[face * 3 + 0] = u[3];
1673 uvsw[face * 3 + 1] = u[2];
1674 uvsw[face * 3 + 2] = u[0];
1675
1676 smoothw[face] = smooth_faces;
1677 invertw[face] = invert_val;
1678 materialsw[face] = base_material;
1679 face++;
1680 }
1681 }
1682 }
1683
1684 if (face != face_count) {
1685 ERR_PRINT("Face mismatch bug! fix code");
1686 }
1687 }
1688
1689 new_brush->build_from_faces(faces, uvs, smooth, materials, invert);
1690
1691 return new_brush;
1692}
1693
1694void CSGTorus3D::_bind_methods() {
1695 ClassDB::bind_method(D_METHOD("set_inner_radius", "radius"), &CSGTorus3D::set_inner_radius);
1696 ClassDB::bind_method(D_METHOD("get_inner_radius"), &CSGTorus3D::get_inner_radius);
1697
1698 ClassDB::bind_method(D_METHOD("set_outer_radius", "radius"), &CSGTorus3D::set_outer_radius);
1699 ClassDB::bind_method(D_METHOD("get_outer_radius"), &CSGTorus3D::get_outer_radius);
1700
1701 ClassDB::bind_method(D_METHOD("set_sides", "sides"), &CSGTorus3D::set_sides);
1702 ClassDB::bind_method(D_METHOD("get_sides"), &CSGTorus3D::get_sides);
1703
1704 ClassDB::bind_method(D_METHOD("set_ring_sides", "sides"), &CSGTorus3D::set_ring_sides);
1705 ClassDB::bind_method(D_METHOD("get_ring_sides"), &CSGTorus3D::get_ring_sides);
1706
1707 ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGTorus3D::set_material);
1708 ClassDB::bind_method(D_METHOD("get_material"), &CSGTorus3D::get_material);
1709
1710 ClassDB::bind_method(D_METHOD("set_smooth_faces", "smooth_faces"), &CSGTorus3D::set_smooth_faces);
1711 ClassDB::bind_method(D_METHOD("get_smooth_faces"), &CSGTorus3D::get_smooth_faces);
1712
1713 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "inner_radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp,suffix:m"), "set_inner_radius", "get_inner_radius");
1714 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "outer_radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp,suffix:m"), "set_outer_radius", "get_outer_radius");
1715 ADD_PROPERTY(PropertyInfo(Variant::INT, "sides", PROPERTY_HINT_RANGE, "3,64,1"), "set_sides", "get_sides");
1716 ADD_PROPERTY(PropertyInfo(Variant::INT, "ring_sides", PROPERTY_HINT_RANGE, "3,64,1"), "set_ring_sides", "get_ring_sides");
1717 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces");
1718 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial"), "set_material", "get_material");
1719}
1720
1721void CSGTorus3D::set_inner_radius(const float p_inner_radius) {
1722 inner_radius = p_inner_radius;
1723 _make_dirty();
1724 update_gizmos();
1725}
1726
1727float CSGTorus3D::get_inner_radius() const {
1728 return inner_radius;
1729}
1730
1731void CSGTorus3D::set_outer_radius(const float p_outer_radius) {
1732 outer_radius = p_outer_radius;
1733 _make_dirty();
1734 update_gizmos();
1735}
1736
1737float CSGTorus3D::get_outer_radius() const {
1738 return outer_radius;
1739}
1740
1741void CSGTorus3D::set_sides(const int p_sides) {
1742 ERR_FAIL_COND(p_sides < 3);
1743 sides = p_sides;
1744 _make_dirty();
1745 update_gizmos();
1746}
1747
1748int CSGTorus3D::get_sides() const {
1749 return sides;
1750}
1751
1752void CSGTorus3D::set_ring_sides(const int p_ring_sides) {
1753 ERR_FAIL_COND(p_ring_sides < 3);
1754 ring_sides = p_ring_sides;
1755 _make_dirty();
1756 update_gizmos();
1757}
1758
1759int CSGTorus3D::get_ring_sides() const {
1760 return ring_sides;
1761}
1762
1763void CSGTorus3D::set_smooth_faces(const bool p_smooth_faces) {
1764 smooth_faces = p_smooth_faces;
1765 _make_dirty();
1766}
1767
1768bool CSGTorus3D::get_smooth_faces() const {
1769 return smooth_faces;
1770}
1771
1772void CSGTorus3D::set_material(const Ref<Material> &p_material) {
1773 material = p_material;
1774 _make_dirty();
1775}
1776
1777Ref<Material> CSGTorus3D::get_material() const {
1778 return material;
1779}
1780
1781CSGTorus3D::CSGTorus3D() {
1782 // defaults
1783 inner_radius = 0.5;
1784 outer_radius = 1.0;
1785 sides = 8;
1786 ring_sides = 6;
1787 smooth_faces = true;
1788}
1789
1790///////////////
1791
1792CSGBrush *CSGPolygon3D::_build_brush() {
1793 CSGBrush *new_brush = memnew(CSGBrush);
1794
1795 if (polygon.size() < 3) {
1796 return new_brush;
1797 }
1798
1799 // Triangulate polygon shape.
1800 Vector<Point2> shape_polygon = polygon;
1801 if (Triangulate::get_area(shape_polygon) > 0) {
1802 shape_polygon.reverse();
1803 }
1804 int shape_sides = shape_polygon.size();
1805 Vector<int> shape_faces = Geometry2D::triangulate_polygon(shape_polygon);
1806 ERR_FAIL_COND_V_MSG(shape_faces.size() < 3, new_brush, "Failed to triangulate CSGPolygon. Make sure the polygon doesn't have any intersecting edges.");
1807
1808 // Get polygon enclosing Rect2.
1809 Rect2 shape_rect(shape_polygon[0], Vector2());
1810 for (int i = 1; i < shape_sides; i++) {
1811 shape_rect.expand_to(shape_polygon[i]);
1812 }
1813
1814 // If MODE_PATH, check if curve has changed.
1815 Ref<Curve3D> curve;
1816 if (mode == MODE_PATH) {
1817 Path3D *current_path = Object::cast_to<Path3D>(get_node_or_null(path_node));
1818 if (path != current_path) {
1819 if (path) {
1820 path->disconnect("tree_exited", callable_mp(this, &CSGPolygon3D::_path_exited));
1821 path->disconnect("curve_changed", callable_mp(this, &CSGPolygon3D::_path_changed));
1822 }
1823 path = current_path;
1824 if (path) {
1825 path->connect("tree_exited", callable_mp(this, &CSGPolygon3D::_path_exited));
1826 path->connect("curve_changed", callable_mp(this, &CSGPolygon3D::_path_changed));
1827 }
1828 }
1829
1830 if (!path) {
1831 return new_brush;
1832 }
1833
1834 curve = path->get_curve();
1835 if (curve.is_null() || curve->get_point_count() < 2) {
1836 return new_brush;
1837 }
1838 }
1839
1840 // Calculate the number extrusions, ends and faces.
1841 int extrusions = 0;
1842 int extrusion_face_count = shape_sides * 2;
1843 int end_count = 0;
1844 int shape_face_count = shape_faces.size() / 3;
1845 real_t curve_length = 1.0;
1846 switch (mode) {
1847 case MODE_DEPTH:
1848 extrusions = 1;
1849 end_count = 2;
1850 break;
1851 case MODE_SPIN:
1852 extrusions = spin_sides;
1853 if (spin_degrees < 360) {
1854 end_count = 2;
1855 }
1856 break;
1857 case MODE_PATH: {
1858 curve_length = curve->get_baked_length();
1859 if (path_interval_type == PATH_INTERVAL_DISTANCE) {
1860 extrusions = MAX(1, Math::ceil(curve_length / path_interval)) + 1;
1861 } else {
1862 extrusions = Math::ceil(1.0 * curve->get_point_count() / path_interval);
1863 }
1864 if (!path_joined) {
1865 end_count = 2;
1866 extrusions -= 1;
1867 }
1868 } break;
1869 }
1870 int face_count = extrusions * extrusion_face_count + end_count * shape_face_count;
1871
1872 // Initialize variables used to create the mesh.
1873 Ref<Material> base_material = get_material();
1874
1875 Vector<Vector3> faces;
1876 Vector<Vector2> uvs;
1877 Vector<bool> smooth;
1878 Vector<Ref<Material>> materials;
1879 Vector<bool> invert;
1880
1881 faces.resize(face_count * 3);
1882 uvs.resize(face_count * 3);
1883 smooth.resize(face_count);
1884 materials.resize(face_count);
1885 invert.resize(face_count);
1886 int faces_removed = 0;
1887
1888 {
1889 Vector3 *facesw = faces.ptrw();
1890 Vector2 *uvsw = uvs.ptrw();
1891 bool *smoothw = smooth.ptrw();
1892 Ref<Material> *materialsw = materials.ptrw();
1893 bool *invertw = invert.ptrw();
1894
1895 int face = 0;
1896 Transform3D base_xform;
1897 Transform3D current_xform;
1898 Transform3D previous_xform;
1899 Transform3D previous_previous_xform;
1900 double u_step = 1.0 / extrusions;
1901 if (path_u_distance > 0.0) {
1902 u_step *= curve_length / path_u_distance;
1903 }
1904 double v_step = 1.0 / shape_sides;
1905 double spin_step = Math::deg_to_rad(spin_degrees / spin_sides);
1906 double extrusion_step = 1.0 / extrusions;
1907 if (mode == MODE_PATH) {
1908 if (path_joined) {
1909 extrusion_step = 1.0 / (extrusions - 1);
1910 }
1911 extrusion_step *= curve_length;
1912 }
1913
1914 if (mode == MODE_PATH) {
1915 if (!path_local) {
1916 base_xform = path->get_global_transform();
1917 }
1918
1919 Vector3 current_point = curve->sample_baked(0);
1920 Vector3 next_point = curve->sample_baked(extrusion_step);
1921 Vector3 current_up = Vector3(0, 1, 0);
1922 Vector3 direction = next_point - current_point;
1923
1924 if (path_joined) {
1925 Vector3 last_point = curve->sample_baked(curve->get_baked_length());
1926 direction = next_point - last_point;
1927 }
1928
1929 switch (path_rotation) {
1930 case PATH_ROTATION_POLYGON:
1931 direction = Vector3(0, 0, -1);
1932 break;
1933 case PATH_ROTATION_PATH:
1934 break;
1935 case PATH_ROTATION_PATH_FOLLOW:
1936 current_up = curve->sample_baked_up_vector(0, true);
1937 break;
1938 }
1939
1940 Transform3D facing = Transform3D().looking_at(direction, current_up);
1941 current_xform = base_xform.translated_local(current_point) * facing;
1942 }
1943
1944 // Create the mesh.
1945 if (end_count > 0) {
1946 // Add front end face.
1947 for (int face_idx = 0; face_idx < shape_face_count; face_idx++) {
1948 for (int face_vertex_idx = 0; face_vertex_idx < 3; face_vertex_idx++) {
1949 // We need to reverse the rotation of the shape face vertices.
1950 int index = shape_faces[face_idx * 3 + 2 - face_vertex_idx];
1951 Point2 p = shape_polygon[index];
1952 Point2 uv = (p - shape_rect.position) / shape_rect.size;
1953
1954 // Use the left side of the bottom half of the y-inverted texture.
1955 uv.x = uv.x / 2;
1956 uv.y = 1 - (uv.y / 2);
1957
1958 facesw[face * 3 + face_vertex_idx] = current_xform.xform(Vector3(p.x, p.y, 0));
1959 uvsw[face * 3 + face_vertex_idx] = uv;
1960 }
1961
1962 smoothw[face] = false;
1963 materialsw[face] = base_material;
1964 invertw[face] = flip_faces;
1965 face++;
1966 }
1967 }
1968
1969 real_t angle_simplify_dot = Math::cos(Math::deg_to_rad(path_simplify_angle));
1970 Vector3 previous_simplify_dir = Vector3(0, 0, 0);
1971 int faces_combined = 0;
1972
1973 // Add extrusion faces.
1974 for (int x0 = 0; x0 < extrusions; x0++) {
1975 previous_previous_xform = previous_xform;
1976 previous_xform = current_xform;
1977
1978 switch (mode) {
1979 case MODE_DEPTH: {
1980 current_xform.translate_local(Vector3(0, 0, -depth));
1981 } break;
1982 case MODE_SPIN: {
1983 current_xform.rotate(Vector3(0, 1, 0), spin_step);
1984 } break;
1985 case MODE_PATH: {
1986 double previous_offset = x0 * extrusion_step;
1987 double current_offset = (x0 + 1) * extrusion_step;
1988 double next_offset = (x0 + 2) * extrusion_step;
1989 if (x0 == extrusions - 1) {
1990 if (path_joined) {
1991 current_offset = 0;
1992 next_offset = extrusion_step;
1993 } else {
1994 next_offset = current_offset;
1995 }
1996 }
1997
1998 Vector3 previous_point = curve->sample_baked(previous_offset);
1999 Vector3 current_point = curve->sample_baked(current_offset);
2000 Vector3 next_point = curve->sample_baked(next_offset);
2001 Vector3 current_up = Vector3(0, 1, 0);
2002 Vector3 direction = next_point - previous_point;
2003 Vector3 current_dir = (current_point - previous_point).normalized();
2004
2005 // If the angles are similar, remove the previous face and replace it with this one.
2006 if (path_simplify_angle > 0.0 && x0 > 0 && previous_simplify_dir.dot(current_dir) > angle_simplify_dot) {
2007 faces_combined += 1;
2008 previous_xform = previous_previous_xform;
2009 face -= extrusion_face_count;
2010 faces_removed += extrusion_face_count;
2011 } else {
2012 faces_combined = 0;
2013 previous_simplify_dir = current_dir;
2014 }
2015
2016 switch (path_rotation) {
2017 case PATH_ROTATION_POLYGON:
2018 direction = Vector3(0, 0, -1);
2019 break;
2020 case PATH_ROTATION_PATH:
2021 break;
2022 case PATH_ROTATION_PATH_FOLLOW:
2023 current_up = curve->sample_baked_up_vector(current_offset, true);
2024 break;
2025 }
2026
2027 Transform3D facing = Transform3D().looking_at(direction, current_up);
2028 current_xform = base_xform.translated_local(current_point) * facing;
2029 } break;
2030 }
2031
2032 double u0 = (x0 - faces_combined) * u_step;
2033 double u1 = ((x0 + 1) * u_step);
2034 if (mode == MODE_PATH && !path_continuous_u) {
2035 u0 = 0.0;
2036 u1 = 1.0;
2037 }
2038
2039 for (int y0 = 0; y0 < shape_sides; y0++) {
2040 int y1 = (y0 + 1) % shape_sides;
2041 // Use the top half of the texture.
2042 double v0 = (y0 * v_step) / 2;
2043 double v1 = ((y0 + 1) * v_step) / 2;
2044
2045 Vector3 v[4] = {
2046 previous_xform.xform(Vector3(shape_polygon[y0].x, shape_polygon[y0].y, 0)),
2047 current_xform.xform(Vector3(shape_polygon[y0].x, shape_polygon[y0].y, 0)),
2048 current_xform.xform(Vector3(shape_polygon[y1].x, shape_polygon[y1].y, 0)),
2049 previous_xform.xform(Vector3(shape_polygon[y1].x, shape_polygon[y1].y, 0)),
2050 };
2051
2052 Vector2 u[4] = {
2053 Vector2(u0, v0),
2054 Vector2(u1, v0),
2055 Vector2(u1, v1),
2056 Vector2(u0, v1),
2057 };
2058
2059 // Face 1
2060 facesw[face * 3 + 0] = v[0];
2061 facesw[face * 3 + 1] = v[1];
2062 facesw[face * 3 + 2] = v[2];
2063
2064 uvsw[face * 3 + 0] = u[0];
2065 uvsw[face * 3 + 1] = u[1];
2066 uvsw[face * 3 + 2] = u[2];
2067
2068 smoothw[face] = smooth_faces;
2069 invertw[face] = flip_faces;
2070 materialsw[face] = base_material;
2071
2072 face++;
2073
2074 // Face 2
2075 facesw[face * 3 + 0] = v[2];
2076 facesw[face * 3 + 1] = v[3];
2077 facesw[face * 3 + 2] = v[0];
2078
2079 uvsw[face * 3 + 0] = u[2];
2080 uvsw[face * 3 + 1] = u[3];
2081 uvsw[face * 3 + 2] = u[0];
2082
2083 smoothw[face] = smooth_faces;
2084 invertw[face] = flip_faces;
2085 materialsw[face] = base_material;
2086
2087 face++;
2088 }
2089 }
2090
2091 if (end_count > 1) {
2092 // Add back end face.
2093 for (int face_idx = 0; face_idx < shape_face_count; face_idx++) {
2094 for (int face_vertex_idx = 0; face_vertex_idx < 3; face_vertex_idx++) {
2095 int index = shape_faces[face_idx * 3 + face_vertex_idx];
2096 Point2 p = shape_polygon[index];
2097 Point2 uv = (p - shape_rect.position) / shape_rect.size;
2098
2099 // Use the x-inverted ride side of the bottom half of the y-inverted texture.
2100 uv.x = 1 - uv.x / 2;
2101 uv.y = 1 - (uv.y / 2);
2102
2103 facesw[face * 3 + face_vertex_idx] = current_xform.xform(Vector3(p.x, p.y, 0));
2104 uvsw[face * 3 + face_vertex_idx] = uv;
2105 }
2106
2107 smoothw[face] = false;
2108 materialsw[face] = base_material;
2109 invertw[face] = flip_faces;
2110 face++;
2111 }
2112 }
2113
2114 face_count -= faces_removed;
2115 ERR_FAIL_COND_V_MSG(face != face_count, new_brush, "Bug: Failed to create the CSGPolygon mesh correctly.");
2116 }
2117
2118 if (faces_removed > 0) {
2119 faces.resize(face_count * 3);
2120 uvs.resize(face_count * 3);
2121 smooth.resize(face_count);
2122 materials.resize(face_count);
2123 invert.resize(face_count);
2124 }
2125
2126 new_brush->build_from_faces(faces, uvs, smooth, materials, invert);
2127
2128 return new_brush;
2129}
2130
2131void CSGPolygon3D::_notification(int p_what) {
2132 if (p_what == NOTIFICATION_EXIT_TREE) {
2133 if (path) {
2134 path->disconnect("tree_exited", callable_mp(this, &CSGPolygon3D::_path_exited));
2135 path->disconnect("curve_changed", callable_mp(this, &CSGPolygon3D::_path_changed));
2136 path = nullptr;
2137 }
2138 }
2139}
2140
2141void CSGPolygon3D::_validate_property(PropertyInfo &p_property) const {
2142 if (p_property.name.begins_with("spin") && mode != MODE_SPIN) {
2143 p_property.usage = PROPERTY_USAGE_NONE;
2144 }
2145 if (p_property.name.begins_with("path") && mode != MODE_PATH) {
2146 p_property.usage = PROPERTY_USAGE_NONE;
2147 }
2148 if (p_property.name == "depth" && mode != MODE_DEPTH) {
2149 p_property.usage = PROPERTY_USAGE_NONE;
2150 }
2151}
2152
2153void CSGPolygon3D::_path_changed() {
2154 _make_dirty();
2155 update_gizmos();
2156}
2157
2158void CSGPolygon3D::_path_exited() {
2159 path = nullptr;
2160}
2161
2162void CSGPolygon3D::_bind_methods() {
2163 ClassDB::bind_method(D_METHOD("set_polygon", "polygon"), &CSGPolygon3D::set_polygon);
2164 ClassDB::bind_method(D_METHOD("get_polygon"), &CSGPolygon3D::get_polygon);
2165
2166 ClassDB::bind_method(D_METHOD("set_mode", "mode"), &CSGPolygon3D::set_mode);
2167 ClassDB::bind_method(D_METHOD("get_mode"), &CSGPolygon3D::get_mode);
2168
2169 ClassDB::bind_method(D_METHOD("set_depth", "depth"), &CSGPolygon3D::set_depth);
2170 ClassDB::bind_method(D_METHOD("get_depth"), &CSGPolygon3D::get_depth);
2171
2172 ClassDB::bind_method(D_METHOD("set_spin_degrees", "degrees"), &CSGPolygon3D::set_spin_degrees);
2173 ClassDB::bind_method(D_METHOD("get_spin_degrees"), &CSGPolygon3D::get_spin_degrees);
2174
2175 ClassDB::bind_method(D_METHOD("set_spin_sides", "spin_sides"), &CSGPolygon3D::set_spin_sides);
2176 ClassDB::bind_method(D_METHOD("get_spin_sides"), &CSGPolygon3D::get_spin_sides);
2177
2178 ClassDB::bind_method(D_METHOD("set_path_node", "path"), &CSGPolygon3D::set_path_node);
2179 ClassDB::bind_method(D_METHOD("get_path_node"), &CSGPolygon3D::get_path_node);
2180
2181 ClassDB::bind_method(D_METHOD("set_path_interval_type", "interval_type"), &CSGPolygon3D::set_path_interval_type);
2182 ClassDB::bind_method(D_METHOD("get_path_interval_type"), &CSGPolygon3D::get_path_interval_type);
2183
2184 ClassDB::bind_method(D_METHOD("set_path_interval", "interval"), &CSGPolygon3D::set_path_interval);
2185 ClassDB::bind_method(D_METHOD("get_path_interval"), &CSGPolygon3D::get_path_interval);
2186
2187 ClassDB::bind_method(D_METHOD("set_path_simplify_angle", "degrees"), &CSGPolygon3D::set_path_simplify_angle);
2188 ClassDB::bind_method(D_METHOD("get_path_simplify_angle"), &CSGPolygon3D::get_path_simplify_angle);
2189
2190 ClassDB::bind_method(D_METHOD("set_path_rotation", "path_rotation"), &CSGPolygon3D::set_path_rotation);
2191 ClassDB::bind_method(D_METHOD("get_path_rotation"), &CSGPolygon3D::get_path_rotation);
2192
2193 ClassDB::bind_method(D_METHOD("set_path_local", "enable"), &CSGPolygon3D::set_path_local);
2194 ClassDB::bind_method(D_METHOD("is_path_local"), &CSGPolygon3D::is_path_local);
2195
2196 ClassDB::bind_method(D_METHOD("set_path_continuous_u", "enable"), &CSGPolygon3D::set_path_continuous_u);
2197 ClassDB::bind_method(D_METHOD("is_path_continuous_u"), &CSGPolygon3D::is_path_continuous_u);
2198
2199 ClassDB::bind_method(D_METHOD("set_path_u_distance", "distance"), &CSGPolygon3D::set_path_u_distance);
2200 ClassDB::bind_method(D_METHOD("get_path_u_distance"), &CSGPolygon3D::get_path_u_distance);
2201
2202 ClassDB::bind_method(D_METHOD("set_path_joined", "enable"), &CSGPolygon3D::set_path_joined);
2203 ClassDB::bind_method(D_METHOD("is_path_joined"), &CSGPolygon3D::is_path_joined);
2204
2205 ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGPolygon3D::set_material);
2206 ClassDB::bind_method(D_METHOD("get_material"), &CSGPolygon3D::get_material);
2207
2208 ClassDB::bind_method(D_METHOD("set_smooth_faces", "smooth_faces"), &CSGPolygon3D::set_smooth_faces);
2209 ClassDB::bind_method(D_METHOD("get_smooth_faces"), &CSGPolygon3D::get_smooth_faces);
2210
2211 ClassDB::bind_method(D_METHOD("_is_editable_3d_polygon"), &CSGPolygon3D::_is_editable_3d_polygon);
2212 ClassDB::bind_method(D_METHOD("_has_editable_3d_polygon_no_depth"), &CSGPolygon3D::_has_editable_3d_polygon_no_depth);
2213
2214 ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "polygon"), "set_polygon", "get_polygon");
2215 ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Depth,Spin,Path"), "set_mode", "get_mode");
2216 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth", PROPERTY_HINT_RANGE, "0.01,100.0,0.01,or_greater,exp,suffix:m"), "set_depth", "get_depth");
2217 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "spin_degrees", PROPERTY_HINT_RANGE, "1,360,0.1"), "set_spin_degrees", "get_spin_degrees");
2218 ADD_PROPERTY(PropertyInfo(Variant::INT, "spin_sides", PROPERTY_HINT_RANGE, "3,64,1"), "set_spin_sides", "get_spin_sides");
2219 ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "path_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Path3D"), "set_path_node", "get_path_node");
2220 ADD_PROPERTY(PropertyInfo(Variant::INT, "path_interval_type", PROPERTY_HINT_ENUM, "Distance,Subdivide"), "set_path_interval_type", "get_path_interval_type");
2221 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_interval", PROPERTY_HINT_RANGE, "0.01,1.0,0.01,exp,or_greater"), "set_path_interval", "get_path_interval");
2222 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_simplify_angle", PROPERTY_HINT_RANGE, "0.0,180.0,0.1,exp"), "set_path_simplify_angle", "get_path_simplify_angle");
2223 ADD_PROPERTY(PropertyInfo(Variant::INT, "path_rotation", PROPERTY_HINT_ENUM, "Polygon,Path,PathFollow"), "set_path_rotation", "get_path_rotation");
2224 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "path_local"), "set_path_local", "is_path_local");
2225 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "path_continuous_u"), "set_path_continuous_u", "is_path_continuous_u");
2226 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_u_distance", PROPERTY_HINT_RANGE, "0.0,10.0,0.01,or_greater,suffix:m"), "set_path_u_distance", "get_path_u_distance");
2227 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "path_joined"), "set_path_joined", "is_path_joined");
2228 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces");
2229 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial"), "set_material", "get_material");
2230
2231 BIND_ENUM_CONSTANT(MODE_DEPTH);
2232 BIND_ENUM_CONSTANT(MODE_SPIN);
2233 BIND_ENUM_CONSTANT(MODE_PATH);
2234
2235 BIND_ENUM_CONSTANT(PATH_ROTATION_POLYGON);
2236 BIND_ENUM_CONSTANT(PATH_ROTATION_PATH);
2237 BIND_ENUM_CONSTANT(PATH_ROTATION_PATH_FOLLOW);
2238
2239 BIND_ENUM_CONSTANT(PATH_INTERVAL_DISTANCE);
2240 BIND_ENUM_CONSTANT(PATH_INTERVAL_SUBDIVIDE);
2241}
2242
2243void CSGPolygon3D::set_polygon(const Vector<Vector2> &p_polygon) {
2244 polygon = p_polygon;
2245 _make_dirty();
2246 update_gizmos();
2247}
2248
2249Vector<Vector2> CSGPolygon3D::get_polygon() const {
2250 return polygon;
2251}
2252
2253void CSGPolygon3D::set_mode(Mode p_mode) {
2254 mode = p_mode;
2255 _make_dirty();
2256 update_gizmos();
2257 notify_property_list_changed();
2258}
2259
2260CSGPolygon3D::Mode CSGPolygon3D::get_mode() const {
2261 return mode;
2262}
2263
2264void CSGPolygon3D::set_depth(const float p_depth) {
2265 ERR_FAIL_COND(p_depth < 0.001);
2266 depth = p_depth;
2267 _make_dirty();
2268 update_gizmos();
2269}
2270
2271float CSGPolygon3D::get_depth() const {
2272 return depth;
2273}
2274
2275void CSGPolygon3D::set_path_continuous_u(bool p_enable) {
2276 path_continuous_u = p_enable;
2277 _make_dirty();
2278}
2279
2280bool CSGPolygon3D::is_path_continuous_u() const {
2281 return path_continuous_u;
2282}
2283
2284void CSGPolygon3D::set_path_u_distance(real_t p_path_u_distance) {
2285 path_u_distance = p_path_u_distance;
2286 _make_dirty();
2287 update_gizmos();
2288}
2289
2290real_t CSGPolygon3D::get_path_u_distance() const {
2291 return path_u_distance;
2292}
2293
2294void CSGPolygon3D::set_spin_degrees(const float p_spin_degrees) {
2295 ERR_FAIL_COND(p_spin_degrees < 0.01 || p_spin_degrees > 360);
2296 spin_degrees = p_spin_degrees;
2297 _make_dirty();
2298 update_gizmos();
2299}
2300
2301float CSGPolygon3D::get_spin_degrees() const {
2302 return spin_degrees;
2303}
2304
2305void CSGPolygon3D::set_spin_sides(int p_spin_sides) {
2306 ERR_FAIL_COND(p_spin_sides < 3);
2307 spin_sides = p_spin_sides;
2308 _make_dirty();
2309 update_gizmos();
2310}
2311
2312int CSGPolygon3D::get_spin_sides() const {
2313 return spin_sides;
2314}
2315
2316void CSGPolygon3D::set_path_node(const NodePath &p_path) {
2317 path_node = p_path;
2318 _make_dirty();
2319 update_gizmos();
2320}
2321
2322NodePath CSGPolygon3D::get_path_node() const {
2323 return path_node;
2324}
2325
2326void CSGPolygon3D::set_path_interval_type(PathIntervalType p_interval_type) {
2327 path_interval_type = p_interval_type;
2328 _make_dirty();
2329 update_gizmos();
2330}
2331
2332CSGPolygon3D::PathIntervalType CSGPolygon3D::get_path_interval_type() const {
2333 return path_interval_type;
2334}
2335
2336void CSGPolygon3D::set_path_interval(float p_interval) {
2337 path_interval = p_interval;
2338 _make_dirty();
2339 update_gizmos();
2340}
2341
2342float CSGPolygon3D::get_path_interval() const {
2343 return path_interval;
2344}
2345
2346void CSGPolygon3D::set_path_simplify_angle(float p_angle) {
2347 path_simplify_angle = p_angle;
2348 _make_dirty();
2349 update_gizmos();
2350}
2351
2352float CSGPolygon3D::get_path_simplify_angle() const {
2353 return path_simplify_angle;
2354}
2355
2356void CSGPolygon3D::set_path_rotation(PathRotation p_rotation) {
2357 path_rotation = p_rotation;
2358 _make_dirty();
2359 update_gizmos();
2360}
2361
2362CSGPolygon3D::PathRotation CSGPolygon3D::get_path_rotation() const {
2363 return path_rotation;
2364}
2365
2366void CSGPolygon3D::set_path_local(bool p_enable) {
2367 path_local = p_enable;
2368 _make_dirty();
2369 update_gizmos();
2370}
2371
2372bool CSGPolygon3D::is_path_local() const {
2373 return path_local;
2374}
2375
2376void CSGPolygon3D::set_path_joined(bool p_enable) {
2377 path_joined = p_enable;
2378 _make_dirty();
2379 update_gizmos();
2380}
2381
2382bool CSGPolygon3D::is_path_joined() const {
2383 return path_joined;
2384}
2385
2386void CSGPolygon3D::set_smooth_faces(const bool p_smooth_faces) {
2387 smooth_faces = p_smooth_faces;
2388 _make_dirty();
2389}
2390
2391bool CSGPolygon3D::get_smooth_faces() const {
2392 return smooth_faces;
2393}
2394
2395void CSGPolygon3D::set_material(const Ref<Material> &p_material) {
2396 material = p_material;
2397 _make_dirty();
2398}
2399
2400Ref<Material> CSGPolygon3D::get_material() const {
2401 return material;
2402}
2403
2404bool CSGPolygon3D::_is_editable_3d_polygon() const {
2405 return true;
2406}
2407
2408bool CSGPolygon3D::_has_editable_3d_polygon_no_depth() const {
2409 return true;
2410}
2411
2412CSGPolygon3D::CSGPolygon3D() {
2413 // defaults
2414 mode = MODE_DEPTH;
2415 polygon.push_back(Vector2(0, 0));
2416 polygon.push_back(Vector2(0, 1));
2417 polygon.push_back(Vector2(1, 1));
2418 polygon.push_back(Vector2(1, 0));
2419 depth = 1.0;
2420 spin_degrees = 360;
2421 spin_sides = 8;
2422 smooth_faces = false;
2423 path_interval_type = PATH_INTERVAL_DISTANCE;
2424 path_interval = 1.0;
2425 path_simplify_angle = 0.0;
2426 path_rotation = PATH_ROTATION_PATH_FOLLOW;
2427 path_local = false;
2428 path_continuous_u = true;
2429 path_u_distance = 1.0;
2430 path_joined = false;
2431 path = nullptr;
2432}
2433