This commit is contained in:
ZY4N
2024-12-22 16:58:40 +01:00
parent 2704814de2
commit db8db8f9d7
161 changed files with 17102 additions and 0 deletions

View 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;
}
}

View 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 {};
}

View 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;
}

View 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();
}
}

View 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 {};
}

View 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 {};
}

View 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 {};
}

View 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);
}
}

View File

@@ -0,0 +1,41 @@
#include "assets/dynamic_data_loaders/dynamic_mesh_loader.hpp"
std::error_code dynamic_mesh_loader::prefetch(
loader_id_type loader_id,
const ztu::string_list& directories,
prefetch_queue& queue
) {
return this->invoke_with_matching_loader(
loader_id,
[&](auto& loader)
{
return loader.prefetch(
directories,
queue
);
}
);
}
std::error_code dynamic_mesh_loader::load(
loader_id_type loader_id,
const ztu::string_list& directories,
dynamic_mesh_store& store,
mesh_prefetch_lookup& id_lookup,
const bool pedantic
) {
return this->invoke_with_matching_loader(
loader_id,
[&](auto& loader)
{
return loader.load(
m_buffer,
directories,
store,
id_lookup,
pedantic
);
}
);
}

View File

@@ -0,0 +1,40 @@
#include "assets/dynamic_data_loaders/dynamic_point_cloud_loader.hpp"
std::error_code dynamic_point_cloud_loader::prefetch(
const loader_id_type loader_id,
const ztu::string_list& directories,
prefetch_queue& queue
) {
return this->invoke_with_matching_loader(
loader_id,
[&](auto& loader)
{
return loader.prefetch(
directories,
queue
);
}
);
}
std::error_code dynamic_point_cloud_loader::load(
const loader_id_type loader_id,
const ztu::string_list& directories,
dynamic_point_cloud_store& store,
point_cloud_prefetch_lookup& id_lookup,
const bool pedantic
) {
return this->invoke_with_matching_loader(
loader_id,
[&](auto& loader)
{
return loader.load(
m_buffer,
directories,
store,
id_lookup,
pedantic
);
}
);
}

View File

@@ -0,0 +1,122 @@
#include "assets/dynamic_data_loaders/dynamic_texture_loader.hpp"
#if defined(__GNUC__) || defined(__GNUG__)
#pragma GCC diagnostic push
#pragma GCC system_header
#elif defined(_MSC_VER)
#pragma warning(push, 0)
#endif
#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_STATIC
#include "stb_image.h"
#if defined(__GNUC__) || defined(__GNUG__)
#pragma GCC diagnostic pop
#elif defined(_MSC_VER)
#pragma warning(pop)
#endif
#include "util/logger.hpp"
dynamic_texture_loader::dynamic_texture_loader(components::texture::flags enabled_components) :
m_enabled_components{ enabled_components },
m_loader_id_lookup{
{ "jpg", loader_id_type{ 0 } },
{ "png", loader_id_type{ 1 } },
{ "tga", loader_id_type{ 2 } },
{ "bmp", loader_id_type{ 3 } },
{ "psd", loader_id_type{ 4 } },
{ "gif", loader_id_type{ 5 } },
{ "hdr", loader_id_type{ 6 } },
{ "pic", loader_id_type{ 7 } }
} {}
std::optional<dynamic_texture_loader::loader_id_type> dynamic_texture_loader::find_loader(const std::string_view& name)
{
const auto it = m_loader_id_lookup.find(name);
if (it != m_loader_id_lookup.end())
{
return it->second;
}
return std::nullopt;
}
std::error_code dynamic_texture_loader::prefetch(
const loader_id_type loader_id,
const ztu::string_list& directories,
prefetch_queue& queue
) {
// Nothing to prefetch...
return {};
}
std::error_code dynamic_texture_loader::load(
const loader_id_type loader_id,
const ztu::string_list& directories,
dynamic_texture_store& store,
texture_prefetch_lookup& id_lookup,
const bool pedantic
) {
stbi_set_flip_vertically_on_load(true);
int width, height, channels;
for (const auto filename : directories)
{
const auto id_it = id_lookup.find(filename);
if (id_it != id_lookup.end()) [[unlikely]]
{
continue;
}
auto ptr = reinterpret_cast<std::uint8_t*>(stbi_load(
filename.data(), // Null terminated by string_list
&width,
&height,
&channels,
0
));
if (ptr == nullptr) {
return std::make_error_code(std::errc::no_such_file_or_directory);
}
auto data = std::unique_ptr<std::uint8_t[]>(ptr);
using flags = components::texture::flags;
auto components = flags{};
switch (channels)
{
case 1:
components = flags::luminance;
break;
case 2:
components = flags::luminance | flags::alpha;
break;
case 3:
components = flags::red | flags::green | flags::blue;
break;
case 4:
components = flags::red | flags::green | flags::blue | flags::alpha;
break;
default: [[unlikely]]
ztu::logger::error("Unsupported pixel component composition %", static_cast<int>(components));
continue;
}
const auto id = store.add(dynamic_texture_buffer(
std::move(data),
width,
height,
components
));
id_lookup.emplace_hint(id_it, filename, id);
}
return {};
}

View File

@@ -0,0 +1,92 @@
#ifndef INCLUDE_BASE_DYNAMIC_LOADER_IMPLEMENTATION
# error Never include this file directly include 'base_dynamic_loader.hpp'
#endif
#include "util/for_each.hpp"
template<typename C, class... Loaders>
base_dynamic_loader<C, Loaders...>::base_dynamic_loader(const C enabled_components) :
m_enabled_components{ enabled_components }
{
[&]<std::size_t... Is>(std::index_sequence<Is...>) {
m_loader_id_lookup = { { std::string{ Loaders::name }, { Is } }... };
}(std::index_sequence_for<Loaders...>());
}
template<typename C, class... Loaders>
std::optional<typename base_dynamic_loader<C, Loaders...>::loader_id_type> base_dynamic_loader<C, Loaders...>::find_loader(
std::string_view name
) {
const auto it = m_loader_id_lookup.find(name);
if (it != m_loader_id_lookup.end())
{
return it->second;
}
return std::nullopt;
}
template<typename C, class... Loaders>
consteval std::optional<typename base_dynamic_loader<C, Loaders...>::loader_id_type> base_dynamic_loader<C, Loaders...>::find_loader_static(
std::string_view name
) {
constexpr auto invalid_index = std::numeric_limits<typename loader_id_type::index_type>::max();
auto index = invalid_index;
ztu::for_each::indexed_type<Loaders...>([&]<auto Index, typename Loader>()
{
if (name == Loader::name)
{
index = Index;
return true;
}
return false;
});
return index == invalid_index ? std::nullopt : loader_id_type{ index };
}
template<typename C, class... Loaders>
template<typename base_dynamic_loader<C, Loaders...>::loader_id_type ID>
auto& base_dynamic_loader<C, Loaders...>::get_loader()
{
return std::get<ID.index>(m_loaders);
}
template<typename C, class... Loaders>
template<typename F>
ztu::result<dynamic_point_cloud_store::id_type> base_dynamic_loader<C, Loaders...>::invoke_with_matching_loader(
const loader_id_type loader_id, F&& f
) {
return std::apply(
[&](Loaders&... loaders)
{
return [&]<std::size_t... Is>(std::index_sequence<Is...>)
{
std::error_code error;
const auto found_parser = (
[&](auto& loader, const std::size_t index)
{
if (loader_id == index)
{
error = f(loader);
return true;
}
return false;
} (loaders, Is)
or ...
);
if (not found_parser)
{
error = std::make_error_code(std::errc::invalid_argument);
}
return error;
}(std::index_sequence_for<Loaders...>());
},
m_loaders
);
}

View File

@@ -0,0 +1,26 @@
#include "assets/dynamic_data_stores/dynamic_material_store.hpp"
dynamic_material_store::id_type dynamic_material_store::add(const dynamic_material_buffer& buffer)
{
return m_store.add(buffer.data);
}
std::pair<dynamic_material_store::iterator_type, bool> dynamic_material_store::find(const id_type id)
{
return m_store.find(id);
}
std::pair<dynamic_material_store::const_iterator, bool> dynamic_material_store::find(const id_type id) const
{
return m_store.find(id);
}
void dynamic_material_store::remove(const iterator_type& it)
{
m_store.remove(it);
}
void dynamic_material_store::clear()
{
m_store.clear();
}

View File

@@ -0,0 +1,31 @@
#include "assets/dynamic_data_stores/dynamic_mesh_store.hpp"
dynamic_mesh_store::id_type dynamic_mesh_store::add(const dynamic_mesh_buffer& mesh_buffer)
{
const auto& triangles = mesh_buffer.triangles();
return m_store.add(
std::span{ triangles.front().data(), triangles.size() * 3 },
mesh_buffer.vertices
);
}
std::pair<dynamic_mesh_store::iterator_type, bool> dynamic_mesh_store::find(id_type id)
{
return m_store.find(id);
}
std::pair<dynamic_mesh_store::const_iterator, bool> dynamic_mesh_store::find(id_type id) const
{
return m_store.find(id);
}
void dynamic_mesh_store::remove(const iterator_type& it)
{
m_store.remove(it);
}
void dynamic_mesh_store::clear()
{
m_store.clear();
}

View File

@@ -0,0 +1,27 @@
#include "assets/dynamic_data_stores/dynamic_point_cloud_store.hpp"
dynamic_point_cloud_store::id_type dynamic_point_cloud_store::add(const dynamic_point_cloud_buffer& mesh_buffer)
{
return m_store.add(mesh_buffer.vertices);
}
std::pair<dynamic_point_cloud_store::iterator_type, bool> dynamic_point_cloud_store::find(id_type id)
{
return m_store.find(id);
}
std::pair<dynamic_point_cloud_store::const_iterator, bool> dynamic_point_cloud_store::find(id_type id) const
{
return m_store.find(id);
}
void dynamic_point_cloud_store::remove(const iterator_type& it)
{
m_store.remove(it);
}
void dynamic_point_cloud_store::clear()
{
m_store.clear();
}

View File

@@ -0,0 +1,387 @@
#ifndef INCLUDE_GENERIC_DYNAMIC_COMPONENT_ARRAY_STORE_IMPLEMENTATION
# error Never include this file directly include 'generic_dynamic_component_array_store.hpp'
#endif
#include <bits/ranges_algo.h>
template<typename C, typename... Ts>
component_array_iterator<C, Ts...>::component_array_iterator(
component_array_pointer_type components,
flag_count_pointer_type flags,
std::size_t index,
const offsets_type& offsets
) :
m_components{ components },
m_flag_counts{ flags },
m_index{ index },
m_offsets{ offsets } {}
template<typename C, typename... Ts>
typename component_array_iterator<C, Ts...>::reference component_array_iterator<C, Ts...>::operator*() const {
return dereference(std::index_sequence_for<Ts...>{});
}
template<typename C, typename... Ts>
component_array_iterator<C, Ts...>& component_array_iterator<C, Ts...>::operator++() {
adjust_offsets(std::index_sequence_for<Ts...>{}, 1);
++m_index;
return *this;
}
template<typename C, typename... Ts>
component_array_iterator<C, Ts...> component_array_iterator<C, Ts...>::operator++(int) {
component_array_iterator tmp = *this;
++(*this);
return tmp;
}
template<typename C, typename... Ts>
component_array_iterator<C, Ts...>& component_array_iterator<C, Ts...>::operator--() {
adjust_offsets(std::index_sequence_for<Ts...>{}, -1);
--m_index;
return *this;
}
template<typename C, typename... Ts>
component_array_iterator<C, Ts...> component_array_iterator<C, Ts...>::operator--(int) {
auto tmp = *this;
--(*this);
return tmp;
}
template<typename C, typename... Ts>
component_array_iterator<C, Ts...>& component_array_iterator<C, Ts...>::operator+=(const difference_type n)
{
adjust_offsets(std::index_sequence_for<Ts...>{}, n);
m_index += n;
return *this;
}
template<typename C, typename... Ts>
component_array_iterator<C, Ts...>& component_array_iterator<C, Ts...>::operator-=(const difference_type n)
{
return (*this) += -n;
}
template<typename C, typename... Ts>
component_array_iterator<C, Ts...> component_array_iterator<C, Ts...>::operator+(const difference_type n) const
{
auto tmp = *this;
return tmp += n;
}
template<typename C, typename... Ts>
component_array_iterator<C, Ts...> component_array_iterator<C, Ts...>::operator-(const difference_type n) const
{
auto tmp = *this;
return tmp -= n;
}
template<typename C, typename... Ts>
typename component_array_iterator<C, Ts...>::difference_type
component_array_iterator<C, Ts...>::operator-(const component_array_iterator& other) const
{
return static_cast<difference_type>(m_index) - static_cast<difference_type>(other.m_index);
}
template<typename C, typename... Ts>
typename component_array_iterator<C, Ts...>::reference component_array_iterator<C, Ts...>::operator[](
const difference_type n
) const {
return *((*this) + n);
}
template<typename C, typename... Ts>
bool component_array_iterator<C, Ts...>::operator==(const component_array_iterator& other) const
{
return m_components == other.m_components and m_index == other.m_index;
}
template<typename C, typename... Ts>
bool component_array_iterator<C, Ts...>::operator!=(const component_array_iterator& other) const
{
return not (*this == other);
}
template<typename C, typename... Ts>
bool component_array_iterator<C, Ts...>::operator<(const component_array_iterator& other) const
{
return m_index < other.m_index;
}
template<typename C, typename... Ts>
bool component_array_iterator<C, Ts...>::operator<=(const component_array_iterator& other) const
{
return m_index <= other.m_index;
}
template<typename C, typename... Ts>
bool component_array_iterator<C, Ts...>::operator>(const component_array_iterator& other) const
{
return m_index > other.m_index;
}
template<typename C, typename... Ts>
bool component_array_iterator<C, Ts...>::operator>=(const component_array_iterator& other) const
{
return m_index >= other.m_index;
}
template<typename C, typename... Ts>
template<std::size_t I>
bool component_array_iterator<C, Ts...>::is_component_enabled(C flag)
{
return (flag & (C{1} << I)) != C{};
}
template<typename C, typename... Ts>
template<std::size_t... Is>
void component_array_iterator<C, Ts...>::calc_offsets(std::index_sequence<Is...>, difference_type n)
{
const auto negative = n < difference_type{ 0 };
const auto positive = n > difference_type{ 0 };
const auto step = difference_type{ positive } - difference_type{ negative };
n = negative ? -n : n;
// TODO template optimize for single steps
while (n--)
{
const auto& [ flags, count ] = m_flag_counts[m_index + n];
([&] {
if (is_component_enabled<Is>(flags)) {
std::get<Is>(m_offsets) += step * count;
}
}(), ...);
m_index += step;
}
}
template<typename C, typename... Ts>
template<std::size_t... Is>
typename component_array_iterator<C, Ts...>::reference
component_array_iterator<C, Ts...>::dereference(std::index_sequence<Is...>) const
{
return std::make_tuple(get_span<Is>()...);
}
template<typename C, typename... Ts>
template<std::size_t N>
std::tuple_element_t<N, typename component_array_iterator<C, Ts...>::value_type>
component_array_iterator<C, Ts...>::get_span() const
{
const auto& [ flags, count ] = m_flag_counts[m_index];
if (is_component_enabled<N>(flags))
{
return { &std::get<N>(m_components)[m_offsets[N]], count };
}
return nullptr;
}
template<typename C, typename... Ts>
std::tuple<std::add_pointer_t<Ts>...> generic_dynamic_component_array_store<C, Ts...>::data_ptrs()
{
return [&]<auto... Is>(std::index_sequence<Is>)
{
return std::make_tuple(std::get<Is>(m_component_arrays).data()...);
}
(std::index_sequence_for<Ts...>{});
}
template<typename C, typename... Ts>
std::tuple<std::add_pointer_t<std::add_const_t<Ts>>...> generic_dynamic_component_array_store<C, Ts...>::data_ptrs() const
{
return [&]<auto... Is>(std::index_sequence<Is>)
{
return std::make_tuple(std::get<Is>(m_component_arrays).data()...);
}
(std::index_sequence_for<Ts...>{});
}
template<typename C, typename... Ts>
std::array<std::size_t, sizeof...(Ts)> generic_dynamic_component_array_store<C, Ts...>::data_counts() const
{
return [&]<auto... Is>(std::index_sequence<Is>)
{
return std::array{ std::get<Is>(m_component_arrays).size()... };
}
(std::index_sequence_for<Ts...>{});
}
template<typename C, typename... Ts>
typename generic_dynamic_component_array_store<C, Ts...>::id_type generic_dynamic_component_array_store<C, Ts...>::add(
const std::tuple<std::vector<Ts>...>& component_arrays
) {
auto component_flags = C{};
auto count = count_type{};
[&]<auto... Is>(std::integer_sequence<Is...>)
{
const auto& array = std::get<Is>(component_arrays);
if (not array.empty())
{
const auto array_count = static_cast<count_type>(array.size());
count = count ? std::min(array_count, count) : array_count;
component_flags |= C{ 1 } << Is;
}
}
(std::index_sequence_for<Ts...>{});
[&]<auto... Is>(std::integer_sequence<Is...>)
{
const auto& src_array = std::get<Is>(component_arrays);
auto& dst_array = std::get<Is>(m_component_arrays);
if (not src_array.empty())
{
dst_array.insert(dst_array.end(), src_array.begin(), src_array.begin() + count);
}
}
(std::index_sequence_for<Ts...>{});
m_component_flag_counts.emplace_back(component_flags, count);
const auto id = id_type{ m_next_data_id.index++ };
m_ids.push_back(id);
return id;
}
template<typename C, typename... Ts>
std::pair<typename generic_dynamic_component_array_store<C, Ts...>::iterator_type, bool> generic_dynamic_component_array_store<C, Ts...>::find(id_type id)
{
const auto id_it = std::ranges::upper_bound(m_ids, id);
const auto match = (
id_it != m_ids.begin() and
*std::prev(id_it) == id
);
const auto index = id_it - m_ids.begin() - match;
auto it = begin();
it += index;
return { it, match };
}
template<typename C, typename... Ts>
std::pair<typename generic_dynamic_component_array_store<C, Ts...>::const_iterator, bool> generic_dynamic_component_array_store<C, Ts...>::find(id_type id) const
{
const auto id_it = std::ranges::upper_bound(m_ids, id);
const auto match = (
id_it != m_ids.begin() and
*std::prev(id_it) == id
);
const auto index = id_it - m_ids.begin() - match;
auto it = begin();
it += index;
return { it, match };
}
template<typename C, typename... Ts>
void generic_dynamic_component_array_store<C, Ts...>::remove(const iterator_type& it)
{
[&]<auto... Is>(std::index_sequence<Is>)
{
([&]{
auto& component_vector = std::get<Is>(m_component_arrays);
const auto begin = component_vector.begin() + it.m_offsets[Is];
const auto end = begin + it.m_flag_counts[it.m_index];
component_vector.erase(begin, end);
}(), ...);
} (std::index_sequence_for<Ts...>{});
m_component_flag_counts.erase(m_component_flag_counts.begin() + it.m_index);
m_ids.erase(m_ids.begin() + it.m_index);
}
template<typename C, typename... Ts>
void generic_dynamic_component_array_store<C, Ts...>::clear()
{
[&]<auto... Is>(std::index_sequence<Is>)
{
std::get<Is>(m_component_arrays).clear();
} (std::index_sequence_for<Ts...>{});
m_component_flag_counts.clear();
m_ids.clear();
}
template<typename C, typename... Ts>
typename generic_dynamic_component_array_store<C, Ts...>::iterator_type generic_dynamic_component_array_store<C, Ts...>::begin()
{
return iterator_type{
data_ptrs(),
m_component_flag_counts.data(),
0,
{}
};
}
template<typename C, typename... Ts>
typename generic_dynamic_component_array_store<C, Ts...>::iterator_type generic_dynamic_component_array_store<C, Ts...>::end()
{
return iterator_type{
data_ptrs(),
m_component_flag_counts.data(),
m_component_flag_counts.size(),
data_counts()
};
}
template<typename C, typename... Ts>
typename generic_dynamic_component_array_store<C, Ts...>::const_iterator generic_dynamic_component_array_store<C, Ts...>::begin() const
{
return iterator_type{
data_ptrs(),
m_component_flag_counts.data(),
0,
{}
};
}
template<typename C, typename... Ts>
typename generic_dynamic_component_array_store<C, Ts...>::const_iterator generic_dynamic_component_array_store<C, Ts...>::end() const
{
return iterator_type{
data_ptrs(),
m_component_flag_counts.data(),
m_component_flag_counts.size(),
data_counts()
};
}
template<typename C, typename... Ts>
typename generic_dynamic_component_array_store<C, Ts...>::const_iterator generic_dynamic_component_array_store<C, Ts...>::cbegin() const
{
return const_cast<const generic_dynamic_component_array_store*>(this)->begin();
}
template<typename C, typename... Ts>
typename generic_dynamic_component_array_store<C, Ts...>::const_iterator generic_dynamic_component_array_store<C, Ts...>::cend() const
{
return const_cast<const generic_dynamic_component_array_store*>(this)->end();
}
template<typename C, typename... Ts>
typename generic_dynamic_component_array_store<C, Ts...>::view_type generic_dynamic_component_array_store<C, Ts...>::view()
{
return { begin(), end() };
}
template<typename C, typename... Ts>
typename generic_dynamic_component_array_store<C, Ts...>::const_view_type generic_dynamic_component_array_store<C, Ts...>::view() const
{
return { begin(), end() };
}

View File

@@ -0,0 +1,373 @@
#ifndef INCLUDE_GENERIC_DYNAMIC_COMPONENT_STORE_IMPLEMENTATION
# error Never include this file directly include 'basic_dynamic_component_store.hpp'
#endif
#pragma once
#include <vector>
#include <span>
#include "util/uix.hpp"
#include "util/id_type.hpp"
#include <tuple>
#include <cstddef>
#include <type_traits>
#include <algorithm>
template<typename C, typename... Ts>
component_iterator<C, Ts...>::component_iterator(
const value_type components,
const C* flags,
std::size_t index,
const offsets_type& offsets
) : m_components{ components }, m_flags{ flags }, m_index{ index }, m_offsets{ offsets } {}
template<typename C, typename... Ts>
typename component_iterator<C, Ts...>::reference component_iterator<C, Ts...>::operator*() const {
return dereference(std::index_sequence_for<Ts...>{});
}
template<typename C, typename... Ts>
component_iterator<C, Ts...>& component_iterator<C, Ts...>::operator++() {
adjust_offsets(std::index_sequence_for<Ts...>{}, 1);
++m_index;
return *this;
}
template<typename C, typename... Ts>
component_iterator<C, Ts...> component_iterator<C, Ts...>::operator++(int) {
component_iterator tmp = *this;
++(*this);
return tmp;
}
template<typename C, typename... Ts>
component_iterator<C, Ts...>& component_iterator<C, Ts...>::operator--() {
adjust_offsets(std::index_sequence_for<Ts...>{}, -1);
--m_index;
return *this;
}
template<typename C, typename... Ts>
component_iterator<C, Ts...> component_iterator<C, Ts...>::operator--(int) {
auto tmp = *this;
--(*this);
return tmp;
}
template<typename C, typename... Ts>
component_iterator<C, Ts...>& component_iterator<C, Ts...>::operator+=(const difference_type n)
{
adjust_offsets(std::index_sequence_for<Ts...>{}, n);
m_index += n;
return *this;
}
template<typename C, typename... Ts>
component_iterator<C, Ts...>& component_iterator<C, Ts...>::operator-=(const difference_type n)
{
return (*this) += -n;
}
template<typename C, typename... Ts>
component_iterator<C, Ts...> component_iterator<C, Ts...>::operator+(const difference_type n) const
{
auto tmp = *this;
return tmp += n;
}
template<typename C, typename... Ts>
component_iterator<C, Ts...> component_iterator<C, Ts...>::operator-(const difference_type n) const
{
auto tmp = *this;
return tmp -= n;
}
template<typename C, typename... Ts>
typename component_iterator<C, Ts...>::difference_type
component_iterator<C, Ts...>::operator-(const component_iterator& other) const
{
return static_cast<difference_type>(m_index) - static_cast<difference_type>(other.m_index);
}
template<typename C, typename... Ts>
typename component_iterator<C, Ts...>::reference component_iterator<C, Ts...>::operator[](
const difference_type n
) const {
return *((*this) + n);
}
template<typename C, typename... Ts>
bool component_iterator<C, Ts...>::operator==(const component_iterator& other) const
{
return m_components == other.m_components and m_index == other.m_index;
}
template<typename C, typename... Ts>
bool component_iterator<C, Ts...>::operator!=(const component_iterator& other) const
{
return not (*this == other);
}
template<typename C, typename... Ts>
bool component_iterator<C, Ts...>::operator<(const component_iterator& other) const
{
return m_index < other.m_index;
}
template<typename C, typename... Ts>
bool component_iterator<C, Ts...>::operator<=(const component_iterator& other) const
{
return m_index <= other.m_index;
}
template<typename C, typename... Ts>
bool component_iterator<C, Ts...>::operator>(const component_iterator& other) const
{
return m_index > other.m_index;
}
template<typename C, typename... Ts>
bool component_iterator<C, Ts...>::operator>=(const component_iterator& other) const
{
return m_index >= other.m_index;
}
template<typename C, typename... Ts>
template<std::size_t I>
bool component_iterator<C, Ts...>::is_component_enabled(C flag)
{
return (flag & (C{1} << I)) != C{};
}
template<typename C, typename... Ts>
template<std::size_t... Is>
void component_iterator<C, Ts...>::calc_offsets(std::index_sequence<Is...>, difference_type n)
{
const auto negative = n < difference_type{ 0 };
const auto positive = n > difference_type{ 0 };
const auto step = difference_type{ positive } - difference_type{ negative };
n = negative ? -n : n;
// TODO template optimize for single steps
while (n--)
{
const C& flag = m_flags[m_index + n];
([&] {
if (is_component_enabled<Is>(flag)) {
std::get<Is>(m_offsets) += step;
}
}(), ...);
m_index += step;
}
}
template<typename C, typename... Ts>
template<std::size_t... Is>
typename component_iterator<C, Ts...>::reference
component_iterator<C, Ts...>::dereference(std::index_sequence<Is...>) const
{
return std::make_tuple(get_pointer<Is>()...);
}
template<typename C, typename... Ts>
template<std::size_t N>
std::tuple_element_t<N, typename component_iterator<C, Ts...>::value_type>
component_iterator<C, Ts...>::get_pointer() const
{
if (is_component_enabled<N>(m_flags[m_index]))
{
return &std::get<N>(m_components)[m_offsets[N]];
}
return nullptr;
}
template<typename C, typename... Ts>
std::tuple<std::add_pointer_t<Ts>...> component_iterator<C, Ts...>::data_ptrs()
{
return [&]<auto... Is>(std::index_sequence<Is>)
{
return std::make_tuple(std::get<Is>(m_components).data()...);
}
(std::index_sequence_for<Ts...>{});
}
template<typename C, typename... Ts>
std::tuple<std::add_pointer_t<std::add_const_t<Ts>>...> component_iterator<C, Ts...>::data_ptrs() const
{
return [&]<auto... Is>(std::index_sequence<Is>)
{
return std::make_tuple(std::get<Is>(m_components).data()...);
}
(std::index_sequence_for<Ts...>{});
}
template<typename C, typename... Ts>
std::array<std::size_t, sizeof...(Ts)> component_iterator<C, Ts...>::data_counts() const
{
return [&]<auto... Is>(std::index_sequence<Is>)
{
return std::array{ std::get<Is>(m_components).size()... };
}
(std::index_sequence_for<Ts...>{});
}
template<typename C, typename... Ts>
typename generic_dynamic_component_store<C, Ts...>::id_type generic_dynamic_component_store<C, Ts...>::add(
const std::tuple<std::optional<Ts>...>& data
) {
auto component_flags = C{};
[&]<auto... Is>(std::integer_sequence<Is...>)
{
if (const auto& component_opt = std::get<Is>(data))
{
std::get<Is>(m_components).push_back(*component_opt);
component_flags |= C{ 1 } << Is;
}
}
(std::index_sequence_for<Ts...>{});
m_component_flags.push_back(component_flags);
const auto id = id_type{ m_next_data_id.index++ };
m_ids.push_back(id);
return id;
}
template<typename C, typename... Ts>
std::pair<typename generic_dynamic_component_store<C, Ts...>::iterator_type, bool> generic_dynamic_component_store<C, Ts...>::find(id_type id)
{
const auto id_it = std::ranges::upper_bound(m_ids, id);
const auto match = (
id_it != m_ids.begin() and
*std::prev(id_it) == id
);
const auto index = id_it - m_ids.begin() - match;
auto it = begin();
it += index;
return { it, match };
}
template<typename C, typename... Ts>
std::pair<typename generic_dynamic_component_store<C, Ts...>::const_iterator, bool> generic_dynamic_component_store<C, Ts...>::find(id_type id) const
{
const auto id_it = std::ranges::upper_bound(m_ids, id);
const auto match = (
id_it != m_ids.begin() and
*std::prev(id_it) == id
);
const auto index = id_it - m_ids.begin() - match;
auto it = begin();
it += index;
return { it, match };
}
template<typename C, typename... Ts>
void generic_dynamic_component_store<C, Ts...>::remove(const iterator_type& it)
{
return [&]<auto... Is>(std::index_sequence<Is>)
{
auto& component_vector = std::get<Is>(m_components);
(component_vector.erase(component_vector.begin() + it.m_offsets[Is]), ...);
} (std::index_sequence_for<Ts...>{});
m_component_flags.erase(m_component_flags.begin() + it.m_index);
m_ids.erase(m_ids.begin() + it.m_index);
}
template<typename C, typename... Ts>
void generic_dynamic_component_store<C, Ts...>::clear()
{
return [&]<auto... Is>(std::index_sequence<Is>)
{
std::get<Is>(m_component_counts).clear();
} (std::index_sequence_for<Ts...>{});
m_component_flags.clear();
m_ids.clear();
}
template<typename C, typename... Ts>
generic_dynamic_component_store<C, Ts...>::iterator_type generic_dynamic_component_store<C, Ts...>::begin()
{
return iterator_type{
data_ptrs(),
m_component_flags.data(),
0,
{}
};
}
template<typename C, typename... Ts>
generic_dynamic_component_store<C, Ts...>::iterator_type generic_dynamic_component_store<C, Ts...>::end()
{
return iterator_type{
data_ptrs(),
m_component_flags.data(),
m_component_flags.size(),
data_counts()
};
}
template<typename C, typename... Ts>
generic_dynamic_component_store<C, Ts...>::const_iterator generic_dynamic_component_store<C, Ts...>::begin() const
{
return iterator_type{
data_ptrs(),
m_component_flags.data(),
0,
{}
};
}
template<typename C, typename... Ts>
generic_dynamic_component_store<C, Ts...>::const_iterator generic_dynamic_component_store<C, Ts...>::end() const
{
return iterator_type{
data_ptrs(),
m_component_flags.data(),
m_component_flags.size(),
data_counts()
};
}
template<typename C, typename... Ts>
generic_dynamic_component_store<C, Ts...>::const_iterator generic_dynamic_component_store<C, Ts...>::cbegin() const
{
return const_cast<const generic_dynamic_component_store*>(this)->begin();
}
template<typename C, typename... Ts>
generic_dynamic_component_store<C, Ts...>::const_iterator generic_dynamic_component_store<C, Ts...>::cend() const
{
return const_cast<const generic_dynamic_component_store*>(this)->end();
}
template<typename C, typename... Ts>
generic_dynamic_component_store<C, Ts...>::view_type generic_dynamic_component_store<C, Ts...>::view()
{
return { begin(), end() };
}
template<typename C, typename... Ts>
generic_dynamic_component_store<C, Ts...>::const_view_type generic_dynamic_component_store<C, Ts...>::view() const
{
return { begin(), end() };
}

View File

@@ -0,0 +1,418 @@
#ifndef INCLUDE_GENERIC_DYNAMIC_INDEXED_COMPONENT_ARRAY_STORE_IMPLEMENTATION
# error Never include this file directly include 'generic_dynamic_indexed_component_array_store.hpp'
#endif
#pragma once
#include <vector>
#include <span>
#include <tuple>
#include <cstddef>
#include <type_traits>
#include <algorithm>
template<typename C, typename I, typename... Ts>
indexed_component_array_iterator<C, I, Ts...>::indexed_component_array_iterator(
const index_array_pointer_type indices,
const component_array_pointer_type& components,
const flag_count_pointer_type flag_counts,
std::size_t index,
const offsets_type& offsets
) :
m_indices{ indices },
m_components{ components },
m_flag_counts{ flag_counts },
m_index{ index },
m_offsets{ offsets } {}
template<typename C, typename I, typename... Ts>
typename indexed_component_array_iterator<C, I, Ts...>::reference indexed_component_array_iterator<C, I, Ts...>::operator*() const {
return dereference(std::index_sequence_for<Ts...>{});
}
template<typename C, typename I, typename... Ts>
indexed_component_array_iterator<C, I, Ts...>& indexed_component_array_iterator<C, I, Ts...>::operator++() {
adjust_offsets(std::index_sequence_for<Ts...>{}, 1);
++m_index;
return *this;
}
template<typename C, typename I, typename... Ts>
indexed_component_array_iterator<C, I, Ts...> indexed_component_array_iterator<C, I, Ts...>::operator++(int) {
indexed_component_array_iterator tmp = *this;
++(*this);
return tmp;
}
template<typename C, typename I, typename... Ts>
indexed_component_array_iterator<C, I, Ts...>& indexed_component_array_iterator<C, I, Ts...>::operator--() {
adjust_offsets(std::index_sequence_for<Ts...>{}, -1);
--m_index;
return *this;
}
template<typename C, typename I, typename... Ts>
indexed_component_array_iterator<C, I, Ts...> indexed_component_array_iterator<C, I, Ts...>::operator--(int) {
auto tmp = *this;
--(*this);
return tmp;
}
template<typename C, typename I, typename... Ts>
indexed_component_array_iterator<C, I, Ts...>& indexed_component_array_iterator<C, I, Ts...>::operator+=(const difference_type n)
{
adjust_offsets(std::index_sequence_for<Ts...>{}, n);
m_index += n;
return *this;
}
template<typename C, typename I, typename... Ts>
indexed_component_array_iterator<C, I, Ts...>& indexed_component_array_iterator<C, I, Ts...>::operator-=(const difference_type n)
{
return (*this) += -n;
}
template<typename C, typename I, typename... Ts>
indexed_component_array_iterator<C, I, Ts...> indexed_component_array_iterator<C, I, Ts...>::operator+(const difference_type n) const
{
auto tmp = *this;
return tmp += n;
}
template<typename C, typename I, typename... Ts>
indexed_component_array_iterator<C, I, Ts...> indexed_component_array_iterator<C, I, Ts...>::operator-(const difference_type n) const
{
auto tmp = *this;
return tmp -= n;
}
template<typename C, typename I, typename... Ts>
typename indexed_component_array_iterator<C, I, Ts...>::difference_type
indexed_component_array_iterator<C, I, Ts...>::operator-(const indexed_component_array_iterator& other) const
{
return static_cast<difference_type>(m_index) - static_cast<difference_type>(other.m_index);
}
template<typename C, typename I, typename... Ts>
typename indexed_component_array_iterator<C, I, Ts...>::reference indexed_component_array_iterator<C, I, Ts...>::operator[](
const difference_type n
) const {
return *((*this) + n);
}
template<typename C, typename I, typename... Ts>
bool indexed_component_array_iterator<C, I, Ts...>::operator==(const indexed_component_array_iterator& other) const
{
return m_components == other.m_components and m_index == other.m_index;
}
template<typename C, typename I, typename... Ts>
bool indexed_component_array_iterator<C, I, Ts...>::operator!=(const indexed_component_array_iterator& other) const
{
return not (*this == other);
}
template<typename C, typename I, typename... Ts>
bool indexed_component_array_iterator<C, I, Ts...>::operator<(const indexed_component_array_iterator& other) const
{
return m_index < other.m_index;
}
template<typename C, typename I, typename... Ts>
bool indexed_component_array_iterator<C, I, Ts...>::operator<=(const indexed_component_array_iterator& other) const
{
return m_index <= other.m_index;
}
template<typename C, typename I, typename... Ts>
bool indexed_component_array_iterator<C, I, Ts...>::operator>(const indexed_component_array_iterator& other) const
{
return m_index > other.m_index;
}
template<typename C, typename I, typename... Ts>
bool indexed_component_array_iterator<C, I, Ts...>::operator>=(const indexed_component_array_iterator& other) const
{
return m_index >= other.m_index;
}
template<typename C, typename I, typename... Ts>
template<std::size_t N>
bool indexed_component_array_iterator<C, I, Ts...>::is_component_enabled(C flag)
{
return (flag & (C{1} << N)) != C{};
}
template<typename C, typename I, typename... Ts>
template<std::size_t... Is>
void indexed_component_array_iterator<C, I, Ts...>::calc_offsets(
std::index_sequence<Is...>,
difference_type n
) {
const auto negative = n < difference_type{ 0 };
const auto positive = n > difference_type{ 0 };
const auto step = difference_type{ positive } - difference_type{ negative };
n = negative ? -n : n;
// TODO template optimize for single steps
while (n--)
{
const auto& [ flags, index_count, component_count ] = m_flag_counts[m_index + n];
std::get<0>(m_offsets) += step * index_count;
([&] {
if (is_component_enabled<Is>(flags)) {
std::get<1 + Is>(m_offsets) += step * component_count;
}
}(), ...);
m_index += step;
}
}
template<typename C, typename I, typename... Ts>
template<std::size_t... Is>
typename indexed_component_array_iterator<C, I, Ts...>::reference
indexed_component_array_iterator<C, I, Ts...>::dereference(std::index_sequence<Is...>) const
{
const auto& [ flags, index_count, component_count ] = m_flag_counts[m_index];
return std::make_tuple(
std::span(
m_indices[m_offsets[0]],
index_count
),
std::span(
(
is_component_enabled<Is>(flags)
? &std::get<Is>(m_components)[m_offsets[1 + Is]]
: nullptr
),
component_count
)...
);
}
template<typename C, typename I, typename... Ts>
std::tuple<std::add_pointer_t<Ts>...> generic_dynamic_indexed_component_array_store<C, I, Ts...>::component_array_ptrs()
{
return [&]<auto... Is>(std::index_sequence<Is>)
{
return std::make_tuple(std::get<Is>(m_component_arrays).data()...);
}
(std::index_sequence_for<Ts...>{});
}
template<typename C, typename I, typename... Ts>
std::tuple<std::add_pointer_t<std::add_const_t<Ts>>...> generic_dynamic_indexed_component_array_store<C, I, Ts...>::component_array_ptrs() const
{
return [&]<auto... Is>(std::index_sequence<Is>)
{
return std::make_tuple(std::get<Is>(m_component_arrays).data()...);
}
(std::index_sequence_for<Ts...>{});
}
template<typename C, typename I, typename... Ts>
std::array<std::size_t, 1 + sizeof...(Ts)> generic_dynamic_indexed_component_array_store<C, I, Ts...>::array_counts() const
{
return [&]<auto... Is>(std::index_sequence<Is>)
{
return std::array{
m_indices.size(),
std::get<Is>(m_component_arrays).size()...
};
}
(std::index_sequence_for<Ts...>{});
}
template<typename C, typename I, typename... Ts>
typename generic_dynamic_indexed_component_array_store<C, I,Ts...>::id_type generic_dynamic_indexed_component_array_store<C, I,Ts...>::add(
std::span<const I> indices,
const std::tuple<std::vector<Ts>...>& component_arrays
) {
auto component_flags = C{};
auto min_component_count = count_type{};
[&]<auto... Is>(std::integer_sequence<Is...>)
{
const auto& component_array = std::get<Is>(component_arrays);
if (not component_array.empty())
{
const auto component_count = static_cast<count_type>(component_array.size());
if (min_component_count != 0 and component_count < min_component_count)
{
min_component_count = component_count;
}
component_flags |= C{ 1 } << Is;
}
}
(std::index_sequence_for<Ts...>{});
m_indices.insert(m_indices.end(), indices.begin(), indices.end());
[&]<auto... Is>(std::integer_sequence<Is...>)
{
const auto& src_array = std::get<Is>(component_arrays);
auto& dst_array = std::get<Is>(m_component_arrays);
if (not src_array.empty())
{
dst_array.insert(dst_array.end(), src_array.begin(), src_array.begin() + min_component_count);
}
}
(std::index_sequence_for<Ts...>{});
m_component_flag_counts.emplace_back(component_flags, indices.size(), min_component_count);
const auto id = id_type{ m_next_data_id.index++ };
m_ids.push_back(id);
return id;
}
template<typename C, typename I, typename... Ts>
std::pair<typename generic_dynamic_indexed_component_array_store<C, I,Ts...>::iterator_type, bool> generic_dynamic_indexed_component_array_store<C, I,Ts...>::find(id_type id)
{
const auto id_it = std::ranges::upper_bound(m_ids, id);
const auto match = (
id_it != m_ids.begin() and
*std::prev(id_it) == id
);
const auto index = id_it - m_ids.begin() - match;
auto it = begin();
it += index;
return { it, match };
}
template<typename C, typename I, typename... Ts>
std::pair<typename generic_dynamic_indexed_component_array_store<C, I,Ts...>::const_iterator, bool> generic_dynamic_indexed_component_array_store<C, I,Ts...>::find(id_type id) const
{
const auto id_it = std::ranges::upper_bound(m_ids, id);
const auto match = (
id_it != m_ids.begin() and
*std::prev(id_it) == id
);
const auto index = id_it - m_ids.begin() - match;
auto it = begin();
it += index;
return { it, match };
}
template<typename C, typename I, typename... Ts>
void generic_dynamic_indexed_component_array_store<C, I,Ts...>::remove(const iterator_type& it)
{
m_indices.erase(m_indices.begin() + it.m_offsets[0]);
[&]<auto... Is>(std::index_sequence<Is>)
{
([&]{
auto& component_vector = std::get<Is>(m_component_arrays);
const auto begin = component_vector.begin() + it.m_offsets[1 + Is];
const auto end = begin + it.m_flag_counts[it.m_index];
component_vector.erase(begin, end);
}(), ...);
} (std::index_sequence_for<Ts...>{});
m_component_flag_counts.erase(m_component_flag_counts.begin() + it.m_index);
m_ids.erase(m_ids.begin() + it.m_index);
}
template<typename C, typename I, typename... Ts>
void generic_dynamic_indexed_component_array_store<C, I,Ts...>::clear()
{
m_indices.clear();
[&]<auto... Is>(std::index_sequence<Is>)
{
std::get<Is>(m_component_arrays).clear();
} (std::index_sequence_for<Ts...>{});
m_component_flag_counts.clear();
m_ids.clear();
}
template<typename C, typename I, typename... Ts>
typename generic_dynamic_indexed_component_array_store<C, I,Ts...>::iterator_type generic_dynamic_indexed_component_array_store<C, I,Ts...>::begin()
{
return iterator_type{
m_indices.data(),
component_array_ptrs(),
m_component_flag_counts.data(),
0,
{}
};
}
template<typename C, typename I, typename... Ts>
typename generic_dynamic_indexed_component_array_store<C, I,Ts...>::iterator_type generic_dynamic_indexed_component_array_store<C, I,Ts...>::end()
{
return iterator_type{
m_indices.data(),
component_array_ptrs(),
m_component_flag_counts.data(),
m_component_flag_counts.size(),
array_counts()
};
}
template<typename C, typename I, typename... Ts>
typename generic_dynamic_indexed_component_array_store<C, I,Ts...>::const_iterator generic_dynamic_indexed_component_array_store<C, I,Ts...>::begin() const
{
return iterator_type{
m_indices.data(),
component_array_ptrs(),
m_component_flag_counts.data(),
0,
{}
};
}
template<typename C, typename I, typename... Ts>
typename generic_dynamic_indexed_component_array_store<C, I,Ts...>::const_iterator generic_dynamic_indexed_component_array_store<C, I,Ts...>::end() const
{
return iterator_type{
m_indices.data(),
component_array_ptrs(),
m_component_flag_counts.data(),
m_component_flag_counts.size(),
array_counts()
};
}
template<typename C, typename I, typename... Ts>
typename generic_dynamic_indexed_component_array_store<C, I,Ts...>::const_iterator generic_dynamic_indexed_component_array_store<C, I,Ts...>::cbegin() const
{
return const_cast<const generic_dynamic_indexed_component_array_store*>(this)->begin();
}
template<typename C, typename I, typename... Ts>
typename generic_dynamic_indexed_component_array_store<C, I,Ts...>::const_iterator generic_dynamic_indexed_component_array_store<C, I,Ts...>::cend() const
{
return const_cast<const generic_dynamic_indexed_component_array_store*>(this)->end();
}
template<typename C, typename I, typename... Ts>
typename generic_dynamic_indexed_component_array_store<C, I,Ts...>::view_type generic_dynamic_indexed_component_array_store<C, I,Ts...>::view()
{
return { begin(), end() };
}
template<typename C, typename I, typename... Ts>
typename generic_dynamic_indexed_component_array_store<C, I,Ts...>::const_view_type generic_dynamic_indexed_component_array_store<C, I,Ts...>::view() const
{
return { begin(), end() };
}

View File

@@ -0,0 +1,61 @@
#ifndef INCLUDE_GENERIC_DYNAMIC_STORE_IMPLEMENTATION
# error Never include this file directly include 'basic_dynamic_store.hpp'
#endif
#include <algorithm>
template<typename T>
typename generic_dynamic_store<T>::id_type generic_dynamic_store<T>::add(const T& data)
{
auto id = id_type{ m_next_data_id.index++ };
m_data.emplace_back(data);
m_ids.emplace_back(id);
return id;
}
template<typename T>
std::pair<typename generic_dynamic_store<T>::iterator_type, bool> generic_dynamic_store<T>::find(id_type id)
{
const auto it = std::ranges::upper_bound(m_ids, id);
const auto found = it != m_ids.begin() and *std::prev(it) == id;
const auto index = it - m_ids.begin() - found;
return { m_data.begin() + index, found };
}
template<typename T>
std::pair<typename generic_dynamic_store<T>::const_iterator, bool> generic_dynamic_store<T>::find(id_type id) const
{
const auto it = std::ranges::upper_bound(m_ids, id);
const auto found = it != m_ids.begin() and *std::prev(it) == id;
const auto index = it - m_ids.begin() - found;
return { m_data.begin() + index, found };
}
template<typename T>
void generic_dynamic_store<T>::remove(iterator_type it)
{
const auto index = it - m_data.begin();
m_data.erase(it);
m_ids.erase(m_ids.begin() + index);
}
template<typename T>
void generic_dynamic_store<T>::clear()
{
m_data.clear();
m_ids.clear();
}
template<typename T>
std::span<T> generic_dynamic_store<T>::data()
{
return m_data;
}
template<typename T>
std::span<const T> generic_dynamic_store<T>::data() const
{
return m_data;
}

View File

@@ -0,0 +1,88 @@
#ifndef INCLUDE_DYNAMIC_MATERIAL_DATA_IMPLEMENTATION
# error Never include this file directly include 'dynamic_material_buffer.hpp'
#endif
inline std::optional<components::material::surface_properties>& dynamic_material_buffer::surface_properties()
{
return std::get<components::material::indices::surface_properties>(data);
}
inline std::optional<components::material::transparency>& dynamic_material_buffer::transparency()
{
return std::get<components::material::indices::transparency>(data);
}
inline std::optional<dynamic_texture_store::id_type>& dynamic_material_buffer::ambient_color_texture_id()
{
return std::get<components::material::indices::ambient_color_texture>(data);
}
inline std::optional<dynamic_texture_store::id_type>& dynamic_material_buffer::diffuse_color_texture_id()
{
return std::get<components::material::indices::diffuse_color_texture>(data);
}
inline std::optional<dynamic_texture_store::id_type>& dynamic_material_buffer::specular_color_texture_id()
{
return std::get<components::material::indices::specular_color_texture>(data);
}
inline std::optional<dynamic_texture_store::id_type>& dynamic_material_buffer::shininess_texture_id()
{
return std::get<components::material::indices::shininess_texture>(data);
}
inline std::optional<dynamic_texture_store::id_type>& dynamic_material_buffer::alpha_texture_id()
{
return std::get<components::material::indices::alpha_texture>(data);
}
inline std::optional<dynamic_texture_store::id_type>& dynamic_material_buffer::bump_texture_id()
{
return std::get<components::material::indices::bump_texture(data);
}
inline const std::optional<components::material::surface_properties>& dynamic_material_buffer::surface_properties() const
{
return std::get<components::material::indices::surface_properties>(data);
}
inline const std::optional<components::material::transparency>& dynamic_material_buffer::transparency() const
{
return std::get<components::material::indices::transparency>(data);
}
inline const std::optional<dynamic_texture_store::id_type>& dynamic_material_buffer::ambient_color_texture_id() const
{
return std::get<components::material::indices::ambient_color_texture>(data);
}
inline const std::optional<dynamic_texture_store::id_type>& dynamic_material_buffer::diffuse_color_texture_id() const
{
return std::get<components::material::indices::diffuse_color_texture>(data);
}
inline const std::optional<dynamic_texture_store::id_type>& dynamic_material_buffer::specular_color_texture_id() const
{
return std::get<components::material::indices::specular_color_texture>(data);
}
inline const std::optional<dynamic_texture_store::id_type>& dynamic_material_buffer::shininess_texture_id() const
{
return std::get<components::material::indices::shininess_texture>(data);
}
inline const std::optional<dynamic_texture_store::id_type>& dynamic_material_buffer::alpha_texture_id() const
{
return std::get<components::material::indices::alpha_texture>(data);
}
inline const std::optional<dynamic_texture_store::id_type>& dynamic_material_buffer::bump_texture_id() const
{
return std::get<components::material::indices::bump_texture>(data);
}
inline components::material::surface_properties& dynamic_material_buffer::initialized_surface_properties()
{
auto& surface_properties_opt = surface_properties();
if (not surface_properties_opt)
{
surface_properties_opt = components::material::surface_properties{};
}
return *surface_properties_opt;
}

View File

@@ -0,0 +1,73 @@
#ifndef INCLUDE_DYNAMIC_MESH_DATA_IMPLEMENTATION
# error Never include this file directly include 'dynamic_mesh_buffer.hpp'
#endif
inline std::vector<components::mesh_vertex::position>& dynamic_mesh_buffer::positions()
{
return std::get<components::mesh_vertex::indices::position>(vertices);
}
inline std::vector<components::mesh_vertex::normal>& dynamic_mesh_buffer::normals()
{
return std::get<components::mesh_vertex::indices::normal>(vertices);
}
inline std::vector<components::mesh_vertex::tex_coord>& dynamic_mesh_buffer::tex_coords()
{
return std::get<components::mesh_vertex::indices::tex_coord>(vertices);
}
inline std::vector<components::mesh_vertex::color>& dynamic_mesh_buffer::colors()
{
return std::get<components::mesh_vertex::indices::color>(vertices);
}
inline std::vector<components::mesh_vertex::reflectance>& dynamic_mesh_buffer::reflectances()
{
return std::get<components::mesh_vertex::indices::reflectance>(vertices);
}
inline std::vector<dynamic_mesh_buffer::triangle_type>& dynamic_mesh_buffer::triangles()
{
return m_triangles;
}
inline auto& dynamic_mesh_buffer::material_id()
{
return m_material_id;
}
inline const std::vector<components::mesh_vertex::position>& dynamic_mesh_buffer::positions() const
{
return std::get<components::mesh_vertex::indices::position>(vertices);
}
inline const std::vector<components::mesh_vertex::normal>& dynamic_mesh_buffer::normals() const
{
return std::get<components::mesh_vertex::indices::normal>(vertices);
}
inline const std::vector<components::mesh_vertex::tex_coord>& dynamic_mesh_buffer::tex_coords() const
{
return std::get<components::mesh_vertex::indices::tex_coord>(vertices);
}
inline const std::vector<components::mesh_vertex::color>& dynamic_mesh_buffer::colors() const
{
return std::get<components::mesh_vertex::indices::color>(vertices);
}
inline const std::vector<components::mesh_vertex::reflectance>& dynamic_mesh_buffer::reflectances() const
{
return std::get<components::mesh_vertex::indices::reflectance>(vertices);
}
inline const std::vector<dynamic_mesh_buffer::triangle_type>& dynamic_mesh_buffer::triangles() const
{
return m_triangles;
}
inline const auto& dynamic_mesh_buffer::material_id() const
{
return m_material_id;
}

View File

@@ -0,0 +1,148 @@
#ifndef INCLUDE_DYNAMIC_MODEL_DATA_IMPLEMENTATION
# error Never include this file directly include 'dynamic_vertex_buffer.hpp'
#endif
#include "util/specialised_lambda.hpp"
#include "opengl/type_utils.hpp"
#include <cstring>
#include <algorithm>
#include <numeric>
template<typename C, typename... Ts>
C& dynamic_vertex_buffer<C, Ts...>::components()
{
return m_components;
}
template<typename C, typename... Ts>
const C& dynamic_vertex_buffer<C, Ts...>::components() const
{
return m_components;
}
template <typename T>
concept numeric_type = std::integral<T> or std::floating_point<T>;
template<typename C, typename... Ts>
void dynamic_vertex_buffer<C, Ts...>::build_vertex_buffer(
std::vector<ztu::u8>& vertex_buffer,
std::size_t& component_count,
std::array<GLenum, sizeof...(Ts)>& component_types,
std::array<GLint, sizeof...(Ts)>& component_lengths,
GLsizei& stride
) const {
const auto for_all_components = [&]<typename T>(auto&& f, const T default_value)
{
return std::apply(
[&](const auto&... component_buffer)
{
std::array<T, sizeof...(component_buffer)> results{};
auto i = std::size_t{};
(
(
results[i] = [&](const auto& buffer, const auto index) -> T
{
if ((m_components & C{ 1 << index }) != C{})
{
return f(buffer, index);
}
return default_value;
}(component_buffer, i),
++i
),
...
);
return results;
},
m_component_buffers
);
};
component_count = 0;
component_types = for_all_components(
ztu::specialised_lambda
{
[&component_count]<numeric_type Component, std::size_t Count>(const std::vector<std::array<Component, Count>>&, std::size_t)
{
++component_count;
return zgl::type_utils::to_gl_type<Component>();
},
[&component_count]<numeric_type Component>(const std::vector<Component>&, std::size_t)
{
++component_count;
return zgl::type_utils::to_gl_type<Component>();
}
},
GLenum{ GL_INVALID_VALUE }
);
const auto element_counts = for_all_components(
[]<class Component>(const std::vector<Component>& buffer, std::size_t)
{
return buffer.size();
},
std::numeric_limits<std::size_t>::max()
);
const auto minimum_element_count = std::ranges::min(element_counts);
component_lengths = for_all_components(
ztu::specialised_lambda
{
[]<class Component>(const std::vector<Component>&, std::size_t)
{
return 1;
},
[]<class Component, std::size_t Count>(const std::vector<std::array<Component, Count>>&, std::size_t)
{
return Count;
}
},
GLsizei{ 0 }
);
auto component_sizes = std::array<GLsizei, sizeof...(Ts)>{};
for (std::size_t i{}; i != component_sizes.size(); ++i)
{
component_sizes[i] = component_lengths[i] * zgl::type_utils::size_of(component_types[i]);
}
const auto total_size = minimum_element_count * std::accumulate(
component_sizes.begin(),
component_sizes.end(),
GLsizei{ 0 }
);
vertex_buffer.resize(total_size);
// Calculate offsets and stride
auto component_offsets = component_sizes;
stride = 0;
for (std::size_t i{}; i != component_offsets.size(); ++i) {
component_offsets[i] = stride;
stride += component_sizes[i];
}
// Copy all the components over one by one
for_all_components(
[&]<class Component>(const std::vector<Component>& buffer, std::size_t index)
{
std::size_t pos = component_offsets[index];
for (std::size_t i{}; i != minimum_element_count; ++i)
{
std::memcpy(
&vertex_buffer[pos],
buffer[i].data(),
component_sizes[index]
);
pos += stride;
}
return 0;
},
0
);
// remove values of unused components
std::ignore = std::ranges::remove(component_lengths, 0);
std::ignore = std::ranges::remove(component_types, GL_INVALID_VALUE);
}

View File

@@ -0,0 +1,44 @@
#ifndef INCLUDE_DYNAMIC_TEXTURE_DATA_IMPLEMENTATION
# error Never include this file directly include 'dynamic_point_cloud_buffer.hpp'
#endif
#include "assets/components/point_cloud_vertex_components.hpp"
inline std::vector<components::point_cloud_vertex::position>& dynamic_point_cloud_buffer::positions()
{
return std::get<components::point_cloud_vertex::indices::position>(vertices);
}
inline std::vector<components::point_cloud_vertex::normal>& dynamic_point_cloud_buffer::normals()
{
return std::get<components::point_cloud_vertex::indices::normal>(vertices);
}
inline std::vector<components::point_cloud_vertex::color>& dynamic_point_cloud_buffer::colors()
{
return std::get<components::point_cloud_vertex::indices::color>(vertices);
}
inline std::vector<components::point_cloud_vertex::reflectance>& dynamic_point_cloud_buffer::reflectances()
{
return std::get<components::point_cloud_vertex::indices::reflectance>(vertices);
}
inline const std::vector<components::point_cloud_vertex::position>& dynamic_point_cloud_buffer::positions() const
{
return std::get<components::point_cloud_vertex::indices::position>(vertices);
}
inline const std::vector<components::point_cloud_vertex::normal>& dynamic_point_cloud_buffer::normals() const
{
return std::get<components::point_cloud_vertex::indices::normal>(vertices);
}
inline const std::vector<components::point_cloud_vertex::color>& dynamic_point_cloud_buffer::colors() const
{
return std::get<components::point_cloud_vertex::indices::color>(vertices);
}
inline const std::vector<components::point_cloud_vertex::reflectance>& dynamic_point_cloud_buffer::reflectances() const
{
return std::get<components::point_cloud_vertex::indices::reflectance>(vertices);
}

View File

@@ -0,0 +1,151 @@
#ifndef INCLUDE_DYNAMIC_TEXTURE_DATA_IMPLEMENTATION
# error Never include this file directly include 'dynamic_texture_buffer.hpp'
#endif
inline dynamic_texture_buffer::dynamic_texture_buffer(
std::unique_ptr<value_type>&& data,
const dim_type width,
const dim_type height,
const components::texture::flags components
) :
m_data{ std::move(data) },
m_width{ width },
m_height{ height },
m_components{ components }
{};
inline dynamic_texture_buffer::dynamic_texture_buffer(const dynamic_texture_buffer& other) :
m_data{ new value_type[other.component_count()] },
m_width{ other.m_width },
m_height{ other.m_height },
m_components{ other.m_components }
{
std::copy_n(other.m_data.get(), other.m_width * other.m_height, this->m_data.get());
}
inline dynamic_texture_buffer::dynamic_texture_buffer(dynamic_texture_buffer&& other) noexcept :
m_data{ std::move(other.m_data) },
m_width{ other.m_width },
m_height{ other.m_height },
m_components{ other.m_components }
{
other.m_width = 0;
other.m_height = 0;
other.m_components = components::texture::flags::none;
}
inline dynamic_texture_buffer& dynamic_texture_buffer::operator=(const dynamic_texture_buffer& other)
{
if (this != &other) [[likely]]
{
const auto m_size = this->component_count();
const auto o_size = other.component_count();
if (o_size > m_size) {
this->~dynamic_texture_buffer();
this->m_data.reset(new value_type[o_size]);
}
std::copy_n(other.m_data.get(), o_size, this->m_data.get());
this->m_width = other.m_width;
this->m_height = other.m_height;
this->m_components = other.m_components;
}
return *this;
}
inline dynamic_texture_buffer& dynamic_texture_buffer::operator=(dynamic_texture_buffer&& other) noexcept
{
if (this != &other) [[likely]]
{
this->~dynamic_texture_buffer();
this->m_data = std::move(other.m_data);
this->m_width = other.m_width;
this->m_height = other.m_height;
other.m_width = 0;
other.m_height = 0;
other.m_components = components::texture::flags::none;
}
return *this;
}
inline components::texture::flags dynamic_texture_buffer::components() const
{
return m_components;
}
inline dynamic_texture_buffer::dim_type dynamic_texture_buffer::width() const
{
return m_width;
}
inline dynamic_texture_buffer::dim_type dynamic_texture_buffer::height() const
{
return m_height;
}
inline std::pair<dynamic_texture_buffer::dim_type, dynamic_texture_buffer::dim_type> dynamic_texture_buffer::dimensions() const
{
return { m_width, m_height };
}
inline dynamic_texture_buffer::size_type dynamic_texture_buffer::pixel_count() const
{
return static_cast<size_type>(m_width) * static_cast<size_type>(m_height);
}
inline dynamic_texture_buffer::size_type dynamic_texture_buffer::component_count() const
{
return std::popcount(static_cast<std::underlying_type_t<components::texture::flags>>(m_components));
}
inline dynamic_texture_buffer::size_type dynamic_texture_buffer::size() const
{
return pixel_count() * component_count();
}
inline dynamic_texture_buffer::const_pointer dynamic_texture_buffer::data() const
{
return m_data.get();
}
inline dynamic_texture_buffer::pointer dynamic_texture_buffer::data()
{
return m_data.get();
}
inline dynamic_texture_buffer::const_iterator dynamic_texture_buffer::begin() const
{
return data();
}
inline dynamic_texture_buffer::iterator dynamic_texture_buffer::begin()
{
return data();
}
inline dynamic_texture_buffer::const_iterator dynamic_texture_buffer::end() const
{
return begin() + component_count();
}
inline dynamic_texture_buffer::iterator dynamic_texture_buffer::end()
{
return begin() + component_count();
}
inline dynamic_texture_buffer::const_iterator dynamic_texture_buffer::cbegin() const
{
return const_cast<const_iterator>(begin());
}
inline dynamic_texture_buffer::const_iterator dynamic_texture_buffer::cend() const
{
return const_cast<const_iterator>(begin());
}

View File

@@ -0,0 +1,139 @@
#include "assets/prefetch_lookups/pose_prefetch_lookup.hpp"
#include <algorithm>
pose_prefetch_lookup::find_directory(
const std::filesystem::path& directory
) {
const auto dir_it = m_directory_lookup.find(directory);
const auto dir_match = dir_it != m_directory_lookup.end();
auto offset = index_type{};
if (dir_match)
{
for (index_type i{}; i != dir_match; ++i)
{
offset += m_directory_indices[offset];
}
}
return { std::make_pair(dir_it, offset), dir_match };
}
std::pair<pose_prefetch_lookup::index_iterator, dynamic_pose_store::id_type> pose_prefetch_lookup::find_index(
directory_iterator directory_it,
index_type index
) {
const auto [ dir_it, dir_offset ] = directory_it;
const auto index_count = m_directory_indices[dir_offset];
const auto indices_begin = m_directory_indices.begin() + dir_offset + 1;
const auto indices_end = indices_begin + index_count;
const auto it = std::upper_bound(indices_begin, indices_end, index);
const auto is_match = it != indices_begin and *std::prev(it) == index;
auto match = dynamic_pose_store::id_type{};
if (is_match)
{
const auto dir_index = dir_it->second;
const auto id_index = it - m_directory_indices.begin() - match - dir_index;
match = m_pose_ids[id_index];
}
return { it, match };
}
pose_prefetch_lookup::directory_iterator pose_prefetch_lookup::emplace_dir(
directory_iterator directory_it,
const std::filesystem::path& directory
) {
auto [ dir_it, dir_offset ] = directory_it;
dir_it = m_directory_lookup.emplace_hint(dir_it, directory, m_directory_lookup.size());
dir_offset = m_directory_indices.size();
m_directory_indices.push_back(1);
return { dir_it, dir_offset };
}
void pose_prefetch_lookup::emplace(
const std::filesystem::path& directory,
index_type index,
dynamic_pose_store::id_type id
) {
auto [ directory_it, dir_match ] = find_directory(directory);
if (not dir_match) [[unlikely]]
{
directory_it = emplace_dir(directory_it, directory);
}
const auto [ index_it, match ] = find_index(
directory_it,
index
);
if (not match)
{
}
emplace_hint_dir_index(
directory_it,
index_it,
index,
id
);
}
void pose_prefetch_lookup::emplace_hint_dir(
directory_iterator directory_it,
const std::filesystem::path& directory,
const index_type index,
const dynamic_pose_store::id_type id
) {
directory_it = emplace_dir(directory_it, directory);
const auto [ index_it, match ] = find_index(
directory_it,
index
);
if (match) [[unlikely]]
{
const auto dir_index = dir_it->second;
const auto id_index = index_it - m_directory_indices.begin() - dir_index;
m_pose_ids[id_index] = match; // TODO I guess I should warn?!?
}
else [[likely]]
{
emplace_hint_dir_index(
directory_it,
index_it,
index,
id
);
}
}
void pose_prefetch_lookup::emplace_hint_dir_index(
directory_iterator directory_it,
index_iterator index_it,
index_type index,
const dynamic_pose_store::id_type id
) {
const auto [ dir_it, dir_offset ] = directory_it;
const auto dir_index = dir_it->second;
index_it = m_directory_indices.emplace(index_it, index);
++m_directory_indices[dir_offset];
const auto id_index = index_it - m_directory_indices.begin() - dir_index;
const auto id_it = m_pose_ids.begin() + id_index;
m_pose_ids.insert(id_it, id);
}