/*************************************************************************/ /* occluder_instance_3d.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ /* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ /* "Software"), to deal in the Software without restriction, including */ /* without limitation the rights to use, copy, modify, merge, publish, */ /* distribute, sublicense, and/or sell copies of the Software, and to */ /* permit persons to whom the Software is furnished to do so, subject to */ /* the following conditions: */ /* */ /* The above copyright notice and this permission notice shall be */ /* included in all copies or substantial portions of the Software. */ /* */ /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "occluder_instance_3d.h" #include "core/core_string_names.h" #include "scene/3d/mesh_instance_3d.h" RID Occluder3D::get_rid() const { if (!occluder.is_valid()) { occluder = RS::get_singleton()->occluder_create(); RS::get_singleton()->occluder_set_mesh(occluder, vertices, indices); } return occluder; } void Occluder3D::set_vertices(PackedVector3Array p_vertices) { vertices = p_vertices; if (occluder.is_valid()) { RS::get_singleton()->occluder_set_mesh(occluder, vertices, indices); } _update_changes(); } PackedVector3Array Occluder3D::get_vertices() const { return vertices; } void Occluder3D::set_indices(PackedInt32Array p_indices) { indices = p_indices; if (occluder.is_valid()) { RS::get_singleton()->occluder_set_mesh(occluder, vertices, indices); } _update_changes(); } PackedInt32Array Occluder3D::get_indices() const { return indices; } void Occluder3D::_update_changes() { aabb = AABB(); const Vector3 *ptr = vertices.ptr(); for (int i = 0; i < vertices.size(); i++) { aabb.expand_to(ptr[i]); } debug_lines.clear(); debug_mesh.unref(); emit_changed(); } Vector Occluder3D::get_debug_lines() const { if (!debug_lines.is_empty()) { return debug_lines; } if (indices.size() % 3 != 0) { return Vector(); } for (int i = 0; i < indices.size() / 3; i++) { for (int j = 0; j < 3; j++) { int a = indices[i * 3 + j]; int b = indices[i * 3 + (j + 1) % 3]; ERR_FAIL_INDEX_V_MSG(a, vertices.size(), Vector(), "Occluder indices are out of range."); ERR_FAIL_INDEX_V_MSG(b, vertices.size(), Vector(), "Occluder indices are out of range."); debug_lines.push_back(vertices[a]); debug_lines.push_back(vertices[b]); } } return debug_lines; } Ref Occluder3D::get_debug_mesh() const { if (debug_mesh.is_valid()) { return debug_mesh; } if (indices.size() % 3 != 0) { return debug_mesh; } Array arrays; arrays.resize(Mesh::ARRAY_MAX); arrays[Mesh::ARRAY_VERTEX] = vertices; arrays[Mesh::ARRAY_INDEX] = indices; debug_mesh.instantiate(); debug_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, arrays); return debug_mesh; } AABB Occluder3D::get_aabb() const { return aabb; } void Occluder3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_vertices", "vertices"), &Occluder3D::set_vertices); ClassDB::bind_method(D_METHOD("get_vertices"), &Occluder3D::get_vertices); ClassDB::bind_method(D_METHOD("set_indices", "indices"), &Occluder3D::set_indices); ClassDB::bind_method(D_METHOD("get_indices"), &Occluder3D::get_indices); ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_vertices", "get_vertices"); ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "indices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_indices", "get_indices"); } Occluder3D::Occluder3D() { } Occluder3D::~Occluder3D() { if (occluder.is_valid()) { RS::get_singleton()->free(occluder); } } ///////////////////////////////////////////////// AABB OccluderInstance3D::get_aabb() const { if (occluder.is_valid()) { return occluder->get_aabb(); } return AABB(); } Vector OccluderInstance3D::get_faces(uint32_t p_usage_flags) const { return Vector(); } void OccluderInstance3D::set_occluder(const Ref &p_occluder) { if (occluder == p_occluder) { return; } if (occluder.is_valid()) { occluder->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &OccluderInstance3D::_occluder_changed)); } occluder = p_occluder; if (occluder.is_valid()) { set_base(occluder->get_rid()); occluder->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &OccluderInstance3D::_occluder_changed)); } else { set_base(RID()); } update_gizmo(); } void OccluderInstance3D::_occluder_changed() { update_gizmo(); } Ref OccluderInstance3D::get_occluder() const { return occluder; } void OccluderInstance3D::set_bake_mask(uint32_t p_mask) { bake_mask = p_mask; } uint32_t OccluderInstance3D::get_bake_mask() const { return bake_mask; } void OccluderInstance3D::set_bake_mask_bit(int p_layer, bool p_enable) { ERR_FAIL_INDEX(p_layer, 32); if (p_enable) { set_bake_mask(bake_mask | (1 << p_layer)); } else { set_bake_mask(bake_mask & (~(1 << p_layer))); } } bool OccluderInstance3D::get_bake_mask_bit(int p_layer) const { ERR_FAIL_INDEX_V(p_layer, 32, false); return (bake_mask & (1 << p_layer)); } bool OccluderInstance3D::_bake_material_check(Ref p_material) { StandardMaterial3D *standard_mat = Object::cast_to(p_material.ptr()); if (standard_mat && standard_mat->get_transparency() != StandardMaterial3D::TRANSPARENCY_DISABLED) { return false; } return true; } void OccluderInstance3D::_bake_node(Node *p_node, PackedVector3Array &r_vertices, PackedInt32Array &r_indices) { MeshInstance3D *mi = Object::cast_to(p_node); if (mi && mi->is_visible_in_tree()) { Ref mesh = mi->get_mesh(); bool valid = true; if (mesh.is_null()) { valid = false; } if (valid && !_bake_material_check(mi->get_material_override())) { valid = false; } if ((mi->get_layer_mask() & bake_mask) == 0) { valid = false; } if (valid) { Transform3D global_to_local = get_global_transform().affine_inverse() * mi->get_global_transform(); for (int i = 0; i < mesh->get_surface_count(); i++) { if (mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) { continue; } if (mi->get_surface_override_material(i).is_valid()) { if (!_bake_material_check(mi->get_surface_override_material(i))) { continue; } } else { if (!_bake_material_check(mesh->surface_get_material(i))) { continue; } } Array arrays = mesh->surface_get_arrays(i); int vertex_offset = r_vertices.size(); PackedVector3Array vertices = arrays[Mesh::ARRAY_VERTEX]; r_vertices.resize(r_vertices.size() + vertices.size()); Vector3 *vtx_ptr = r_vertices.ptrw(); for (int j = 0; j < vertices.size(); j++) { vtx_ptr[vertex_offset + j] = global_to_local.xform(vertices[j]); } int index_offset = r_indices.size(); PackedInt32Array indices = arrays[Mesh::ARRAY_INDEX]; r_indices.resize(r_indices.size() + indices.size()); int *idx_ptr = r_indices.ptrw(); for (int j = 0; j < indices.size(); j++) { idx_ptr[index_offset + j] = vertex_offset + indices[j]; } } } } for (int i = 0; i < p_node->get_child_count(); i++) { Node *child = p_node->get_child(i); if (!child->get_owner()) { continue; //maybe a helper } _bake_node(child, r_vertices, r_indices); } } OccluderInstance3D::BakeError OccluderInstance3D::bake(Node *p_from_node, String p_occluder_path) { if (p_occluder_path == "") { if (get_occluder().is_null()) { return BAKE_ERROR_NO_SAVE_PATH; } } PackedVector3Array vertices; PackedInt32Array indices; _bake_node(p_from_node, vertices, indices); if (vertices.is_empty() || indices.is_empty()) { return BAKE_ERROR_NO_MESHES; } Ref occ; if (get_occluder().is_valid()) { occ = get_occluder(); } else { occ.instantiate(); occ->set_path(p_occluder_path); } occ->set_vertices(vertices); occ->set_indices(indices); set_occluder(occ); return BAKE_ERROR_OK; } void OccluderInstance3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_bake_mask", "mask"), &OccluderInstance3D::set_bake_mask); ClassDB::bind_method(D_METHOD("get_bake_mask"), &OccluderInstance3D::get_bake_mask); ClassDB::bind_method(D_METHOD("set_bake_mask_bit", "layer", "enabled"), &OccluderInstance3D::set_bake_mask_bit); ClassDB::bind_method(D_METHOD("get_bake_mask_bit", "layer"), &OccluderInstance3D::get_bake_mask_bit); ClassDB::bind_method(D_METHOD("set_occluder", "occluder"), &OccluderInstance3D::set_occluder); ClassDB::bind_method(D_METHOD("get_occluder"), &OccluderInstance3D::get_occluder); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "occluder", PROPERTY_HINT_RESOURCE_TYPE, "Occluder3D"), "set_occluder", "get_occluder"); ADD_GROUP("Bake", "bake_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "bake_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_bake_mask", "get_bake_mask"); } OccluderInstance3D::OccluderInstance3D() { } OccluderInstance3D::~OccluderInstance3D() { }