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