fixes
This commit is contained in:
444
source/assets/data_loaders/generic/generic_3dtk_loader.ipp
Normal file
444
source/assets/data_loaders/generic/generic_3dtk_loader.ipp
Normal file
@@ -0,0 +1,444 @@
|
||||
#ifndef INCLUDE_GENERIC_3DTK_LOADER_IMPLEMENTATION
|
||||
# error Never include this file directly include 'generic_3dtk_loader.hpp'
|
||||
#endif
|
||||
|
||||
#include <charconv>
|
||||
#include <fstream>
|
||||
#include "util/logger.hpp"
|
||||
#include "glm/glm.hpp"
|
||||
#include "glm/gtx/euler_angles.hpp"
|
||||
#include <charconv>
|
||||
#include <fstream>
|
||||
|
||||
template<bool Normal, bool Color, bool Reflectance>
|
||||
ztu::result<pose_prefetch_lookup::index_type> generic_3dtk_loader<Normal, Color, Reflectance>::parse_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::make_error_code(std::errc::invalid_argument);
|
||||
}
|
||||
|
||||
name_view = name_view.substr(prefix.length());
|
||||
|
||||
pose_prefetch_lookup::index_type index;
|
||||
|
||||
const auto res = std::from_chars(name_view.begin(), name_view.end(), index);
|
||||
|
||||
if (res.ec != std::errc{}) [[unlikely]]
|
||||
{
|
||||
return std::make_error_code(res.ec);
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
template<bool Normal, bool Color, bool Reflectance>
|
||||
std::error_code generic_3dtk_loader<Normal, Color, Reflectance>::prefetch(
|
||||
const file_dir_list& paths,
|
||||
prefetch_queue& queue
|
||||
) {
|
||||
|
||||
auto path_buffer = std::filesystem::path{};
|
||||
|
||||
for (const auto filename : paths.files)
|
||||
{
|
||||
path_buffer.assign(filename.begin(), filename.end());
|
||||
path_buffer.replace_extension(".pose");
|
||||
queue.uos_queue.files.push_back(path_buffer.c_str());
|
||||
}
|
||||
|
||||
// TODO optimize
|
||||
for (const auto directory : paths.directories)
|
||||
{
|
||||
queue.uos_queue.directories.push_back(directory);
|
||||
}
|
||||
}
|
||||
|
||||
template<bool Normal, bool Color, bool Reflectance>
|
||||
std::error_code generic_3dtk_loader<Normal, Color, Reflectance>::load(
|
||||
dynamic_point_cloud_buffer& buffer,
|
||||
const file_dir_list& paths,
|
||||
prefetch_lookup& asset_lookup,
|
||||
dynamic_point_cloud_store& store,
|
||||
const bool pedantic
|
||||
) {
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
auto in = std::ifstream{};
|
||||
auto path_buffer = fs::path{};
|
||||
auto error = std::error_code{};
|
||||
|
||||
const auto load_file = [&](const char* filename)
|
||||
{
|
||||
// TODO look up pose
|
||||
auto scan_index = pose_prefetch_lookup::index_type{};
|
||||
|
||||
if (auto res = parse_index(filename))
|
||||
{
|
||||
scan_index = *res;
|
||||
}
|
||||
else [[unlikely]]
|
||||
{
|
||||
error = res.error();
|
||||
ztu::logger::error(
|
||||
"Error occurred while parsing scan index in filename %: [%] %",
|
||||
error.category().name(),
|
||||
error.message()
|
||||
);
|
||||
}
|
||||
|
||||
auto pose = asset_lookup.poses.find();
|
||||
|
||||
|
||||
const auto id_it = id_lookup.find(filename);
|
||||
|
||||
if (id_it != id_lookup.end()) [[unlikely]]
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
in.open(filename);
|
||||
if (in.is_open())
|
||||
{
|
||||
if ((error = this->read_point_file(filename, buffer))) {
|
||||
return error;
|
||||
}
|
||||
|
||||
this->transform_point_cloud(buffer.positions(), pose);
|
||||
|
||||
const auto id = store.add(std::move(buffer));
|
||||
id_lookup.emplace_hint(id_it, filename, id);
|
||||
}
|
||||
else
|
||||
{
|
||||
ztu::logger::error("Cannot open 3dtk file %", filename);
|
||||
}
|
||||
|
||||
in.close();
|
||||
};
|
||||
|
||||
for (const auto filename : paths.files)
|
||||
{
|
||||
load_file(filename.data());
|
||||
}
|
||||
|
||||
|
||||
|
||||
for (const auto directory : paths.directories)
|
||||
{
|
||||
directory_buffer.assign(directory.begin(), directory.end());
|
||||
directory_buffer /= "frames";
|
||||
|
||||
const auto directory_exists = not fs::is_directory(directory_buffer, error);
|
||||
|
||||
if (error or not directory_exists) [[unlikely]]
|
||||
{
|
||||
ztu::logger::error("Could not open point cloud directory %", directory_buffer);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const auto& filename : fs::directory_iterator{ directory_buffer }) {
|
||||
|
||||
auto point_filename = reinterpret_cast<const fs::path&>(filename);
|
||||
if (point_filename.extension() != ".3d")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
load_file(filename.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
template<bool Normal, bool Color, bool Reflectance>
|
||||
std::error_code generic_3dtk_loader<Normal, Color, Reflectance>::load_directory(
|
||||
dynamic_data_loader_ctx& ctx,
|
||||
dynamic_point_cloud_store& store,
|
||||
const std::filesystem::path& path,
|
||||
const bool pedantic
|
||||
) {
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
std::error_code error;
|
||||
|
||||
const auto directory_exists = not fs::is_directory(path, error);
|
||||
if (error)
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
if (not directory_exists)
|
||||
{
|
||||
return make_error_code(std::errc::no_such_file_or_directory);
|
||||
}
|
||||
|
||||
for (const auto& filename : fs::directory_iterator{ path / "frames" }) {
|
||||
|
||||
auto point_filename = reinterpret_cast<const fs::path&>(filename);
|
||||
if (point_filename.extension() != ".3d") {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((error = load(ctx, store, point_filename, pedantic)))
|
||||
{
|
||||
ztu::logger::error(
|
||||
"Error while loading point cloud '%': [%] %",
|
||||
point_filename,
|
||||
error.category().name(),
|
||||
error.message()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T, std::size_t Count>
|
||||
std::error_code read_vector(std::string_view& line, std::array<T, Count>& vec) {
|
||||
for (auto& component : vec)
|
||||
{
|
||||
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(),
|
||||
component,
|
||||
std::chars_format::hex
|
||||
);
|
||||
|
||||
if (ec != std::errc{})
|
||||
{
|
||||
return std::make_error_code(ec);
|
||||
}
|
||||
|
||||
if (minus) {
|
||||
component *= -1.0;
|
||||
}
|
||||
|
||||
line = { ptr + sizeof(' '), line.end() };
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
template<bool Normal, bool Color, bool Reflectance>
|
||||
std::error_code generic_3dtk_loader<Normal, Color, Reflectance>::read_point_file(
|
||||
const std::filesystem::path& filename,
|
||||
dynamic_point_cloud_data& point_cloud
|
||||
) {
|
||||
std::error_code error;
|
||||
|
||||
auto in = std::ifstream(filename);
|
||||
|
||||
if (not in.is_open()) {
|
||||
return std::make_error_code(static_cast<std::errc>(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<components::point_cloud_vertex::position>;
|
||||
|
||||
if (Normal)
|
||||
{
|
||||
count += std::tuple_size_v<components::point_cloud_vertex::normal>;
|
||||
}
|
||||
|
||||
if (Color)
|
||||
{
|
||||
count += std::tuple_size_v<components::point_cloud_vertex::color>;
|
||||
}
|
||||
|
||||
if (Reflectance)
|
||||
{
|
||||
count += std::tuple_size_v<components::point_cloud_vertex::reflectance>;
|
||||
}
|
||||
|
||||
return count;
|
||||
}();
|
||||
|
||||
ztu::u32 component_count;
|
||||
std::chars_format float_format;
|
||||
|
||||
if ((error = analyze_component_format(line, component_count, float_format)))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
if (component_count != expected_component_count)
|
||||
{
|
||||
return std::make_error_code(std::errc::invalid_argument);
|
||||
}
|
||||
|
||||
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 };
|
||||
|
||||
components::point_cloud_vertex::position position;
|
||||
if ((error = read_vector(line_view, position)))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
positions.push_back(position);
|
||||
|
||||
if constexpr (Normal)
|
||||
{
|
||||
components::point_cloud_vertex::normal normal;
|
||||
if ((error = read_vector(line_view, normal)))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
normals.push_back(normal);
|
||||
}
|
||||
|
||||
if constexpr (Color)
|
||||
{
|
||||
components::point_cloud_vertex::color color;
|
||||
if ((error = read_vector(line_view, color)))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
colors.push_back(color);
|
||||
}
|
||||
|
||||
if constexpr (Reflectance)
|
||||
{
|
||||
components::point_cloud_vertex::reflectance reflectance;
|
||||
if ((error = read_vector(line_view, reflectance)))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
reflectances.push_back(reflectance);
|
||||
}
|
||||
}
|
||||
while (std::getline(in, line));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::error_code base_3dtk_loader::read_pose_file(
|
||||
const std::filesystem::path& filename,
|
||||
glm::mat4& pose
|
||||
) {
|
||||
auto in = std::ifstream(filename);
|
||||
if (not in.is_open()) {
|
||||
return std::make_error_code(static_cast<std::errc>(errno));
|
||||
}
|
||||
std::string line;
|
||||
|
||||
std::array<glm::vec3, 2> numbers{};
|
||||
|
||||
for (std::size_t row{}; row != 2; ++row) {
|
||||
|
||||
std::getline(in, line);
|
||||
|
||||
auto it = line.cbegin().base();
|
||||
auto end = line.cend().base();
|
||||
|
||||
for (glm::vec3::length_type col{}; col != 3; ++col) {
|
||||
|
||||
const auto [ ptr, ec ] = std::from_chars(
|
||||
it, end,
|
||||
numbers[row][col],
|
||||
std::chars_format::general
|
||||
);
|
||||
|
||||
if (ec != std::errc{}) {
|
||||
return std::make_error_code(ec);
|
||||
}
|
||||
|
||||
it = ptr + 1; // skip space in between components
|
||||
}
|
||||
}
|
||||
|
||||
const auto& translation = numbers[0];
|
||||
auto& angles = numbers[1];
|
||||
angles *= static_cast<float>(M_PI / 180.0);
|
||||
|
||||
pose = (
|
||||
glm::translate(glm::identity<glm::mat4>(), translation) *
|
||||
glm::eulerAngleXYZ(angles[0], angles[1], angles[2])
|
||||
);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::error_code base_3dtk_loader::analyze_component_format(
|
||||
const std::string& line,
|
||||
ztu::u32& component_count,
|
||||
std::chars_format& format
|
||||
) {
|
||||
auto begin = line.cbegin().base();
|
||||
auto end = line.cend().base();
|
||||
|
||||
format = std::chars_format::general;
|
||||
|
||||
component_count = 0;
|
||||
float buffer;
|
||||
|
||||
for (auto it = begin; it < end; it += sizeof(' '))
|
||||
{
|
||||
it += *it == '-' or *it == '+';
|
||||
|
||||
std::chars_format current_format;
|
||||
if (*it == '0' and std::next(it) < end and *std::next(it) == 'x')
|
||||
{
|
||||
it += 2; // skip '0x'
|
||||
current_format = std::chars_format::hex;
|
||||
}
|
||||
else
|
||||
{
|
||||
current_format = std::chars_format::general;
|
||||
}
|
||||
|
||||
if (it == begin and current_format != format)
|
||||
{
|
||||
return std::make_error_code(std::errc::invalid_argument);
|
||||
}
|
||||
|
||||
const auto [next_it, err] = std::from_chars(it, end, buffer, current_format);
|
||||
if (err != std::errc())
|
||||
{
|
||||
return std::make_error_code(err);
|
||||
}
|
||||
|
||||
it = next_it;
|
||||
format = current_format;
|
||||
++component_count;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
void base_3dtk_loader::transform_point_cloud(
|
||||
std::span<components::point_cloud_vertex::position::value_type> points,
|
||||
const glm::mat4& pose
|
||||
) {
|
||||
for (auto& [ x, y, z ] : points) {
|
||||
auto vec = glm::vec4{ x, y, z, 1.0f };
|
||||
vec = pose * vec;
|
||||
x = vec.x;
|
||||
y = vec.y;
|
||||
z = vec.z;
|
||||
}
|
||||
}
|
||||
36
source/assets/data_loaders/glsl_loader.cpp
Normal file
36
source/assets/data_loaders/glsl_loader.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#include "assets/data_loaders/glsl_loader.hpp"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
std::error_code glsl_loader::load(
|
||||
const std::filesystem::path& filename,
|
||||
std::string& source
|
||||
) {
|
||||
|
||||
auto file = std::ifstream(filename);
|
||||
if (not file.is_open())
|
||||
{
|
||||
return std::make_error_code(std::errc::no_such_file_or_directory);
|
||||
}
|
||||
|
||||
file.seekg(0, std::ios::end);
|
||||
const auto size = file.tellg();
|
||||
|
||||
if (size == 0 or size == std::numeric_limits<std::streamsize>::max())
|
||||
{
|
||||
return std::make_error_code(std::errc::invalid_seek);
|
||||
}
|
||||
|
||||
source.reserve(size);
|
||||
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
source.assign(
|
||||
std::istreambuf_iterator<char>(file),
|
||||
std::istreambuf_iterator<char>()
|
||||
);
|
||||
|
||||
file.close();
|
||||
|
||||
return {};
|
||||
}
|
||||
302
source/assets/data_loaders/kitti_loader.cpp
Normal file
302
source/assets/data_loaders/kitti_loader.cpp
Normal file
@@ -0,0 +1,302 @@
|
||||
#include "assets/data_loaders/kitti_loader.hpp"
|
||||
|
||||
#include "glm/glm.hpp"
|
||||
|
||||
#include <charconv>
|
||||
#include <fstream>
|
||||
#include <glm/ext/matrix_transform.hpp>
|
||||
|
||||
#include "assets/components/point_cloud_vertex_components.hpp"
|
||||
#include "util/binary_ifstream.hpp"
|
||||
#include "util/logger.hpp"
|
||||
|
||||
|
||||
ztu::result<std::string_view> kitti_loader::parent_directory(const std::string_view path)
|
||||
{
|
||||
const auto sep_index = path.rfind(std::filesystem::path::preferred_separator);
|
||||
|
||||
if (sep_index == std::string_view::npos)
|
||||
{
|
||||
return std::unexpected(std::make_error_code(std::errc::no_such_file_or_directory));
|
||||
}
|
||||
|
||||
return path.substr(0, sep_index);
|
||||
};
|
||||
|
||||
std::error_code kitti_loader::prefetch(
|
||||
const file_dir_list& paths,
|
||||
prefetch_queue& queue
|
||||
) {
|
||||
|
||||
// Directories can simply be passed on
|
||||
queue.kitti_pose_queue.directories.push_back(paths.directories);
|
||||
|
||||
// For files, we just forward the files directory
|
||||
for (const auto file : queue.kitti_pose_queue.files)
|
||||
{
|
||||
if (const auto base_directory = parent_directory(file).and_then(parent_directory))
|
||||
{
|
||||
queue.kitti_pose_queue.directories.push_back(*base_directory);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO remove from list
|
||||
ztu::logger::error("Malformed kitti file path: %.", file);
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::error_code kitti_loader::load(
|
||||
dynamic_point_cloud_buffer& buffer,
|
||||
const file_dir_list& paths,
|
||||
prefetch_lookup& id_lookup,
|
||||
dynamic_data_store& store,
|
||||
bool
|
||||
) {
|
||||
namespace fs = std::filesystem;
|
||||
std::error_code error;
|
||||
|
||||
std::vector<dynamic_pose_store::iterator_type> pose_its;
|
||||
pose_its.reserve(paths.files.size());
|
||||
|
||||
auto processed_filenames = ztu::string_list{};
|
||||
|
||||
auto path_buffer = fs::path{};
|
||||
|
||||
const auto preprocess_filename = [&](
|
||||
std::string_view path,
|
||||
const auto& directory,
|
||||
std::string_view filename,
|
||||
const pose_prefetch_lookup::directory_iterator& dir_it
|
||||
) {
|
||||
const auto pose_index = frame_id_from_filename(filename);
|
||||
if (not pose_index) [[unlikely]]
|
||||
{
|
||||
ztu::logger::error("Could not parse frame id from kitti file path: %.", filename);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto [ index_it, pose_id_match ] = id_lookup.poses.find_index(dir_it, *pose_index);
|
||||
|
||||
if (not pose_id_match) [[unlikely]]
|
||||
{
|
||||
ztu::logger::error("No matching pose index (%) found in directory (%).", directory, *pose_index);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto [ pose_it, pose_match ] = store.poses.find(pose_id_match);
|
||||
if (not pose_id_match) [[unlikely]]
|
||||
{
|
||||
ztu::logger::error("No matching pose found for id: %.", pose_id_match);
|
||||
return;
|
||||
}
|
||||
|
||||
processed_filenames.push_back(path);
|
||||
pose_its.push_back(pose_it);
|
||||
};
|
||||
|
||||
for (const auto file : paths.files)
|
||||
{
|
||||
path_buffer.assign(file.begin(), file.end());
|
||||
|
||||
if (not fs::is_regular_file(path_buffer))
|
||||
{
|
||||
ztu::logger::error("Given kitti file does not exist: %.", path_buffer);
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto sep_index = file.rfind(fs::path::preferred_separator);
|
||||
|
||||
if (sep_index == std::string_view::npos) [[unlikely]]
|
||||
{
|
||||
ztu::logger::error("Could not parse frame directory from kitti file path: %.", file);
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto base_directory = parent_directory(file.substr(0, sep_index));
|
||||
if (not base_directory) [[unlikely]]
|
||||
{
|
||||
ztu::logger::error("Could not parse base directory from kitti file path: %.", file);
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto [ dir_it, dir_match ] = id_lookup.poses.find_directory(*base_directory);
|
||||
if (not dir_match) [[unlikely]]
|
||||
{
|
||||
ztu::logger::error("No matching pose directory found for %.", file);
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto filename = file.substr(sep_index + 1);
|
||||
|
||||
preprocess_filename(
|
||||
file,
|
||||
*base_directory,
|
||||
filename,
|
||||
dir_it
|
||||
);
|
||||
}
|
||||
|
||||
for (const auto directory : paths.directories)
|
||||
{
|
||||
path_buffer.assign(directory.begin(), directory.end());
|
||||
|
||||
const auto [ dir_it, dir_match ] = id_lookup.poses.find_directory(path_buffer);
|
||||
if (not dir_match) [[unlikely]]
|
||||
{
|
||||
ztu::logger::error("No matching pose directory found for %.", path_buffer);
|
||||
continue;
|
||||
}
|
||||
|
||||
path_buffer /= frame_folder;
|
||||
|
||||
if (not fs::is_directory(path_buffer))
|
||||
{
|
||||
ztu::logger::error("Given kitti directory does not exist: %.", directory);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const auto& file : fs::directory_iterator{ path_buffer })
|
||||
{
|
||||
const auto file_path = std::string_view{ file.path().c_str() };
|
||||
|
||||
const auto extension_begin = file_path.rfind('.');
|
||||
|
||||
if (extension_begin == std::string_view::npos or file_path.substr(extension_begin) != ".bin")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto filename_begin = file_path.rfind(fs::path::preferred_separator, extension_begin);
|
||||
filename_begin = filename_begin == std::string_view::npos ? 0 : filename_begin + 1;
|
||||
|
||||
const auto filename_only = file_path.substr(filename_begin);
|
||||
|
||||
const auto pose_index = frame_id_from_filename(filename_only);
|
||||
if (not pose_index) [[unlikely]]
|
||||
{
|
||||
ztu::logger::error("Could not parse frame id from kitti filename: %.", filename_only);
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto [ index_it, pose_id_match ] = id_lookup.poses.find_index(dir_it, *pose_index);
|
||||
|
||||
if (not pose_id_match) [[unlikely]]
|
||||
{
|
||||
ztu::logger::error("No matching pose index (%) found in directory (%).", directory, *pose_index);
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto [ pose_it, pose_match ] = store.poses.find(pose_id_match);
|
||||
if (not pose_id_match) [[unlikely]]
|
||||
{
|
||||
ztu::logger::error("No matching pose found for id: %.", pose_id_match);
|
||||
continue;
|
||||
}
|
||||
|
||||
processed_filenames.push_back(file_path);
|
||||
pose_its.push_back(pose_it);
|
||||
|
||||
preprocess_filename(
|
||||
file_path,
|
||||
directory,
|
||||
filename_only,
|
||||
dir_it
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (const auto [ filename, pose_it ] : std::ranges::views::zip_view(processed_filenames, pose_its))
|
||||
{
|
||||
buffer.clear();
|
||||
|
||||
if ((error = load_point_file(filename, buffer)))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
transform_point_cloud(buffer.positions(), *pose_it);
|
||||
|
||||
store.point_clouds.add(buffer);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void kitti_loader::transform_point_cloud(
|
||||
std::span<components::point_cloud_vertex::position> points,
|
||||
const glm::mat4& pose
|
||||
) {
|
||||
for (auto& [ x, y, z ] : points) {
|
||||
auto vec = glm::vec4{ x, y, z, 1.0f };
|
||||
vec = pose * vec;
|
||||
x = vec.x;
|
||||
y = vec.y;
|
||||
z = vec.z;
|
||||
}
|
||||
}
|
||||
|
||||
std::error_code kitti_loader::load_point_file(
|
||||
const std::filesystem::path& filename,
|
||||
dynamic_point_cloud_buffer& point_cloud
|
||||
) {
|
||||
|
||||
auto in = binary_ifstream{};
|
||||
|
||||
auto error = std::error_code{};
|
||||
|
||||
if ((error == in.open(filename, true)))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
const auto read_vector = [&in](auto& vector) -> std::error_code
|
||||
{
|
||||
for (auto& component : vector)
|
||||
{
|
||||
float component32;
|
||||
if (const auto e = in.read_ieee754<std::endian::little>(component32))
|
||||
{
|
||||
return e;
|
||||
}
|
||||
component = component32;
|
||||
}
|
||||
return {};
|
||||
};
|
||||
|
||||
components::point_cloud_vertex::position position;
|
||||
|
||||
auto& positions = point_cloud.positions();
|
||||
|
||||
while (not ((error = read_vector(position)))) {
|
||||
positions.push_back(position);
|
||||
if ((error = in.skip<float>())) // TODO what am I skipping here?!?
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (static_cast<std::errc>(error.value()) != std::errc::result_out_of_range)
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
ztu::result<std::size_t> kitti_loader::frame_id_from_filename(
|
||||
std::string_view filename
|
||||
) {
|
||||
std::size_t id;
|
||||
const auto result = std::from_chars(filename.cbegin(), filename.cend(), id);
|
||||
|
||||
if (result.ec != std::errc{})
|
||||
{
|
||||
return std::unexpected(std::make_error_code(result.ec));
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
163
source/assets/data_loaders/kitti_pose_loader.cpp
Normal file
163
source/assets/data_loaders/kitti_pose_loader.cpp
Normal file
@@ -0,0 +1,163 @@
|
||||
#include "assets/data_loaders/kitti_pose_loader.hpp"
|
||||
|
||||
#include "assets/dynamic_read_buffers/dynamic_pose_buffer.hpp"
|
||||
#include <fstream>
|
||||
#include <glm/ext/matrix_transform.hpp>
|
||||
#include "util/logger.hpp"
|
||||
|
||||
inline std::error_code kitti_pose_loader::parse_pose(
|
||||
std::ifstream& in,
|
||||
dynamic_pose_buffer& pose
|
||||
) {
|
||||
for (dynamic_pose_buffer::length_type row{}; row != 3; ++row)
|
||||
{
|
||||
for (dynamic_pose_buffer::length_type col{}; col != 4; ++col)
|
||||
{
|
||||
if (not (in >> pose[row][col]))
|
||||
{
|
||||
return std::make_error_code(std::errc::result_out_of_range);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
std::error_code kitti_pose_loader::prefetch(
|
||||
const file_dir_list& paths,
|
||||
prefetch_queue& queue
|
||||
) {
|
||||
// Nothing to be done here
|
||||
}
|
||||
|
||||
std::error_code kitti_pose_loader::load(
|
||||
dynamic_pose_buffer& buffer,
|
||||
const file_dir_list& paths,
|
||||
prefetch_lookup& id_lookup,
|
||||
dynamic_data_store& store,
|
||||
bool pedantic
|
||||
) {
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
auto path_buffer = fs::path{};
|
||||
auto in = std::ifstream{}; // TODO disable exceptions (for other loaders as well)
|
||||
auto pose_buffer = dynamic_pose_buffer{};
|
||||
|
||||
pose_buffer = glm::identity<glm::mat4>();
|
||||
|
||||
auto processed_filenames = ztu::string_list{};
|
||||
|
||||
processed_filenames.reserve(
|
||||
paths.files.character_count() +
|
||||
paths.directories.character_count() +
|
||||
paths.directories.size() * pose_filename.size(),
|
||||
paths.files.size() + paths.directories.size()
|
||||
);
|
||||
|
||||
const auto preprocess_file = [&]()
|
||||
{
|
||||
if (not fs::is_regular_file(path_buffer))
|
||||
{
|
||||
ztu::logger::error("Kitti pose file does not exist: %", path_buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
processed_filenames.push_back(path_buffer.c_str());
|
||||
};
|
||||
|
||||
for (const auto directory : paths.directories) {
|
||||
path_buffer.assign(directory.begin(), directory.end());
|
||||
path_buffer /= "pose.txt";
|
||||
preprocess_file();
|
||||
}
|
||||
|
||||
for (const auto file : paths.files) {
|
||||
path_buffer.assign(file.begin(), file.end());
|
||||
preprocess_file();
|
||||
}
|
||||
|
||||
for (const auto filename : processed_filenames)
|
||||
{
|
||||
in.open(filename.data()); // Safe because string list adds null terminator
|
||||
if (not in.is_open())
|
||||
{
|
||||
ztu::logger::error("Cannot open kitti pose file %", path_buffer);
|
||||
continue;
|
||||
}
|
||||
|
||||
in >> std::skipws;
|
||||
|
||||
for (auto i = pose_prefetch_lookup::index_type{}; in.peek() != std::ifstream::traits_type::eof(); ++i)
|
||||
{
|
||||
if (const auto error = parse_pose(in, pose_buffer))
|
||||
{
|
||||
ztu::logger::error(
|
||||
"Error occurred while parsing kitti pose % in file %: [%] %",
|
||||
i,
|
||||
path_buffer,
|
||||
error.category().name(),
|
||||
error.message()
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto id = store.poses.add(pose_buffer);
|
||||
|
||||
// TODO if (not) removing the path separator creates issues.
|
||||
const auto directory = filename.substr(0, filename.length() - pose_filename.length());
|
||||
|
||||
id_lookup.poses.emplace(directory, i, id);
|
||||
}
|
||||
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
|
||||
void kitti_pose_loader::load(
|
||||
const ztu::string_list& directories,
|
||||
dynamic_pose_store& store,
|
||||
pose_prefetch_lookup& id_lookup
|
||||
) {
|
||||
|
||||
auto filename_buffer = std::filesystem::path{};
|
||||
auto in = std::ifstream{}; // TODO disable exceptions (for other loaders as well)
|
||||
auto pose_buffer = dynamic_pose_buffer{};
|
||||
|
||||
pose_buffer = glm::identity<glm::mat4>();
|
||||
|
||||
for (const auto directory : directories)
|
||||
{
|
||||
filename_buffer = directory;
|
||||
filename_buffer /= "pose.txt";
|
||||
|
||||
in.open(filename_buffer);
|
||||
if (not in.is_open())
|
||||
{
|
||||
ztu::logger::error("Cannot open kitti pose file %", filename_buffer);
|
||||
continue;
|
||||
}
|
||||
|
||||
in >> std::skipws;
|
||||
|
||||
for (auto i = pose_prefetch_lookup::index_type{}; in.peek() != std::ifstream::traits_type::eof(); ++i)
|
||||
{
|
||||
if (const auto error = parse_pose(in, pose_buffer))
|
||||
{
|
||||
ztu::logger::error(
|
||||
"Error occurred while parsing kitti pose % in file %: [%] %",
|
||||
i,
|
||||
filename_buffer,
|
||||
error.category().name(),
|
||||
error.message()
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto id = store.add(pose_buffer);
|
||||
id_lookup.emplace(directory, i, id);
|
||||
}
|
||||
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
398
source/assets/data_loaders/mtl_loader.cpp
Normal file
398
source/assets/data_loaders/mtl_loader.cpp
Normal file
@@ -0,0 +1,398 @@
|
||||
#include "assets/data_loaders/mtl_loader.hpp"
|
||||
|
||||
#include <charconv>
|
||||
#include <fstream>
|
||||
#include <array>
|
||||
|
||||
#include "util/logger.hpp"
|
||||
#include "util/for_each.hpp"
|
||||
#include "util/line_parser.hpp"
|
||||
|
||||
#include "assets/dynamic_data_loaders/dynamic_texture_loader.hpp"
|
||||
|
||||
namespace mtl_loader_error {
|
||||
|
||||
struct category : std::error_category {
|
||||
[[nodiscard]] const char* name() const noexcept override {
|
||||
return "connector";
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string message(int ev) const override {
|
||||
switch (static_cast<codes>(ev)) {
|
||||
using enum codes;
|
||||
case mtl_cannot_open_file:
|
||||
return "Cannot open mtl file.";
|
||||
case mtl_cannot_open_texture:
|
||||
return "Cannot open texture file.";
|
||||
case mtl_malformed_ambient_color:
|
||||
return "File contains malformed 'Ka' statement.";
|
||||
case mtl_malformed_diffuse_color:
|
||||
return "File contains malformed 'Kd' statement.";
|
||||
case mtl_malformed_specular_color:
|
||||
return "File contains malformed 'Ks' statement.";
|
||||
case mtl_malformed_specular_exponent:
|
||||
return "File contains malformed 'Ns' statement.";
|
||||
case mtl_malformed_dissolve:
|
||||
return "File contains malformed 'd' statement.";
|
||||
case mlt_unknown_line_begin:
|
||||
return "Unknown mtl line begin";
|
||||
default:
|
||||
using namespace std::string_literals;
|
||||
return "unrecognized error ("s + std::to_string(ev) + ")";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mesh_loader_error
|
||||
|
||||
inline std::error_category& connector_error_category() {
|
||||
static mtl_loader_error::category category;
|
||||
return category;
|
||||
}
|
||||
|
||||
namespace mtl_loader_error {
|
||||
|
||||
inline std::error_code make_error_code(codes e) {
|
||||
return { static_cast<int>(e), connector_error_category() };
|
||||
}
|
||||
|
||||
} // namespace mtl_loader_error
|
||||
|
||||
|
||||
template<typename T, std::size_t Count>
|
||||
std::errc parse_numeric_vector(std::string_view param, std::array<T, Count>& values) {
|
||||
auto it = param.begin(), end = param.end();
|
||||
|
||||
for (auto& value : values)
|
||||
{
|
||||
if (it >= end)
|
||||
{
|
||||
return std::errc::invalid_argument;
|
||||
}
|
||||
|
||||
const auto [ptr, ec] = std::from_chars(it, end, value);
|
||||
|
||||
if (ec != std::errc{})
|
||||
{
|
||||
return ec;
|
||||
}
|
||||
|
||||
it = ptr + 1; // skip space in between components
|
||||
}
|
||||
|
||||
return {};
|
||||
};
|
||||
|
||||
std::optional<dynamic_material_store::id_type> mtl_loader::find_id(std::string_view name)
|
||||
{
|
||||
const auto it = m_id_lookup.find(name);
|
||||
|
||||
if (it == m_id_lookup.end())
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void mtl_loader::clear_name_lookup() {
|
||||
m_id_lookup.clear();
|
||||
}
|
||||
|
||||
std::error_code mtl_loader::load_directory(
|
||||
dynamic_data_loader_ctx& ctx,
|
||||
dynamic_material_store& store,
|
||||
components::material::flags enabled_components,
|
||||
const std::filesystem::path& path,
|
||||
const bool pedantic
|
||||
) {
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
if (not fs::exists(path))
|
||||
{
|
||||
return make_error_code(std::errc::no_such_file_or_directory);
|
||||
}
|
||||
|
||||
for (const auto& file : fs::directory_iterator{ path })
|
||||
{
|
||||
const auto& file_path = file.path();
|
||||
|
||||
if (file_path.extension() != ".obj")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (const auto e = load(
|
||||
ctx,
|
||||
store,
|
||||
enabled_components,
|
||||
path,
|
||||
pedantic
|
||||
)) {
|
||||
ztu::logger::error(
|
||||
"Error while loading obj file '%': [%] %",
|
||||
file_path,
|
||||
e.category().name(),
|
||||
e.message()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::error_code mtl_loader::load(
|
||||
dynamic_data_loader_ctx& ctx,
|
||||
dynamic_material_store& store,
|
||||
components::material::flags enabled_components,
|
||||
const std::filesystem::path& filename,
|
||||
const bool pedantic
|
||||
) {
|
||||
using mtl_loader_error::codes;
|
||||
using mtl_loader_error::make_error_code;
|
||||
|
||||
using flags = components::material::flags;
|
||||
|
||||
const auto component_disabled = [&](const components::material::flags component) {
|
||||
return (enabled_components & component) == flags::none;
|
||||
};
|
||||
|
||||
// TODO unroll stuff
|
||||
const auto textures_disabled = component_disabled(flags::ambient_filter_texture);
|
||||
const auto surface_properties_disabled = component_disabled(flags::surface_properties);
|
||||
const auto transparencies_disabled = component_disabled(flags::transparency);
|
||||
|
||||
auto in = std::ifstream{ filename };
|
||||
if (not in.is_open()) {
|
||||
return make_error_code(codes::mtl_cannot_open_file);
|
||||
}
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
const auto directory = fs::canonical(fs::path(filename).parent_path());
|
||||
|
||||
auto name = std::string{};
|
||||
auto material = dynamic_material_data{};
|
||||
|
||||
const auto push_material = [&]()
|
||||
{
|
||||
if (not name.empty())
|
||||
{
|
||||
const auto id = store.add(std::move(material));
|
||||
m_id_lookup.emplace(std::move(name), id);
|
||||
}
|
||||
name = std::string{};
|
||||
material = dynamic_material_data{};
|
||||
};
|
||||
|
||||
const auto load_texture = [&](
|
||||
const std::string_view path,
|
||||
std::string_view texture_type_name,
|
||||
auto&& f
|
||||
) {
|
||||
auto texture_filename = fs::path(path);
|
||||
if (texture_filename.is_relative())
|
||||
{
|
||||
texture_filename = directory / texture_filename;
|
||||
}
|
||||
|
||||
const auto extension = texture_filename.extension().string();
|
||||
|
||||
auto texture_type = std::string_view{ extension };
|
||||
if (not texture_type.empty() and texture_type.front() == '.')
|
||||
{
|
||||
texture_type = texture_type.substr(1);
|
||||
}
|
||||
|
||||
if (const auto loader_id = ctx.texture_loader.find_loader(texture_type))
|
||||
{
|
||||
if (auto res = ctx.texture_loader.read(
|
||||
ctx,
|
||||
*loader_id,
|
||||
texture_filename,
|
||||
pedantic
|
||||
)) {
|
||||
f(*res);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto error = res.error();
|
||||
ztu::logger::warn(
|
||||
"Error while loading % texture '%': [%] %",
|
||||
texture_type_name,
|
||||
path,
|
||||
error.category().name(),
|
||||
error.message()
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ztu::logger::warn(
|
||||
"Failed to load % texture '%' because extension is not supported.",
|
||||
texture_type_name,
|
||||
path
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const auto ec = ztu::parse_lines<codes>(
|
||||
in,
|
||||
pedantic,
|
||||
ztu::make_line_parser("newmtl ", ztu::is_not_repeating, [&](const auto& param)
|
||||
{
|
||||
push_material();
|
||||
name = param;
|
||||
|
||||
return codes::ok;
|
||||
}),
|
||||
ztu::make_line_parser("Ka ", ztu::is_not_repeating, [&](const auto& param)
|
||||
{
|
||||
if (surface_properties_disabled) return codes::ok;
|
||||
|
||||
auto& properties = material.initialized_surface_properties();
|
||||
if (parse_numeric_vector(param, properties.ambient_filter) != std::errc{}) [[unlikely]]
|
||||
{
|
||||
return codes::mtl_malformed_ambient_color;
|
||||
}
|
||||
|
||||
material.components() |= flags::surface_properties;
|
||||
|
||||
return codes::ok;
|
||||
}),
|
||||
ztu::make_line_parser("Kd ", ztu::is_not_repeating, [&](const auto& param)
|
||||
{
|
||||
if (surface_properties_disabled) return codes::ok;
|
||||
|
||||
auto& properties = material.initialized_surface_properties();
|
||||
if (parse_numeric_vector(param, properties.diffuse_filter) != std::errc{}) [[unlikely]]
|
||||
{
|
||||
return codes::mtl_malformed_diffuse_color;
|
||||
}
|
||||
|
||||
material.components() |= flags::surface_properties;
|
||||
|
||||
return codes::ok;
|
||||
}),
|
||||
ztu::make_line_parser("Ks ", ztu::is_not_repeating, [&](const auto& param)
|
||||
{
|
||||
if (surface_properties_disabled) return codes::ok;
|
||||
|
||||
auto& properties = material.initialized_surface_properties();
|
||||
if (parse_numeric_vector(param, properties.specular_filter) != std::errc{}) [[unlikely]]
|
||||
{
|
||||
return codes::mtl_malformed_specular_color;
|
||||
}
|
||||
|
||||
material.components() |= flags::surface_properties;
|
||||
|
||||
return codes::ok;
|
||||
}),
|
||||
ztu::make_line_parser("Ns ", ztu::is_not_repeating, [&](const auto& param)
|
||||
{
|
||||
if (surface_properties_disabled) return codes::ok;
|
||||
|
||||
auto& properties = material.initialized_surface_properties();
|
||||
std::array<float, 1> shininess{};
|
||||
if (parse_numeric_vector(param, shininess) != std::errc{}) [[unlikely]]
|
||||
{
|
||||
return codes::mtl_malformed_specular_exponent;
|
||||
}
|
||||
|
||||
properties.shininess = shininess.front();
|
||||
material.components() |= flags::surface_properties;
|
||||
|
||||
return codes::ok;
|
||||
}),
|
||||
ztu::make_line_parser("d ", ztu::is_not_repeating, [&](const auto& param)
|
||||
{
|
||||
if (transparencies_disabled) return codes::ok;
|
||||
|
||||
std::array<float, 1> transparency{};
|
||||
if (parse_numeric_vector(param, transparency) != std::errc{}) [[unlikely]]
|
||||
{
|
||||
return codes::mtl_malformed_dissolve;
|
||||
}
|
||||
|
||||
material.transparency().emplace(transparency.front());
|
||||
material.components() |= flags::transparency;
|
||||
|
||||
return codes::ok;
|
||||
}),
|
||||
ztu::make_line_parser("map_Ka ", ztu::is_not_repeating, [&](const auto& param)
|
||||
{
|
||||
if (textures_disabled) return codes::ok;
|
||||
|
||||
load_texture(param, "ambient color", [&](const auto id) {
|
||||
material.ambient_color_texture_id() = id;
|
||||
material.components() |= flags::ambient_filter_texture;
|
||||
});
|
||||
|
||||
return codes::ok;
|
||||
}),
|
||||
ztu::make_line_parser("map_Kd ", ztu::is_not_repeating, [&](const auto& param)
|
||||
{
|
||||
if (textures_disabled) return codes::ok;
|
||||
|
||||
load_texture(param, "diffuse color", [&](const auto id) {
|
||||
material.diffuse_color_texture_id() = id;
|
||||
material.components() |= flags::diffuse_filter_texture;
|
||||
});
|
||||
|
||||
return codes::ok;
|
||||
}),
|
||||
ztu::make_line_parser("map_Ks ", ztu::is_not_repeating, [&](const auto& param)
|
||||
{
|
||||
if (textures_disabled) return codes::ok;
|
||||
|
||||
load_texture(param, "specular color", [&](const auto id) {
|
||||
material.specular_color_texture_id() = id;
|
||||
material.components() |= flags::specular_filter_texture;
|
||||
});
|
||||
|
||||
return codes::ok;
|
||||
}),
|
||||
ztu::make_line_parser("map_Ns ", ztu::is_not_repeating, [&](const auto& param)
|
||||
{
|
||||
if (textures_disabled) return codes::ok;
|
||||
|
||||
load_texture(param, "shininess", [&](const auto id) {
|
||||
material.shininess_texture_id() = id;
|
||||
material.components() |= flags::shininess_texture;
|
||||
});
|
||||
|
||||
return codes::ok;
|
||||
}),
|
||||
ztu::make_line_parser("map_d ", ztu::is_not_repeating, [&](const auto& param)
|
||||
{
|
||||
if (textures_disabled) return codes::ok;
|
||||
|
||||
load_texture(param, "alpha", [&](const auto id) {
|
||||
material.alpha_texture_id() = id;
|
||||
material.components() |= flags::alpha_texture;
|
||||
});
|
||||
|
||||
return codes::ok;
|
||||
}),
|
||||
ztu::make_line_parser("bump ", ztu::is_not_repeating, [&](const auto& param)
|
||||
{
|
||||
if (textures_disabled) return codes::ok;
|
||||
|
||||
load_texture(param, "bump", [&](const auto id) {
|
||||
material.bump_texture_id() = id;
|
||||
material.components() |= flags::bump_texture;
|
||||
});
|
||||
|
||||
return codes::ok;
|
||||
})
|
||||
);
|
||||
|
||||
if (ec != codes::ok)
|
||||
{
|
||||
return make_error_code(ec);
|
||||
}
|
||||
|
||||
push_material();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
450
source/assets/data_loaders/obj_loader.cpp
Executable file
450
source/assets/data_loaders/obj_loader.cpp
Executable file
@@ -0,0 +1,450 @@
|
||||
#include "assets/data_loaders/obj_loader.hpp"
|
||||
|
||||
#include <charconv>
|
||||
#include <fstream>
|
||||
#include <array>
|
||||
|
||||
#include "assets/components/mesh_vertex_components.hpp"
|
||||
#include "assets/dynamic_data_loaders/dynamic_material_loader.hpp"
|
||||
|
||||
#include "util/logger.hpp"
|
||||
#include "util/for_each.hpp"
|
||||
#include "util/uix.hpp"
|
||||
#include <set>
|
||||
|
||||
#include "util/line_parser.hpp"
|
||||
|
||||
namespace obj_loader_error {
|
||||
|
||||
struct category : std::error_category {
|
||||
[[nodiscard]] const char* name() const noexcept override {
|
||||
return "connector";
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string message(int ev) const override {
|
||||
switch (static_cast<codes>(ev)) {
|
||||
using enum codes;
|
||||
case obj_cannot_open_file:
|
||||
return "Cannot open given obj file.";
|
||||
case obj_malformed_vertex:
|
||||
return "File contains malformed 'v' statement.";
|
||||
case obj_malformed_texture_coordinate:
|
||||
return "File contains malformed 'vt' statement.";
|
||||
case obj_malformed_normal:
|
||||
return "File contains malformed 'vn' statement.";
|
||||
case obj_malformed_face:
|
||||
return "File contains malformed 'f' statement.";
|
||||
case obj_face_index_out_of_range:
|
||||
return "Face index out of range.";
|
||||
case obj_unknown_line_begin:
|
||||
return "Unknown obj line begin.";
|
||||
default:
|
||||
using namespace std::string_literals;
|
||||
return "unrecognized error ("s + std::to_string(ev) + ")";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mesh_loader_error
|
||||
|
||||
inline std::error_category& connector_error_category() {
|
||||
static obj_loader_error::category category;
|
||||
return category;
|
||||
}
|
||||
|
||||
namespace obj_loader_error {
|
||||
|
||||
inline std::error_code make_error_code(codes e) {
|
||||
return { static_cast<int>(e), connector_error_category() };
|
||||
}
|
||||
|
||||
} // namespace mesh_loader_error
|
||||
|
||||
|
||||
using vertex_type = std::array<dynamic_mesh_data::index_type, 3>;
|
||||
|
||||
struct indexed_vertex_type {
|
||||
vertex_type vertex;
|
||||
ztu::u32 buffer_index;
|
||||
|
||||
friend auto operator<=>(const indexed_vertex_type& a, const indexed_vertex_type& b) {
|
||||
return a.vertex <=> b.vertex;
|
||||
}
|
||||
|
||||
bool operator==(const indexed_vertex_type& other) const noexcept {
|
||||
return other.vertex == vertex;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// TODO add compile time selection and unrolling
|
||||
template<typename T, std::size_t Count>
|
||||
std::errc parse_numeric_vector(std::string_view param, std::array<T, Count>& values) {
|
||||
auto it = param.begin(), end = param.end();
|
||||
|
||||
for (auto& value : values)
|
||||
{
|
||||
if (it >= end)
|
||||
{
|
||||
return std::errc::invalid_argument;
|
||||
}
|
||||
|
||||
const auto [ptr, ec] = std::from_chars(it, end, value);
|
||||
|
||||
if (ec != std::errc{})
|
||||
{
|
||||
return ec;
|
||||
}
|
||||
|
||||
it = ptr + 1; // skip space in between components
|
||||
}
|
||||
|
||||
return {};
|
||||
};
|
||||
|
||||
std::error_code obj_loader::load_directory(
|
||||
dynamic_data_loader_ctx& ctx,
|
||||
dynamic_mesh_store& store,
|
||||
components::mesh_vertex::flags enabled_components,
|
||||
const std::filesystem::path& path,
|
||||
const bool pedantic
|
||||
) {
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
if (not fs::exists(path))
|
||||
{
|
||||
return make_error_code(std::errc::no_such_file_or_directory);
|
||||
}
|
||||
|
||||
for (const auto& file : fs::directory_iterator{ path })
|
||||
{
|
||||
const auto& file_path = file.path();
|
||||
|
||||
if (file_path.extension() != ".obj")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (const auto e = load(
|
||||
ctx,
|
||||
store,
|
||||
enabled_components,
|
||||
path,
|
||||
pedantic
|
||||
)) {
|
||||
ztu::logger::error(
|
||||
"Error while loading obj file '%': [%] %",
|
||||
file_path,
|
||||
e.category().name(),
|
||||
e.message()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
// TODO refactor so there is a function like parse_normals etc.
|
||||
|
||||
std::error_code obj_loader::load(
|
||||
dynamic_data_loader_ctx& ctx,
|
||||
dynamic_mesh_store& store,
|
||||
components::mesh_vertex::flags enabled_components,
|
||||
const std::filesystem::path& filename,
|
||||
const bool pedantic
|
||||
) {
|
||||
using obj_loader_error::codes;
|
||||
using obj_loader_error::make_error_code;
|
||||
|
||||
auto in = std::ifstream{ filename };
|
||||
if (not in.is_open()) {
|
||||
return make_error_code(codes::obj_cannot_open_file);
|
||||
}
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
const auto directory = fs::path(filename).parent_path();
|
||||
|
||||
// Each vertex of a face can represent a unique combination of vertex-/texture-/normal-coordinates.
|
||||
// But some combinations may occur more than once, for example on every corner of a cube 3 triangles will
|
||||
// reference the exact same corner vertex.
|
||||
// To get the best rendering performance and lowest final memory footprint these duplicates
|
||||
// need to be removed. So this std::set lookup is used to identify the aforementioned duplicates
|
||||
// and only push unique combinations to the buffers.
|
||||
std::set<indexed_vertex_type> vertex_ids;
|
||||
|
||||
auto mesh = dynamic_mesh_data{};
|
||||
|
||||
// Buffers
|
||||
auto position_buffer = mesh.positions();
|
||||
auto normal_buffer = mesh.normals();
|
||||
auto tex_coord_buffer = mesh.tex_coords();
|
||||
|
||||
std::unordered_map<std::string, ztu::u32> material_name_lookup;
|
||||
|
||||
|
||||
constexpr auto mtl_loader_id = *ctx.material_loader.find_loader_static("mtl");
|
||||
mtl_loader& material_loader = ctx.material_loader.get_loader<mtl_loader_id>();
|
||||
|
||||
material_loader.clear_name_lookup();
|
||||
|
||||
std::string material_name;
|
||||
|
||||
const auto push_mesh = [&](const bool clear_buffers = false) {
|
||||
|
||||
if (not mesh.positions().empty())
|
||||
{
|
||||
// Copy buffers instead of moving to keep capacity for further parsing
|
||||
// and have the final buffers be shrunk to size.
|
||||
if (not material_name.empty()) {
|
||||
if (const auto id = material_loader.find_id(material_name))
|
||||
{
|
||||
mesh.material_id() = *id;
|
||||
}
|
||||
else
|
||||
{
|
||||
ztu::logger::warn(
|
||||
"Could not find material '%'.",
|
||||
material_name
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ztu::logger::debug("Parsed % positions.", mesh.positions().size());
|
||||
ztu::logger::debug("Parsed % normals.", mesh.normals().size());
|
||||
ztu::logger::debug("Parsed % tex_coords.", mesh.tex_coords().size());
|
||||
|
||||
if (not mesh.positions().empty())
|
||||
{
|
||||
mesh.components() |= components::mesh_vertex::flags::position;
|
||||
}
|
||||
|
||||
if (not mesh.normals().empty())
|
||||
{
|
||||
mesh.components() |= components::mesh_vertex::flags::normal;
|
||||
}
|
||||
|
||||
if (not mesh.tex_coords().empty())
|
||||
{
|
||||
mesh.components() |= components::mesh_vertex::flags::tex_coord;
|
||||
}
|
||||
|
||||
ztu::logger::debug("Pushing obj mesh with % triangles.", mesh.triangles().size());
|
||||
|
||||
store.add(std::move(mesh));
|
||||
}
|
||||
|
||||
if (clear_buffers)
|
||||
{
|
||||
position_buffer.clear();
|
||||
normal_buffer.clear();
|
||||
tex_coord_buffer.clear();
|
||||
}
|
||||
|
||||
mesh = dynamic_mesh_data{};
|
||||
|
||||
vertex_ids.clear();
|
||||
material_name.clear();
|
||||
};
|
||||
|
||||
const auto find_or_push_vertex = [&](const vertex_type& vertex) -> ztu::u32 {
|
||||
|
||||
auto indexed_vid = indexed_vertex_type{
|
||||
.vertex = vertex,
|
||||
.buffer_index = static_cast<ztu::u32>(mesh.positions().size())
|
||||
};
|
||||
|
||||
// Search through sorted lookup to check if index combination is unique
|
||||
const auto [ id_it, unique ] = vertex_ids.insert(indexed_vid);
|
||||
|
||||
if (unique)
|
||||
{
|
||||
const auto& [ position_index, tex_coord_index, normal_index ] = vertex;
|
||||
|
||||
if (position_index < position_buffer.size())
|
||||
{
|
||||
mesh.positions().emplace_back(position_buffer[position_index]);
|
||||
}
|
||||
|
||||
if (normal_index < normal_buffer.size())
|
||||
{
|
||||
mesh.normals().emplace_back(normal_buffer[normal_index]);
|
||||
}
|
||||
|
||||
if (tex_coord_index < tex_coord_buffer.size())
|
||||
{
|
||||
mesh.tex_coords().emplace_back(tex_coord_buffer[tex_coord_index]);
|
||||
}
|
||||
}
|
||||
|
||||
return id_it->buffer_index;
|
||||
};
|
||||
|
||||
using flags = components::mesh_vertex::flags;
|
||||
|
||||
const auto component_disabled = [&](const flags component) {
|
||||
return (enabled_components & component) == flags::none;
|
||||
};
|
||||
|
||||
const auto positions_disabled = component_disabled(flags::position);
|
||||
const auto normals_disabled = component_disabled(flags::normal);
|
||||
const auto tex_coords_disabled = component_disabled(flags::tex_coord);
|
||||
|
||||
const auto ec = ztu::parse_lines<codes>(
|
||||
in,
|
||||
pedantic,
|
||||
ztu::make_line_parser("v ", ztu::is_repeating, [&](const auto& param)
|
||||
{
|
||||
if (positions_disabled) return codes::ok;
|
||||
|
||||
components::mesh_vertex::position position;
|
||||
if (parse_numeric_vector(param, position) != std::errc{}) [[unlikely]]
|
||||
{
|
||||
return codes::obj_malformed_vertex;
|
||||
}
|
||||
|
||||
position_buffer.push_back(position);
|
||||
|
||||
return codes::ok;
|
||||
}),
|
||||
ztu::make_line_parser("vt ", ztu::is_repeating, [&](const auto& param) {
|
||||
if (tex_coords_disabled) return codes::ok;
|
||||
|
||||
components::mesh_vertex::tex_coord coord;
|
||||
if (parse_numeric_vector(param, coord) != std::errc{}) [[unlikely]]
|
||||
{
|
||||
return codes::obj_malformed_texture_coordinate;
|
||||
}
|
||||
|
||||
tex_coord_buffer.push_back(coord);
|
||||
|
||||
return codes::ok;
|
||||
}),
|
||||
ztu::make_line_parser("vn ", ztu::is_repeating, [&](const auto& param)
|
||||
{
|
||||
if (normals_disabled) return codes::ok;
|
||||
|
||||
components::mesh_vertex::normal normal;
|
||||
if (parse_numeric_vector(param, normal) != std::errc{}) [[unlikely]]
|
||||
{
|
||||
return codes::obj_malformed_normal;
|
||||
}
|
||||
|
||||
normal_buffer.push_back(normal);
|
||||
|
||||
return codes::ok;
|
||||
}),
|
||||
ztu::make_line_parser("o ", ztu::is_not_repeating, [&](const auto&)
|
||||
{
|
||||
push_mesh(); // Name is currently ignored
|
||||
return codes::ok;
|
||||
}),
|
||||
ztu::make_line_parser("f ", ztu::is_repeating, [&](const auto& param)
|
||||
{
|
||||
const auto begin = param.begin().base();
|
||||
const auto end = param.end().base();
|
||||
|
||||
auto vertex = vertex_type{};
|
||||
|
||||
ztu::u32 first_index{}, prev_index{};
|
||||
|
||||
auto vertex_count = std::size_t{};
|
||||
|
||||
for (auto it = begin; it <= end; ++it)
|
||||
{
|
||||
|
||||
for (auto& component_index : vertex)
|
||||
{
|
||||
if (it != end and *it == '/')
|
||||
{
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto [ptr, ec] = std::from_chars(it, end, component_index);
|
||||
if (ec != std::errc()) [[unlikely]]
|
||||
{
|
||||
// Discard whole face if one index is malformed
|
||||
return codes::obj_malformed_face;
|
||||
}
|
||||
|
||||
--component_index; // Indices start at one
|
||||
it = ptr;
|
||||
|
||||
if (it == end or *it != '/')
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
++it;
|
||||
}
|
||||
|
||||
++vertex_count;
|
||||
|
||||
if (it != end and *it != ' ') [[unlikely]]
|
||||
{
|
||||
return codes::obj_malformed_face;
|
||||
}
|
||||
|
||||
const auto curr_index = find_or_push_vertex(vertex);
|
||||
|
||||
if (vertex_count >= 3)
|
||||
{
|
||||
auto& triangle = mesh.triangles().emplace_back();
|
||||
triangle[0] = first_index;
|
||||
triangle[1] = prev_index;
|
||||
triangle[2] = curr_index;
|
||||
}
|
||||
else if (vertex_count == 1)
|
||||
{
|
||||
first_index = curr_index;
|
||||
}
|
||||
|
||||
prev_index = curr_index;
|
||||
}
|
||||
|
||||
return codes::ok;
|
||||
}),
|
||||
ztu::make_line_parser("usemtl ", ztu::is_not_repeating, [&](const auto& param)
|
||||
{
|
||||
push_mesh(false);
|
||||
|
||||
material_name = param;
|
||||
|
||||
return codes::ok;
|
||||
}),
|
||||
ztu::make_line_parser("mtllib ", ztu::is_not_repeating, [&](const auto& param)
|
||||
{
|
||||
auto material_filename = fs::path(param);
|
||||
if (material_filename.is_relative())
|
||||
{
|
||||
material_filename = directory / material_filename;
|
||||
}
|
||||
|
||||
if (const auto error = ctx.material_loader.read(
|
||||
ctx,
|
||||
mtl_loader_id,
|
||||
material_filename,
|
||||
pedantic
|
||||
)) {
|
||||
ztu::logger::warn(
|
||||
"Error occurred while loading mtl files '%': [%] %",
|
||||
material_filename,
|
||||
error.category().name(),
|
||||
error.message()
|
||||
);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
material_loader.clear_name_lookup();
|
||||
|
||||
if (ec != codes::ok)
|
||||
{
|
||||
return make_error_code(ec);
|
||||
}
|
||||
|
||||
push_mesh();
|
||||
|
||||
return {};
|
||||
}
|
||||
253
source/assets/data_loaders/stl_loader.cpp
Normal file
253
source/assets/data_loaders/stl_loader.cpp
Normal file
@@ -0,0 +1,253 @@
|
||||
#include "assets/data_loaders/stl_loader.hpp"
|
||||
|
||||
#include "util/binary_ifstream.hpp"
|
||||
#include "util/unroll_bool_template.hpp"
|
||||
#include "util/logger.hpp"
|
||||
|
||||
template<bool Normals>
|
||||
std::error_code read_body(
|
||||
binary_ifstream& in,
|
||||
const std::uint32_t expected_triangle_count,
|
||||
std::vector<components::mesh_vertex::position>& positions,
|
||||
std::vector<components::mesh_vertex::normal>& normals,
|
||||
std::vector<std::array<ztu::u32, 3>>& triangles
|
||||
) {
|
||||
|
||||
const auto read_vector = [&in](auto& vector) -> std::error_code
|
||||
{
|
||||
for (auto& component : vector)
|
||||
{
|
||||
float component32;
|
||||
if (const auto e = in.read_ieee754<std::endian::little>(component32))
|
||||
{
|
||||
return e;
|
||||
}
|
||||
component = component32;
|
||||
}
|
||||
return {};
|
||||
};
|
||||
|
||||
for (std::uint32_t i{}; i != expected_triangle_count; ++i) {
|
||||
|
||||
auto normal = components::mesh_vertex::normal{};
|
||||
if constexpr (Normals)
|
||||
{
|
||||
if (const auto e = read_vector(normal))
|
||||
{
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
auto triangle = std::array<ztu::u32, 3>{};
|
||||
|
||||
for (auto& index : triangle) {
|
||||
|
||||
auto position = components::mesh_vertex::position{};
|
||||
if (const auto e = read_vector(position))
|
||||
{
|
||||
return e;
|
||||
}
|
||||
|
||||
// TODO implement unique insert correctly
|
||||
/*// Insert vertices sorted, to efficiently remove duplicates.
|
||||
const auto it = std::ranges::upper_bound(positions, position);
|
||||
|
||||
// Set index before `it` is invalidated by insert.
|
||||
index = it - positions.begin();
|
||||
|
||||
if (it != positions.begin() and *std::prev(it) == position)
|
||||
{
|
||||
--index;
|
||||
}
|
||||
else
|
||||
{
|
||||
positions.insert(it, position);
|
||||
if constexpr (Normals)
|
||||
{
|
||||
normals.insert(normals.begin() + index, normal);
|
||||
}
|
||||
}*/
|
||||
|
||||
index = positions.size();
|
||||
positions.push_back(position);
|
||||
|
||||
if constexpr (Normals)
|
||||
{
|
||||
normals.push_back(normal);
|
||||
}
|
||||
}
|
||||
|
||||
triangles.push_back(triangle);
|
||||
|
||||
// Skip attribute bytes
|
||||
if (const auto e = in.skip<std::uint16_t>())
|
||||
{
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::error_code stl_loader::read_directory(
|
||||
const std::filesystem::path& path,
|
||||
std::vector<dynamic_mesh_data>& meshes,
|
||||
components::mesh_vertex::flags enabled_components::mesh_vertexs,
|
||||
std::vector<dynamic_material_data>& materials,
|
||||
material_component::flags enabled_material_components,
|
||||
const ztu::u32 base_material_id,
|
||||
bool pedantic
|
||||
) {
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
if (not fs::exists(path)) {
|
||||
return make_error_code(std::errc::no_such_file_or_directory);
|
||||
}
|
||||
|
||||
for (const auto& file : fs::directory_iterator{ path / "frames" })
|
||||
{
|
||||
const auto& file_path = file.path();
|
||||
|
||||
if (file_path.extension() != ".stl")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (const auto e = read(
|
||||
file_path,
|
||||
meshes,
|
||||
enabled_components::mesh_vertexs,
|
||||
materials,
|
||||
enabled_material_components,
|
||||
base_material_id,
|
||||
pedantic
|
||||
)) {
|
||||
ztu::logger::error(
|
||||
"Error while loading stl file '%': [%] %",
|
||||
file_path,
|
||||
e.category().name(),
|
||||
e.message()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
std::error_code stl_loader::read(
|
||||
const std::filesystem::path& filename,
|
||||
std::vector<dynamic_mesh_data>& meshes,
|
||||
components::mesh_vertex::flags enabled_components::mesh_vertexs,
|
||||
std::vector<dynamic_material_data>&,
|
||||
material_component::flags,
|
||||
ztu::u32,
|
||||
const bool pedantic
|
||||
) {
|
||||
auto error = std::error_code{};
|
||||
|
||||
auto in = binary_ifstream{};
|
||||
|
||||
if ((error = in.open(filename, true)))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
auto header_bytes_left = static_cast<binary_ifstream::size_type>(80);
|
||||
|
||||
if (pedantic)
|
||||
{
|
||||
// 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<binary_ifstream::char_type, ascii_magic_string.size()>{};
|
||||
|
||||
if ((error = in.read(magic_bytes)))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
const auto magic_string = std::string_view(
|
||||
reinterpret_cast<const char*>(magic_bytes.data()),
|
||||
magic_bytes.size()
|
||||
);
|
||||
|
||||
if (magic_string == ascii_magic_string)
|
||||
{
|
||||
return std::make_error_code(std::errc::illegal_byte_sequence);
|
||||
}
|
||||
|
||||
header_bytes_left -= ascii_magic_string.size();
|
||||
}
|
||||
|
||||
// Ignore (rest of) header.
|
||||
if ((error = in.skip(header_bytes_left)))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
// Read number of bytes
|
||||
auto expected_triangle_count = std::uint32_t{};
|
||||
|
||||
if ((error = in.read<std::endian::little>(expected_triangle_count)))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
// 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 mesh = dynamic_mesh_data{};
|
||||
|
||||
auto& positions = mesh.positions();
|
||||
auto& normals = mesh.normals();
|
||||
auto& triangles = mesh.triangles();
|
||||
auto& material_id = mesh.material_id();
|
||||
|
||||
material_id = 0; // Set to default material
|
||||
|
||||
positions.reserve(expected_triangle_count * 3);
|
||||
normals.reserve(expected_triangle_count);
|
||||
triangles.reserve(expected_triangle_count);
|
||||
|
||||
const auto normals_enabled = (
|
||||
(enabled_components::mesh_vertexs & components::mesh_vertex::flags::normal) != components::mesh_vertex::flags::none
|
||||
);
|
||||
|
||||
error = unroll_bool_function_template([&]<bool Normals>() {
|
||||
return read_body<Normals>(
|
||||
in,expected_triangle_count,
|
||||
positions,
|
||||
normals,
|
||||
triangles
|
||||
);
|
||||
}, normals_enabled);
|
||||
|
||||
// Free any unused reserved memory
|
||||
positions.shrink_to_fit();
|
||||
normals.shrink_to_fit();
|
||||
triangles.shrink_to_fit();
|
||||
|
||||
if (error)
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
ztu::logger::debug("Normal count: %", normals.size());
|
||||
|
||||
if (not positions.empty())
|
||||
{
|
||||
mesh.components() |= components::mesh_vertex::flags::position;
|
||||
}
|
||||
|
||||
if (not normals.empty())
|
||||
{
|
||||
ztu::logger::debug("Enabling normals!!!");
|
||||
mesh.components() |= components::mesh_vertex::flags::normal;
|
||||
}
|
||||
|
||||
meshes.emplace_back(std::move(mesh));
|
||||
|
||||
return {};
|
||||
}
|
||||
136
source/assets/data_loaders/threedtk_pose_loader.cpp
Normal file
136
source/assets/data_loaders/threedtk_pose_loader.cpp
Normal file
@@ -0,0 +1,136 @@
|
||||
#include "assets/data_loaders/threedtk_pose_loader.hpp"
|
||||
|
||||
#include "assets/dynamic_read_buffers/dynamic_pose_buffer.hpp"
|
||||
|
||||
#include <fstream>
|
||||
#include <glm/ext/matrix_transform.hpp>
|
||||
#include <glm/gtx/euler_angles.hpp>
|
||||
|
||||
#include "util/logger.hpp"
|
||||
|
||||
|
||||
|
||||
inline std::error_code threedtk_pose_loader::parse_transform_info(
|
||||
std::ifstream& in,
|
||||
std::string& line,
|
||||
std::array<glm::vec3, 2>& transform_info
|
||||
) {
|
||||
|
||||
for (std::size_t row{}; row != 2; ++row) {
|
||||
|
||||
std::getline(in, line);
|
||||
|
||||
auto it = line.cbegin().base();
|
||||
auto end = line.cend().base();
|
||||
|
||||
for (glm::vec3::length_type col{}; col != 3; ++col) {
|
||||
|
||||
const auto [ ptr, ec ] = std::from_chars(
|
||||
it, end,
|
||||
transform_info[row][col],
|
||||
std::chars_format::general
|
||||
);
|
||||
|
||||
if (ec != std::errc{}) {
|
||||
return std::make_error_code(ec);
|
||||
}
|
||||
|
||||
it = ptr + 1; // skip space in between components
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline ztu::result<pose_prefetch_lookup::index_type> threedtk_pose_loader::parse_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::make_error_code(std::errc::invalid_argument);
|
||||
}
|
||||
|
||||
name_view = name_view.substr(prefix.length());
|
||||
|
||||
pose_prefetch_lookup::index_type index;
|
||||
|
||||
const auto res = std::from_chars(name_view.begin(), name_view.end(), index);
|
||||
|
||||
if (res.ec != std::errc{}) [[unlikely]]
|
||||
{
|
||||
return std::make_error_code(res.ec);
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
|
||||
void threedtk_pose_loader::load(
|
||||
const ztu::string_list& filenames,
|
||||
dynamic_pose_store& store,
|
||||
pose_prefetch_lookup& id_lookup
|
||||
) {
|
||||
|
||||
auto filename_buffer = std::string{};
|
||||
auto in = std::ifstream{};
|
||||
auto line = std::string{};
|
||||
auto pose_buffer = dynamic_pose_buffer{};
|
||||
|
||||
for (const auto filename : filenames)
|
||||
{
|
||||
|
||||
pose_prefetch_lookup::index_type index;
|
||||
if (const auto res = parse_index(filename))
|
||||
{
|
||||
index = *res;
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto error = res.error();
|
||||
ztu::logger::error(
|
||||
"Error while parsing 3dtk pose file index %: [%] %",
|
||||
filename,
|
||||
error.category().name(),
|
||||
error.message()
|
||||
);
|
||||
}
|
||||
|
||||
filename_buffer = filename;
|
||||
in.open(filename_buffer.c_str());
|
||||
if (not in.is_open()) {
|
||||
ztu::logger::error("Cannot open 3dtk pose file %", filename);
|
||||
continue;
|
||||
}
|
||||
|
||||
std::array<glm::vec3, 2> transform_info{};
|
||||
|
||||
const auto error = parse_transform_info(in, line, transform_info);
|
||||
|
||||
in.close();
|
||||
|
||||
if (error)
|
||||
{
|
||||
ztu::logger::error(
|
||||
"Error while parsing 3dtk pose file %: [%] %",
|
||||
filename,
|
||||
error.category().name(),
|
||||
error.message()
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& translation = transform_info[0];
|
||||
auto& angles = transform_info[1];
|
||||
angles *= static_cast<float>(M_PI / 180.0);
|
||||
|
||||
pose_buffer = (
|
||||
glm::translate(glm::identity<glm::mat4>(), translation) *
|
||||
glm::eulerAngleXYZ(angles[0], angles[1], angles[2])
|
||||
);
|
||||
|
||||
const auto id = store.add(pose_buffer);
|
||||
id_lookup.emplace(filename, index, id);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user