#ifndef INCLUDE_GENERIC_3DTK_LOADER_IMPLEMENTATION # error Never include this file directly include 'generic_3dtk_loader.hpp' #endif #include #include #include "util/logger.hpp" #include "glm/glm.hpp" #include "glm/gtx/euler_angles.hpp" #include #include #include #include template std::error_code assets::detail::generic_3dtk_loader::prefetch( path_id_lookups& lookups ) { m_path_buffer.clear(); lookups.point_clouds.by_extension(".3d", m_path_buffer); auto path_buffer = std::filesystem::path{}; for (const auto entry : m_path_buffer) { path_buffer = entry->first; path_buffer.replace_extension(".pose"); lookups.poses.try_emplace(path_buffer); } return {}; } template std::error_code assets::detail::generic_3dtk_loader::load( path_id_lookups& lookups, data_stores& stores, bool pedantic ) { m_path_buffer.clear(); lookups.point_clouds.by_extension(".3d", m_path_buffer); std::for_each( std::execution::parallel_unsequenced_policy{}, m_path_buffer.begin(), m_path_buffer.end(), parser_context{ lookups, stores, } ); return {}; } template assets::detail::generic_3dtk_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_point_cloud.positions().reserve(expected_vertex_count); m_point_cloud.normals().reserve(expected_vertex_count); m_point_cloud.colors().reserve(expected_vertex_count); m_point_cloud.reflectances().reserve(expected_vertex_count); } template void assets::detail::generic_3dtk_loader::parser_context::reset() { m_point_cloud.clear(); } template void assets::detail::generic_3dtk_loader::parser_context::operator()(lookup_type::const_pointer entry) noexcept { const auto& [ filename, id ] = *entry; std::size_t pose_index; if (auto res = parse_scan_index(filename)) { pose_index = *res; } else [[unlikely]] { const auto error = res.error(); ztu::logger::error( "Error occurred while parsing scan index in filename %: [%] %", error.category().name(), error.message() ); return; } auto pose_path = entry->first; pose_path.replace_extension(".pose"); if (pose_path != m_last_pose_path) { if (const auto pose_list_id_it = m_id_lookups->pose_lists.find(pose_path); pose_list_id_it != m_id_lookups->pose_lists.end()) { m_last_pose_path = pose_path; const auto pose_list_id = pose_list_id_it->second; // TODO What if the netry is not there we look it pup again and again // TODO Insert maybe a bool was_valid if (const auto [ pose_list_it, found ] = m_id_lookups->poses.find(pose_list_id); found) { m_last_pose_list = pose_list_it->second; } else { m_last_pose_list = {}; ztu::logger::error("No matching pose found in store for %.", pose_path); return; } } else { ztu::logger::error("No matching pose registered in lookup %.", pose_path); return; } } else if (m_last_pose_list.data() == nullptr) { ztu::logger::error("No matching pose found in store for %.", pose_path); return; } reset(); pose_data pose; if (pose_index < m_last_pose_list.size()) { pose = m_last_pose_list[pose_index]; } else { ztu::logger::error( "Pose index % of 3dtk file % is out of range for its pose file %. Proceeding with identity pose.", pose_index, filename, m_last_pose_path ); pose = glm::identity(); } if (const auto e = parse_file(filename, m_point_cloud)) { ztu::logger::error("Could not parse 3dtk file %: %", filename, e.message()); return; } transform_point_cloud(m_point_cloud.positions(), pose); m_stores->point_clouds.insert(id, m_point_cloud); } template ztu::result assets::detail::generic_3dtk_loader::parse_scan_index( const std::string_view filename ) { static constexpr auto prefix = std::string_view{ "scan" }; auto name_view = filename.substr(0, name_view.find('.')); if (name_view.length() <= prefix.length()) [[unlikely]] { return std::unexpected{ std::make_error_code(std::errc::invalid_argument) }; } name_view = name_view.substr(prefix.length()); std::size_t index; const auto res = std::from_chars(name_view.begin(), name_view.end(), index); if (res.ec != std::errc{}) [[unlikely]] { return std::unexpected{ std::make_error_code(res.ec) }; } return index; } template std::error_code read_vector(std::string_view& line, z3d::vec& vec) { for (int i{}; i != L; ++i) { auto it = line.begin(); const auto [minus, plus] = std::pair{ *it == '-', *it == '+' }; it += plus or minus ? 3 : 2; // skip '[-+]?0x' const auto [ ptr, ec ] = std::from_chars( it, line.end(), vec[i], std::chars_format::hex ); if (ec != std::errc{}) { return std::make_error_code(ec); } if (minus) { vec[i] *= -1.0; } line = { ptr + sizeof(' '), line.end() }; } return {}; } template std::error_code assets::detail::generic_3dtk_loader::parse_file( const std::filesystem::path& filename, data_type& point_cloud ) { std::error_code error; auto in = std::ifstream(filename); if (not in.is_open()) { return std::make_error_code(static_cast(errno)); } std::string line; if (not std::getline(in, line)) { return std::make_error_code(std::errc::invalid_seek); } constexpr auto expected_component_count = []() { auto count = std::tuple_size_v; if (Normal) { count += std::tuple_size_v; } if (Color) { count += std::tuple_size_v; } if (Reflectance) { count += std::tuple_size_v; } return count; }(); auto& positions = point_cloud.positions(); auto& normals = point_cloud.normals(); auto& colors = point_cloud.colors(); auto& reflectances = point_cloud.reflectances(); do { auto line_view = std::string_view{ line }; point_cloud_vertex_components::position position; if ((error = read_vector(line_view, position))) { return error; } positions.push_back(position); if constexpr (Normal) { point_cloud_vertex_components::normal normal; if ((error = read_vector(line_view, normal))) { return error; } normals.push_back(normal); } if constexpr (Color) { point_cloud_vertex_components::color color; if ((error = read_vector(line_view, color))) { return error; } colors.push_back(color); } if constexpr (Reflectance) { z3d::vec<1, point_cloud_vertex_components::reflectance> reflectance; if ((error = read_vector(line_view, reflectance))) { return error; } reflectances.push_back(reflectance[0]); } } while (std::getline(in, line)); return {}; } template void assets::detail::generic_3dtk_loader::transform_point_cloud( std::span points, const pose_data& pose ) { for (auto& point : points) { point = pose * glm::vec4{ point, 1.0f }; } }