#include "assets/file_parsers/stl_loader.hpp" #include "util/binary_ifstream.hpp" #include "util/unroll_bool_template.hpp" #include "util/logger.hpp" #include std::error_code assets::stl_loader::prefetch( path_id_lookups& lookups ) { return {}; } std::error_code assets::stl_loader::load( path_id_lookups& lookups, data_stores& stores, bool pedantic ) { m_path_buffer.clear(); lookups.meshes.by_extension(".stl", m_path_buffer); std::for_each( std::execution::parallel_unsequenced_policy{}, m_path_buffer.begin(), m_path_buffer.end(), parser_context{ lookups, stores, } ); return {}; } assets::stl_loader::parser_context::parser_context( path_id_lookups& m_id_lookups, data_stores& m_stores ) : m_id_lookups{ &m_id_lookups }, m_stores{ &m_stores } { constexpr auto expected_vertex_count = 8192; m_mesh.positions().reserve(expected_vertex_count); m_mesh.normals().reserve(expected_vertex_count); m_mesh.colors().reserve(expected_vertex_count); m_mesh.reflectances().reserve(expected_vertex_count); m_mesh.tex_coords().reserve(expected_vertex_count); m_mesh.triangles().reserve(2 * expected_vertex_count); } void assets::stl_loader::parser_context::reset() { m_mesh.clear(); m_vertex_index_lookup.clear(); } template std::error_code read_vector(binary_ifstream& in, z3d::vec& vector) { for (int i{}; i != L; ++i) { if (const auto e = in.read_ieee754(vector[i)) { return e; } } return {}; }; void assets::stl_loader::parser_context::operator()(lookup_type::const_pointer entry) noexcept { const auto& [ filename, id ] = *entry; auto in = binary_ifstream{}; if (const auto e = in.open(filename, true)) { ztu::logger::error("Cannot open stl file %.", filename); return; } reset(); auto header_bytes_left = static_cast(80); if (true) // TODO pedanatic { // Check if ASCII file was provided, these start with a specific character sequence. static constexpr auto ascii_magic_string = std::string_view("solid"); auto magic_bytes = std::array{}; if (const auto e = in.read(magic_bytes)) { ztu::logger::error("Error while parsing stl magic bytes of %: %", filename, e.message()); return; } const auto magic_string = std::string_view( reinterpret_cast(magic_bytes.data()), magic_bytes.size() ); if (magic_string == ascii_magic_string) { ztu::logger::error("Error while parsing stl magic bytes %: ASCII stl files are not supported.", filename); return; } header_bytes_left -= ascii_magic_string.size(); } // Ignore (rest of) header. if (const auto e = in.skip(header_bytes_left)) { ztu::logger::error("Error while parsing stl header of %: %", filename, e.message()); return; } // Read number of bytes auto expected_triangle_count = std::uint32_t{}; if (const auto e = in.read(expected_triangle_count)) { ztu::logger::error("Error while parsing stl triangle count %: %", filename, e.message()); return; } // Use separate mesh for parsing, so original mesh is only overwritten // if no errors occurred. This also guarantees unused reserved memory // is freed immediately in case of an error. auto vertex_parsing_error = std::error_code{}; for (std::uint32_t i{}; i != expected_triangle_count; ++i) { mesh_vertex_components::normal normal; if (((vertex_parsing_error = read_vector(in, normal)))) { break; } auto triangle = z3d::index_triangle{}; for (auto& index : triangle) { mesh_vertex_components::position position; if (((vertex_parsing_error = read_vector(in, normal)))) { break; } if (const auto it = m_vertex_index_lookup.find(position); it != m_vertex_index_lookup.end()) { index = it->second; } else { index = m_mesh.positions().size(); m_vertex_index_lookup.emplace_hint(it, position, index); m_mesh.positions().emplace_back(position); } } m_mesh.triangles().emplace_back(triangle); // Skip attribute bytes if (((vertex_parsing_error = in.skip()))) { break; } } if (vertex_parsing_error) { ztu::logger::error("Error while parsing stl vertices in %: %", filename, vertex_parsing_error.message()); return; } m_mesh.component_flags |= mesh_vertex_components::flags::position; m_mesh.component_flags |= mesh_vertex_components::flags::normal; }