diff options
Diffstat (limited to 'scene/3d/occluder_instance_3d.cpp')
-rw-r--r-- | scene/3d/occluder_instance_3d.cpp | 335 |
1 files changed, 335 insertions, 0 deletions
diff --git a/scene/3d/occluder_instance_3d.cpp b/scene/3d/occluder_instance_3d.cpp new file mode 100644 index 0000000000..d3a256db34 --- /dev/null +++ b/scene/3d/occluder_instance_3d.cpp @@ -0,0 +1,335 @@ +/*************************************************************************/ +/* 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<Vector3> Occluder3D::get_debug_lines() const { + if (!debug_lines.is_empty()) { + return debug_lines; + } + + if (indices.size() % 3 != 0) { + return Vector<Vector3>(); + } + + 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<Vector3>(), "Occluder indices are out of range."); + ERR_FAIL_INDEX_V_MSG(b, vertices.size(), Vector<Vector3>(), "Occluder indices are out of range."); + debug_lines.push_back(vertices[a]); + debug_lines.push_back(vertices[b]); + } + } + return debug_lines; +} + +Ref<ArrayMesh> 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.instance(); + 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<Face3> OccluderInstance3D::get_faces(uint32_t p_usage_flags) const { + return Vector<Face3>(); +} + +void OccluderInstance3D::set_occluder(const Ref<Occluder3D> &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<Occluder3D> 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<Material> p_material) { + StandardMaterial3D *standard_mat = Object::cast_to<StandardMaterial3D>(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<MeshInstance3D>(p_node); + if (mi && mi->is_visible_in_tree()) { + Ref<Mesh> 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) { + Transform 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<Occluder3D> occ; + if (get_occluder().is_valid()) { + occ = get_occluder(); + } else { + occ.instance(); + 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() { +} |