1/**************************************************************************/
2/* occluder_instance_3d.cpp */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#include "occluder_instance_3d.h"
32
33#include "core/config/project_settings.h"
34#include "core/io/marshalls.h"
35#include "core/math/geometry_2d.h"
36#include "core/math/triangulate.h"
37#include "scene/3d/importer_mesh_instance_3d.h"
38#include "scene/3d/mesh_instance_3d.h"
39#include "scene/resources/importer_mesh.h"
40#include "scene/resources/surface_tool.h"
41
42#ifdef TOOLS_ENABLED
43#include "editor/editor_node.h"
44#endif
45
46RID Occluder3D::get_rid() const {
47 return occluder;
48}
49
50void Occluder3D::_update() {
51 _update_arrays(vertices, indices);
52
53 aabb = AABB();
54
55 const Vector3 *ptr = vertices.ptr();
56 for (int i = 0; i < vertices.size(); i++) {
57 aabb.expand_to(ptr[i]);
58 }
59
60 debug_lines.clear();
61 debug_mesh.unref();
62
63 RS::get_singleton()->occluder_set_mesh(occluder, vertices, indices);
64 emit_changed();
65}
66
67PackedVector3Array Occluder3D::get_vertices() const {
68 return vertices;
69}
70
71PackedInt32Array Occluder3D::get_indices() const {
72 return indices;
73}
74
75Vector<Vector3> Occluder3D::get_debug_lines() const {
76 if (!debug_lines.is_empty()) {
77 return debug_lines;
78 }
79
80 if (indices.size() % 3 != 0) {
81 return Vector<Vector3>();
82 }
83
84 const Vector3 *vertices_ptr = vertices.ptr();
85 debug_lines.resize(indices.size() / 3 * 6);
86 Vector3 *line_ptr = debug_lines.ptrw();
87 int line_i = 0;
88 for (int i = 0; i < indices.size() / 3; i++) {
89 for (int j = 0; j < 3; j++) {
90 int a = indices[i * 3 + j];
91 int b = indices[i * 3 + (j + 1) % 3];
92 ERR_FAIL_INDEX_V_MSG(a, vertices.size(), Vector<Vector3>(), "Occluder indices are out of range.");
93 ERR_FAIL_INDEX_V_MSG(b, vertices.size(), Vector<Vector3>(), "Occluder indices are out of range.");
94 line_ptr[line_i++] = vertices_ptr[a];
95 line_ptr[line_i++] = vertices_ptr[b];
96 }
97 }
98 return debug_lines;
99}
100
101Ref<ArrayMesh> Occluder3D::get_debug_mesh() const {
102 if (debug_mesh.is_valid()) {
103 return debug_mesh;
104 }
105
106 if (vertices.is_empty() || indices.is_empty() || indices.size() % 3 != 0) {
107 return debug_mesh;
108 }
109
110 Array arrays;
111 arrays.resize(Mesh::ARRAY_MAX);
112 arrays[Mesh::ARRAY_VERTEX] = vertices;
113 arrays[Mesh::ARRAY_INDEX] = indices;
114
115 debug_mesh.instantiate();
116 debug_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, arrays);
117 return debug_mesh;
118}
119
120AABB Occluder3D::get_aabb() const {
121 return aabb;
122}
123
124void Occluder3D::_notification(int p_what) {
125 switch (p_what) {
126 case NOTIFICATION_POSTINITIALIZE: {
127 _update();
128 } break;
129 }
130}
131
132void Occluder3D::_bind_methods() {
133}
134
135Occluder3D::Occluder3D() {
136 occluder = RS::get_singleton()->occluder_create();
137}
138
139Occluder3D::~Occluder3D() {
140 if (occluder.is_valid()) {
141 ERR_FAIL_NULL(RenderingServer::get_singleton());
142 RS::get_singleton()->free(occluder);
143 }
144}
145
146/////////////////////////////////////////////////
147
148void ArrayOccluder3D::set_arrays(PackedVector3Array p_vertices, PackedInt32Array p_indices) {
149 vertices = p_vertices;
150 indices = p_indices;
151 _update();
152}
153
154void ArrayOccluder3D::set_vertices(PackedVector3Array p_vertices) {
155 vertices = p_vertices;
156 _update();
157}
158
159void ArrayOccluder3D::set_indices(PackedInt32Array p_indices) {
160 indices = p_indices;
161 _update();
162}
163
164void ArrayOccluder3D::_update_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices) {
165 r_vertices = vertices;
166 r_indices = indices;
167}
168
169void ArrayOccluder3D::_bind_methods() {
170 ClassDB::bind_method(D_METHOD("set_arrays", "vertices", "indices"), &ArrayOccluder3D::set_arrays);
171
172 ClassDB::bind_method(D_METHOD("set_vertices", "vertices"), &ArrayOccluder3D::set_vertices);
173 ClassDB::bind_method(D_METHOD("get_vertices"), &ArrayOccluder3D::get_vertices);
174
175 ClassDB::bind_method(D_METHOD("set_indices", "indices"), &ArrayOccluder3D::set_indices);
176 ClassDB::bind_method(D_METHOD("get_indices"), &ArrayOccluder3D::get_indices);
177
178 ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_vertices", "get_vertices");
179 ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "indices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_indices", "get_indices");
180}
181
182ArrayOccluder3D::ArrayOccluder3D() {
183}
184
185ArrayOccluder3D::~ArrayOccluder3D() {
186}
187
188/////////////////////////////////////////////////
189
190void QuadOccluder3D::set_size(const Size2 &p_size) {
191 if (size == p_size) {
192 return;
193 }
194
195 size = p_size.max(Size2());
196 _update();
197}
198
199Size2 QuadOccluder3D::get_size() const {
200 return size;
201}
202
203void QuadOccluder3D::_update_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices) {
204 Size2 _size = Size2(size.x / 2.0f, size.y / 2.0f);
205
206 r_vertices = {
207 Vector3(-_size.x, -_size.y, 0),
208 Vector3(-_size.x, _size.y, 0),
209 Vector3(_size.x, _size.y, 0),
210 Vector3(_size.x, -_size.y, 0),
211 };
212
213 r_indices = {
214 0, 1, 2,
215 0, 2, 3
216 };
217}
218
219void QuadOccluder3D::_bind_methods() {
220 ClassDB::bind_method(D_METHOD("set_size", "size"), &QuadOccluder3D::set_size);
221 ClassDB::bind_method(D_METHOD("get_size"), &QuadOccluder3D::get_size);
222
223 ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size", PROPERTY_HINT_NONE, "suffix:m"), "set_size", "get_size");
224}
225
226QuadOccluder3D::QuadOccluder3D() {
227}
228
229QuadOccluder3D::~QuadOccluder3D() {
230}
231
232/////////////////////////////////////////////////
233
234void BoxOccluder3D::set_size(const Vector3 &p_size) {
235 if (size == p_size) {
236 return;
237 }
238
239 size = Vector3(MAX(p_size.x, 0.0f), MAX(p_size.y, 0.0f), MAX(p_size.z, 0.0f));
240 _update();
241}
242
243Vector3 BoxOccluder3D::get_size() const {
244 return size;
245}
246
247void BoxOccluder3D::_update_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices) {
248 Vector3 _size = Vector3(size.x / 2.0f, size.y / 2.0f, size.z / 2.0f);
249
250 r_vertices = {
251 // front
252 Vector3(-_size.x, -_size.y, _size.z),
253 Vector3(_size.x, -_size.y, _size.z),
254 Vector3(_size.x, _size.y, _size.z),
255 Vector3(-_size.x, _size.y, _size.z),
256 // back
257 Vector3(-_size.x, -_size.y, -_size.z),
258 Vector3(_size.x, -_size.y, -_size.z),
259 Vector3(_size.x, _size.y, -_size.z),
260 Vector3(-_size.x, _size.y, -_size.z),
261 };
262
263 r_indices = {
264 // front
265 0, 1, 2,
266 2, 3, 0,
267 // right
268 1, 5, 6,
269 6, 2, 1,
270 // back
271 7, 6, 5,
272 5, 4, 7,
273 // left
274 4, 0, 3,
275 3, 7, 4,
276 // bottom
277 4, 5, 1,
278 1, 0, 4,
279 // top
280 3, 2, 6,
281 6, 7, 3
282 };
283}
284
285void BoxOccluder3D::_bind_methods() {
286 ClassDB::bind_method(D_METHOD("set_size", "size"), &BoxOccluder3D::set_size);
287 ClassDB::bind_method(D_METHOD("get_size"), &BoxOccluder3D::get_size);
288
289 ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size", PROPERTY_HINT_NONE, "suffix:m"), "set_size", "get_size");
290}
291
292BoxOccluder3D::BoxOccluder3D() {
293}
294
295BoxOccluder3D::~BoxOccluder3D() {
296}
297
298/////////////////////////////////////////////////
299
300void SphereOccluder3D::set_radius(float p_radius) {
301 if (radius == p_radius) {
302 return;
303 }
304
305 radius = MAX(p_radius, 0.0f);
306 _update();
307}
308
309float SphereOccluder3D::get_radius() const {
310 return radius;
311}
312
313void SphereOccluder3D::_update_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices) {
314 r_vertices.resize((RINGS + 2) * (RADIAL_SEGMENTS + 1));
315 int vertex_i = 0;
316 Vector3 *vertex_ptr = r_vertices.ptrw();
317
318 r_indices.resize((RINGS + 1) * RADIAL_SEGMENTS * 6);
319 int idx_i = 0;
320 int *idx_ptr = r_indices.ptrw();
321
322 int current_row = 0;
323 int previous_row = 0;
324 int point = 0;
325 for (int j = 0; j <= (RINGS + 1); j++) {
326 float v = j / float(RINGS + 1);
327 float w = Math::sin(Math_PI * v);
328 float y = Math::cos(Math_PI * v);
329 for (int i = 0; i <= RADIAL_SEGMENTS; i++) {
330 float u = i / float(RADIAL_SEGMENTS);
331
332 float x = Math::cos(u * Math_TAU);
333 float z = Math::sin(u * Math_TAU);
334 vertex_ptr[vertex_i++] = Vector3(x * w, y, z * w) * radius;
335
336 if (i > 0 && j > 0) {
337 idx_ptr[idx_i++] = previous_row + i - 1;
338 idx_ptr[idx_i++] = previous_row + i;
339 idx_ptr[idx_i++] = current_row + i - 1;
340
341 idx_ptr[idx_i++] = previous_row + i;
342 idx_ptr[idx_i++] = current_row + i;
343 idx_ptr[idx_i++] = current_row + i - 1;
344 }
345
346 point++;
347 }
348
349 previous_row = current_row;
350 current_row = point;
351 }
352}
353
354void SphereOccluder3D::_bind_methods() {
355 ClassDB::bind_method(D_METHOD("set_radius", "radius"), &SphereOccluder3D::set_radius);
356 ClassDB::bind_method(D_METHOD("get_radius"), &SphereOccluder3D::get_radius);
357
358 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_NONE, "suffix:m"), "set_radius", "get_radius");
359}
360
361SphereOccluder3D::SphereOccluder3D() {
362}
363
364SphereOccluder3D::~SphereOccluder3D() {
365}
366
367/////////////////////////////////////////////////
368
369void PolygonOccluder3D::set_polygon(const Vector<Vector2> &p_polygon) {
370 polygon = p_polygon;
371 _update();
372}
373
374Vector<Vector2> PolygonOccluder3D::get_polygon() const {
375 return polygon;
376}
377
378void PolygonOccluder3D::_update_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices) {
379 if (polygon.size() < 3) {
380 r_vertices.clear();
381 r_indices.clear();
382 return;
383 }
384
385 Vector<Point2> occluder_polygon = polygon;
386 if (Triangulate::get_area(occluder_polygon) > 0) {
387 occluder_polygon.reverse();
388 }
389
390 Vector<int> occluder_indices = Geometry2D::triangulate_polygon(occluder_polygon);
391
392 if (occluder_indices.size() < 3) {
393 r_vertices.clear();
394 r_indices.clear();
395 ERR_FAIL_MSG("Failed to triangulate PolygonOccluder3D. Make sure the polygon doesn't have any intersecting edges.");
396 }
397
398 r_vertices.resize(occluder_polygon.size());
399 Vector3 *vertex_ptr = r_vertices.ptrw();
400 const Vector2 *polygon_ptr = occluder_polygon.ptr();
401 for (int i = 0; i < occluder_polygon.size(); i++) {
402 vertex_ptr[i] = Vector3(polygon_ptr[i].x, polygon_ptr[i].y, 0.0);
403 }
404
405 r_indices.resize(occluder_indices.size());
406 memcpy(r_indices.ptrw(), occluder_indices.ptr(), occluder_indices.size() * sizeof(int));
407}
408
409bool PolygonOccluder3D::_has_editable_3d_polygon_no_depth() const {
410 return false;
411}
412
413void PolygonOccluder3D::_bind_methods() {
414 ClassDB::bind_method(D_METHOD("set_polygon", "polygon"), &PolygonOccluder3D::set_polygon);
415 ClassDB::bind_method(D_METHOD("get_polygon"), &PolygonOccluder3D::get_polygon);
416
417 ClassDB::bind_method(D_METHOD("_has_editable_3d_polygon_no_depth"), &PolygonOccluder3D::_has_editable_3d_polygon_no_depth);
418
419 ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "polygon"), "set_polygon", "get_polygon");
420}
421
422PolygonOccluder3D::PolygonOccluder3D() {
423}
424
425PolygonOccluder3D::~PolygonOccluder3D() {
426}
427
428/////////////////////////////////////////////////
429
430AABB OccluderInstance3D::get_aabb() const {
431 if (occluder.is_valid()) {
432 return occluder->get_aabb();
433 }
434 return AABB();
435}
436
437void OccluderInstance3D::set_occluder(const Ref<Occluder3D> &p_occluder) {
438 if (occluder == p_occluder) {
439 return;
440 }
441
442 if (occluder.is_valid()) {
443 occluder->disconnect_changed(callable_mp(this, &OccluderInstance3D::_occluder_changed));
444 }
445
446 occluder = p_occluder;
447
448 if (occluder.is_valid()) {
449 set_base(occluder->get_rid());
450 occluder->connect_changed(callable_mp(this, &OccluderInstance3D::_occluder_changed));
451 } else {
452 set_base(RID());
453 }
454
455 update_gizmos();
456 update_configuration_warnings();
457
458#ifdef TOOLS_ENABLED
459 // PolygonOccluder3D is edited via an editor plugin, this ensures the plugin is shown/hidden when necessary
460 if (Engine::get_singleton()->is_editor_hint()) {
461 callable_mp(EditorNode::get_singleton(), &EditorNode::edit_current).call_deferred();
462 }
463#endif
464}
465
466void OccluderInstance3D::_occluder_changed() {
467 update_gizmos();
468 update_configuration_warnings();
469}
470
471Ref<Occluder3D> OccluderInstance3D::get_occluder() const {
472 return occluder;
473}
474
475void OccluderInstance3D::set_bake_mask(uint32_t p_mask) {
476 bake_mask = p_mask;
477 update_configuration_warnings();
478}
479
480uint32_t OccluderInstance3D::get_bake_mask() const {
481 return bake_mask;
482}
483
484void OccluderInstance3D::set_bake_simplification_distance(float p_dist) {
485 bake_simplification_dist = MAX(p_dist, 0.0f);
486}
487
488float OccluderInstance3D::get_bake_simplification_distance() const {
489 return bake_simplification_dist;
490}
491
492void OccluderInstance3D::set_bake_mask_value(int p_layer_number, bool p_value) {
493 ERR_FAIL_COND_MSG(p_layer_number < 1, "Render layer number must be between 1 and 20 inclusive.");
494 ERR_FAIL_COND_MSG(p_layer_number > 20, "Render layer number must be between 1 and 20 inclusive.");
495 uint32_t mask = get_bake_mask();
496 if (p_value) {
497 mask |= 1 << (p_layer_number - 1);
498 } else {
499 mask &= ~(1 << (p_layer_number - 1));
500 }
501 set_bake_mask(mask);
502}
503
504bool OccluderInstance3D::get_bake_mask_value(int p_layer_number) const {
505 ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Render layer number must be between 1 and 20 inclusive.");
506 ERR_FAIL_COND_V_MSG(p_layer_number > 20, false, "Render layer number must be between 1 and 20 inclusive.");
507 return bake_mask & (1 << (p_layer_number - 1));
508}
509
510bool OccluderInstance3D::_bake_material_check(Ref<Material> p_material) {
511 StandardMaterial3D *standard_mat = Object::cast_to<StandardMaterial3D>(p_material.ptr());
512 if (standard_mat && standard_mat->get_transparency() != StandardMaterial3D::TRANSPARENCY_DISABLED) {
513 return false;
514 }
515 return true;
516}
517
518void OccluderInstance3D::_bake_surface(const Transform3D &p_transform, Array p_surface_arrays, Ref<Material> p_material, float p_simplification_dist, PackedVector3Array &r_vertices, PackedInt32Array &r_indices) {
519 if (!_bake_material_check(p_material)) {
520 return;
521 }
522 ERR_FAIL_COND_MSG(p_surface_arrays.size() != Mesh::ARRAY_MAX, "Invalid surface array.");
523
524 PackedVector3Array vertices = p_surface_arrays[Mesh::ARRAY_VERTEX];
525 PackedInt32Array indices = p_surface_arrays[Mesh::ARRAY_INDEX];
526
527 if (vertices.size() == 0 || indices.size() == 0) {
528 return;
529 }
530
531 Vector3 *vertices_ptr = vertices.ptrw();
532 for (int j = 0; j < vertices.size(); j++) {
533 vertices_ptr[j] = p_transform.xform(vertices_ptr[j]);
534 }
535
536 if (!Math::is_zero_approx(p_simplification_dist) && SurfaceTool::simplify_func) {
537 Vector<float> vertices_f32 = vector3_to_float32_array(vertices.ptr(), vertices.size());
538
539 float error_scale = SurfaceTool::simplify_scale_func(vertices_f32.ptr(), vertices.size(), sizeof(float) * 3);
540 float target_error = p_simplification_dist / error_scale;
541 float error = -1.0f;
542 int target_index_count = MIN(indices.size(), 36);
543
544 const int simplify_options = SurfaceTool::SIMPLIFY_LOCK_BORDER;
545
546 uint32_t index_count = SurfaceTool::simplify_func(
547 (unsigned int *)indices.ptrw(),
548 (unsigned int *)indices.ptr(),
549 indices.size(),
550 vertices_f32.ptr(), vertices.size(), sizeof(float) * 3,
551 target_index_count, target_error, simplify_options, &error);
552 indices.resize(index_count);
553 }
554
555 SurfaceTool::strip_mesh_arrays(vertices, indices);
556
557 int vertex_offset = r_vertices.size();
558 r_vertices.resize(vertex_offset + vertices.size());
559 memcpy(r_vertices.ptrw() + vertex_offset, vertices.ptr(), vertices.size() * sizeof(Vector3));
560
561 int index_offset = r_indices.size();
562 r_indices.resize(index_offset + indices.size());
563 int *idx_ptr = r_indices.ptrw();
564 for (int j = 0; j < indices.size(); j++) {
565 idx_ptr[index_offset + j] = vertex_offset + indices[j];
566 }
567}
568
569void OccluderInstance3D::_bake_node(Node *p_node, PackedVector3Array &r_vertices, PackedInt32Array &r_indices) {
570 MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_node);
571 if (mi && mi->is_visible_in_tree()) {
572 Ref<Mesh> mesh = mi->get_mesh();
573 bool valid = true;
574
575 if (mesh.is_null()) {
576 valid = false;
577 }
578
579 if (valid && !_bake_material_check(mi->get_material_override())) {
580 valid = false;
581 }
582
583 if ((mi->get_layer_mask() & bake_mask) == 0) {
584 valid = false;
585 }
586
587 if (valid) {
588 Transform3D global_to_local = get_global_transform().affine_inverse() * mi->get_global_transform();
589
590 for (int i = 0; i < mesh->get_surface_count(); i++) {
591 _bake_surface(global_to_local, mesh->surface_get_arrays(i), mi->get_active_material(i), bake_simplification_dist, r_vertices, r_indices);
592 }
593 }
594 }
595
596 for (int i = 0; i < p_node->get_child_count(); i++) {
597 Node *child = p_node->get_child(i);
598 if (!child->get_owner()) {
599 continue; // may be a helper
600 }
601
602 _bake_node(child, r_vertices, r_indices);
603 }
604}
605
606void OccluderInstance3D::bake_single_node(const Node3D *p_node, float p_simplification_distance, PackedVector3Array &r_vertices, PackedInt32Array &r_indices) {
607 ERR_FAIL_NULL(p_node);
608
609 Transform3D xform = p_node->is_inside_tree() ? p_node->get_global_transform() : p_node->get_transform();
610
611 const MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_node);
612 if (mi) {
613 Ref<Mesh> mesh = mi->get_mesh();
614 bool valid = true;
615
616 if (mesh.is_null()) {
617 valid = false;
618 }
619
620 if (valid && !_bake_material_check(mi->get_material_override())) {
621 valid = false;
622 }
623
624 if (valid) {
625 for (int i = 0; i < mesh->get_surface_count(); i++) {
626 _bake_surface(xform, mesh->surface_get_arrays(i), mi->get_active_material(i), p_simplification_distance, r_vertices, r_indices);
627 }
628 }
629 }
630
631 const ImporterMeshInstance3D *imi = Object::cast_to<ImporterMeshInstance3D>(p_node);
632 if (imi) {
633 Ref<ImporterMesh> mesh = imi->get_mesh();
634 bool valid = true;
635
636 if (mesh.is_null()) {
637 valid = false;
638 }
639
640 if (valid) {
641 for (int i = 0; i < mesh->get_surface_count(); i++) {
642 Ref<Material> material = imi->get_surface_material(i);
643 if (material.is_null()) {
644 material = mesh->get_surface_material(i);
645 }
646 _bake_surface(xform, mesh->get_surface_arrays(i), material, p_simplification_distance, r_vertices, r_indices);
647 }
648 }
649 }
650}
651
652OccluderInstance3D::BakeError OccluderInstance3D::bake_scene(Node *p_from_node, String p_occluder_path) {
653 if (p_occluder_path.is_empty()) {
654 if (get_occluder().is_null()) {
655 return BAKE_ERROR_NO_SAVE_PATH;
656 }
657 p_occluder_path = get_occluder()->get_path();
658 if (!p_occluder_path.is_resource_file()) {
659 return BAKE_ERROR_NO_SAVE_PATH;
660 }
661 }
662
663 PackedVector3Array vertices;
664 PackedInt32Array indices;
665
666 _bake_node(p_from_node, vertices, indices);
667
668 if (vertices.is_empty() || indices.is_empty()) {
669 return BAKE_ERROR_NO_MESHES;
670 }
671
672 Ref<ArrayOccluder3D> occ;
673 if (get_occluder().is_valid()) {
674 occ = get_occluder();
675 set_occluder(Ref<Occluder3D>()); // clear
676 }
677
678 if (occ.is_null()) {
679 occ.instantiate();
680 }
681
682 occ->set_arrays(vertices, indices);
683
684 Error err = ResourceSaver::save(occ, p_occluder_path);
685
686 if (err != OK) {
687 return BAKE_ERROR_CANT_SAVE;
688 }
689
690 occ->set_path(p_occluder_path);
691 set_occluder(occ);
692
693 return BAKE_ERROR_OK;
694}
695
696PackedStringArray OccluderInstance3D::get_configuration_warnings() const {
697 PackedStringArray warnings = Node::get_configuration_warnings();
698
699 if (!bool(GLOBAL_GET("rendering/occlusion_culling/use_occlusion_culling"))) {
700 warnings.push_back(RTR("Occlusion culling is disabled in the Project Settings, which means occlusion culling won't be performed in the root viewport.\nTo resolve this, open the Project Settings and enable Rendering > Occlusion Culling > Use Occlusion Culling."));
701 }
702
703 if (bake_mask == 0) {
704 warnings.push_back(RTR("The Bake Mask has no bits enabled, which means baking will not produce any occluder meshes for this OccluderInstance3D.\nTo resolve this, enable at least one bit in the Bake Mask property."));
705 }
706
707 if (occluder.is_null()) {
708 warnings.push_back(RTR("No occluder mesh is defined in the Occluder property, so no occlusion culling will be performed using this OccluderInstance3D.\nTo resolve this, set the Occluder property to one of the primitive occluder types or bake the scene meshes by selecting the OccluderInstance3D and pressing the Bake Occluders button at the top of the 3D editor viewport."));
709 } else {
710 Ref<ArrayOccluder3D> arr_occluder = occluder;
711 if (arr_occluder.is_valid() && arr_occluder->get_indices().size() < 3) {
712 // Setting a new ArrayOccluder3D from the inspector will create an empty occluder,
713 // so warn the user about this.
714 warnings.push_back(RTR("The occluder mesh has less than 3 vertices, so no occlusion culling will be performed using this OccluderInstance3D.\nTo generate a proper occluder mesh, select the OccluderInstance3D then use the Bake Occluders button at the top of the 3D editor viewport."));
715 }
716 Ref<PolygonOccluder3D> poly_occluder = occluder;
717 if (poly_occluder.is_valid() && poly_occluder->get_polygon().size() < 3) {
718 warnings.push_back(RTR("The polygon occluder has less than 3 vertices, so no occlusion culling will be performed using this OccluderInstance3D.\nVertices can be added in the inspector or using the polygon editing tools at the top of the 3D editor viewport."));
719 }
720 }
721
722 return warnings;
723}
724
725bool OccluderInstance3D::_is_editable_3d_polygon() const {
726 return Ref<PolygonOccluder3D>(occluder).is_valid();
727}
728
729Ref<Resource> OccluderInstance3D::_get_editable_3d_polygon_resource() const {
730 return occluder;
731}
732
733void OccluderInstance3D::_bind_methods() {
734 ClassDB::bind_method(D_METHOD("set_bake_mask", "mask"), &OccluderInstance3D::set_bake_mask);
735 ClassDB::bind_method(D_METHOD("get_bake_mask"), &OccluderInstance3D::get_bake_mask);
736 ClassDB::bind_method(D_METHOD("set_bake_mask_value", "layer_number", "value"), &OccluderInstance3D::set_bake_mask_value);
737 ClassDB::bind_method(D_METHOD("get_bake_mask_value", "layer_number"), &OccluderInstance3D::get_bake_mask_value);
738 ClassDB::bind_method(D_METHOD("set_bake_simplification_distance", "simplification_distance"), &OccluderInstance3D::set_bake_simplification_distance);
739 ClassDB::bind_method(D_METHOD("get_bake_simplification_distance"), &OccluderInstance3D::get_bake_simplification_distance);
740
741 ClassDB::bind_method(D_METHOD("set_occluder", "occluder"), &OccluderInstance3D::set_occluder);
742 ClassDB::bind_method(D_METHOD("get_occluder"), &OccluderInstance3D::get_occluder);
743
744 ClassDB::bind_method(D_METHOD("_is_editable_3d_polygon"), &OccluderInstance3D::_is_editable_3d_polygon);
745 ClassDB::bind_method(D_METHOD("_get_editable_3d_polygon_resource"), &OccluderInstance3D::_get_editable_3d_polygon_resource);
746
747 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "occluder", PROPERTY_HINT_RESOURCE_TYPE, "Occluder3D"), "set_occluder", "get_occluder");
748 ADD_GROUP("Bake", "bake_");
749 ADD_PROPERTY(PropertyInfo(Variant::INT, "bake_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_bake_mask", "get_bake_mask");
750 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bake_simplification_distance", PROPERTY_HINT_RANGE, "0.0,2.0,0.01,suffix:m"), "set_bake_simplification_distance", "get_bake_simplification_distance");
751}
752
753OccluderInstance3D::OccluderInstance3D() {
754}
755
756OccluderInstance3D::~OccluderInstance3D() {
757}
758