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