fixes
This commit is contained in:
444
source/assets/data_loaders/generic/generic_3dtk_loader.ipp
Normal file
444
source/assets/data_loaders/generic/generic_3dtk_loader.ipp
Normal file
@@ -0,0 +1,444 @@
|
||||
#ifndef INCLUDE_GENERIC_3DTK_LOADER_IMPLEMENTATION
|
||||
# error Never include this file directly include 'generic_3dtk_loader.hpp'
|
||||
#endif
|
||||
|
||||
#include <charconv>
|
||||
#include <fstream>
|
||||
#include "util/logger.hpp"
|
||||
#include "glm/glm.hpp"
|
||||
#include "glm/gtx/euler_angles.hpp"
|
||||
#include <charconv>
|
||||
#include <fstream>
|
||||
|
||||
template<bool Normal, bool Color, bool Reflectance>
|
||||
ztu::result<pose_prefetch_lookup::index_type> generic_3dtk_loader<Normal, Color, Reflectance>::parse_index(
|
||||
const std::string_view filename
|
||||
) {
|
||||
static constexpr auto prefix = std::string_view{ "scan" };
|
||||
|
||||
auto name_view = filename.substr(0, name_view.find('.'));
|
||||
|
||||
if (name_view.length() <= prefix.length()) [[unlikely]]
|
||||
{
|
||||
return std::make_error_code(std::errc::invalid_argument);
|
||||
}
|
||||
|
||||
name_view = name_view.substr(prefix.length());
|
||||
|
||||
pose_prefetch_lookup::index_type index;
|
||||
|
||||
const auto res = std::from_chars(name_view.begin(), name_view.end(), index);
|
||||
|
||||
if (res.ec != std::errc{}) [[unlikely]]
|
||||
{
|
||||
return std::make_error_code(res.ec);
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
template<bool Normal, bool Color, bool Reflectance>
|
||||
std::error_code generic_3dtk_loader<Normal, Color, Reflectance>::prefetch(
|
||||
const file_dir_list& paths,
|
||||
prefetch_queue& queue
|
||||
) {
|
||||
|
||||
auto path_buffer = std::filesystem::path{};
|
||||
|
||||
for (const auto filename : paths.files)
|
||||
{
|
||||
path_buffer.assign(filename.begin(), filename.end());
|
||||
path_buffer.replace_extension(".pose");
|
||||
queue.uos_queue.files.push_back(path_buffer.c_str());
|
||||
}
|
||||
|
||||
// TODO optimize
|
||||
for (const auto directory : paths.directories)
|
||||
{
|
||||
queue.uos_queue.directories.push_back(directory);
|
||||
}
|
||||
}
|
||||
|
||||
template<bool Normal, bool Color, bool Reflectance>
|
||||
std::error_code generic_3dtk_loader<Normal, Color, Reflectance>::load(
|
||||
dynamic_point_cloud_buffer& buffer,
|
||||
const file_dir_list& paths,
|
||||
prefetch_lookup& asset_lookup,
|
||||
dynamic_point_cloud_store& store,
|
||||
const bool pedantic
|
||||
) {
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
auto in = std::ifstream{};
|
||||
auto path_buffer = fs::path{};
|
||||
auto error = std::error_code{};
|
||||
|
||||
const auto load_file = [&](const char* filename)
|
||||
{
|
||||
// TODO look up pose
|
||||
auto scan_index = pose_prefetch_lookup::index_type{};
|
||||
|
||||
if (auto res = parse_index(filename))
|
||||
{
|
||||
scan_index = *res;
|
||||
}
|
||||
else [[unlikely]]
|
||||
{
|
||||
error = res.error();
|
||||
ztu::logger::error(
|
||||
"Error occurred while parsing scan index in filename %: [%] %",
|
||||
error.category().name(),
|
||||
error.message()
|
||||
);
|
||||
}
|
||||
|
||||
auto pose = asset_lookup.poses.find();
|
||||
|
||||
|
||||
const auto id_it = id_lookup.find(filename);
|
||||
|
||||
if (id_it != id_lookup.end()) [[unlikely]]
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
in.open(filename);
|
||||
if (in.is_open())
|
||||
{
|
||||
if ((error = this->read_point_file(filename, buffer))) {
|
||||
return error;
|
||||
}
|
||||
|
||||
this->transform_point_cloud(buffer.positions(), pose);
|
||||
|
||||
const auto id = store.add(std::move(buffer));
|
||||
id_lookup.emplace_hint(id_it, filename, id);
|
||||
}
|
||||
else
|
||||
{
|
||||
ztu::logger::error("Cannot open 3dtk file %", filename);
|
||||
}
|
||||
|
||||
in.close();
|
||||
};
|
||||
|
||||
for (const auto filename : paths.files)
|
||||
{
|
||||
load_file(filename.data());
|
||||
}
|
||||
|
||||
|
||||
|
||||
for (const auto directory : paths.directories)
|
||||
{
|
||||
directory_buffer.assign(directory.begin(), directory.end());
|
||||
directory_buffer /= "frames";
|
||||
|
||||
const auto directory_exists = not fs::is_directory(directory_buffer, error);
|
||||
|
||||
if (error or not directory_exists) [[unlikely]]
|
||||
{
|
||||
ztu::logger::error("Could not open point cloud directory %", directory_buffer);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const auto& filename : fs::directory_iterator{ directory_buffer }) {
|
||||
|
||||
auto point_filename = reinterpret_cast<const fs::path&>(filename);
|
||||
if (point_filename.extension() != ".3d")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
load_file(filename.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
template<bool Normal, bool Color, bool Reflectance>
|
||||
std::error_code generic_3dtk_loader<Normal, Color, Reflectance>::load_directory(
|
||||
dynamic_data_loader_ctx& ctx,
|
||||
dynamic_point_cloud_store& store,
|
||||
const std::filesystem::path& path,
|
||||
const bool pedantic
|
||||
) {
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
std::error_code error;
|
||||
|
||||
const auto directory_exists = not fs::is_directory(path, error);
|
||||
if (error)
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
if (not directory_exists)
|
||||
{
|
||||
return make_error_code(std::errc::no_such_file_or_directory);
|
||||
}
|
||||
|
||||
for (const auto& filename : fs::directory_iterator{ path / "frames" }) {
|
||||
|
||||
auto point_filename = reinterpret_cast<const fs::path&>(filename);
|
||||
if (point_filename.extension() != ".3d") {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((error = load(ctx, store, point_filename, pedantic)))
|
||||
{
|
||||
ztu::logger::error(
|
||||
"Error while loading point cloud '%': [%] %",
|
||||
point_filename,
|
||||
error.category().name(),
|
||||
error.message()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T, std::size_t Count>
|
||||
std::error_code read_vector(std::string_view& line, std::array<T, Count>& vec) {
|
||||
for (auto& component : vec)
|
||||
{
|
||||
auto it = line.begin();
|
||||
|
||||
const auto [minus, plus] = std::pair{ *it == '-', *it == '+' };
|
||||
it += plus or minus ? 3 : 2; // skip '[-+]?0x'
|
||||
|
||||
const auto [ ptr, ec ] = std::from_chars(
|
||||
it, line.end(),
|
||||
component,
|
||||
std::chars_format::hex
|
||||
);
|
||||
|
||||
if (ec != std::errc{})
|
||||
{
|
||||
return std::make_error_code(ec);
|
||||
}
|
||||
|
||||
if (minus) {
|
||||
component *= -1.0;
|
||||
}
|
||||
|
||||
line = { ptr + sizeof(' '), line.end() };
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
template<bool Normal, bool Color, bool Reflectance>
|
||||
std::error_code generic_3dtk_loader<Normal, Color, Reflectance>::read_point_file(
|
||||
const std::filesystem::path& filename,
|
||||
dynamic_point_cloud_data& point_cloud
|
||||
) {
|
||||
std::error_code error;
|
||||
|
||||
auto in = std::ifstream(filename);
|
||||
|
||||
if (not in.is_open()) {
|
||||
return std::make_error_code(static_cast<std::errc>(errno));
|
||||
}
|
||||
|
||||
std::string line;
|
||||
if (not std::getline(in, line))
|
||||
{
|
||||
return std::make_error_code(std::errc::invalid_seek);
|
||||
}
|
||||
|
||||
constexpr auto expected_component_count = []()
|
||||
{
|
||||
auto count = std::tuple_size_v<components::point_cloud_vertex::position>;
|
||||
|
||||
if (Normal)
|
||||
{
|
||||
count += std::tuple_size_v<components::point_cloud_vertex::normal>;
|
||||
}
|
||||
|
||||
if (Color)
|
||||
{
|
||||
count += std::tuple_size_v<components::point_cloud_vertex::color>;
|
||||
}
|
||||
|
||||
if (Reflectance)
|
||||
{
|
||||
count += std::tuple_size_v<components::point_cloud_vertex::reflectance>;
|
||||
}
|
||||
|
||||
return count;
|
||||
}();
|
||||
|
||||
ztu::u32 component_count;
|
||||
std::chars_format float_format;
|
||||
|
||||
if ((error = analyze_component_format(line, component_count, float_format)))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
if (component_count != expected_component_count)
|
||||
{
|
||||
return std::make_error_code(std::errc::invalid_argument);
|
||||
}
|
||||
|
||||
auto& positions = point_cloud.positions();
|
||||
auto& normals = point_cloud.normals();
|
||||
auto& colors = point_cloud.colors();
|
||||
auto& reflectances = point_cloud.reflectances();
|
||||
|
||||
do
|
||||
{
|
||||
auto line_view = std::string_view{ line };
|
||||
|
||||
components::point_cloud_vertex::position position;
|
||||
if ((error = read_vector(line_view, position)))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
positions.push_back(position);
|
||||
|
||||
if constexpr (Normal)
|
||||
{
|
||||
components::point_cloud_vertex::normal normal;
|
||||
if ((error = read_vector(line_view, normal)))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
normals.push_back(normal);
|
||||
}
|
||||
|
||||
if constexpr (Color)
|
||||
{
|
||||
components::point_cloud_vertex::color color;
|
||||
if ((error = read_vector(line_view, color)))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
colors.push_back(color);
|
||||
}
|
||||
|
||||
if constexpr (Reflectance)
|
||||
{
|
||||
components::point_cloud_vertex::reflectance reflectance;
|
||||
if ((error = read_vector(line_view, reflectance)))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
reflectances.push_back(reflectance);
|
||||
}
|
||||
}
|
||||
while (std::getline(in, line));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::error_code base_3dtk_loader::read_pose_file(
|
||||
const std::filesystem::path& filename,
|
||||
glm::mat4& pose
|
||||
) {
|
||||
auto in = std::ifstream(filename);
|
||||
if (not in.is_open()) {
|
||||
return std::make_error_code(static_cast<std::errc>(errno));
|
||||
}
|
||||
std::string line;
|
||||
|
||||
std::array<glm::vec3, 2> numbers{};
|
||||
|
||||
for (std::size_t row{}; row != 2; ++row) {
|
||||
|
||||
std::getline(in, line);
|
||||
|
||||
auto it = line.cbegin().base();
|
||||
auto end = line.cend().base();
|
||||
|
||||
for (glm::vec3::length_type col{}; col != 3; ++col) {
|
||||
|
||||
const auto [ ptr, ec ] = std::from_chars(
|
||||
it, end,
|
||||
numbers[row][col],
|
||||
std::chars_format::general
|
||||
);
|
||||
|
||||
if (ec != std::errc{}) {
|
||||
return std::make_error_code(ec);
|
||||
}
|
||||
|
||||
it = ptr + 1; // skip space in between components
|
||||
}
|
||||
}
|
||||
|
||||
const auto& translation = numbers[0];
|
||||
auto& angles = numbers[1];
|
||||
angles *= static_cast<float>(M_PI / 180.0);
|
||||
|
||||
pose = (
|
||||
glm::translate(glm::identity<glm::mat4>(), translation) *
|
||||
glm::eulerAngleXYZ(angles[0], angles[1], angles[2])
|
||||
);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::error_code base_3dtk_loader::analyze_component_format(
|
||||
const std::string& line,
|
||||
ztu::u32& component_count,
|
||||
std::chars_format& format
|
||||
) {
|
||||
auto begin = line.cbegin().base();
|
||||
auto end = line.cend().base();
|
||||
|
||||
format = std::chars_format::general;
|
||||
|
||||
component_count = 0;
|
||||
float buffer;
|
||||
|
||||
for (auto it = begin; it < end; it += sizeof(' '))
|
||||
{
|
||||
it += *it == '-' or *it == '+';
|
||||
|
||||
std::chars_format current_format;
|
||||
if (*it == '0' and std::next(it) < end and *std::next(it) == 'x')
|
||||
{
|
||||
it += 2; // skip '0x'
|
||||
current_format = std::chars_format::hex;
|
||||
}
|
||||
else
|
||||
{
|
||||
current_format = std::chars_format::general;
|
||||
}
|
||||
|
||||
if (it == begin and current_format != format)
|
||||
{
|
||||
return std::make_error_code(std::errc::invalid_argument);
|
||||
}
|
||||
|
||||
const auto [next_it, err] = std::from_chars(it, end, buffer, current_format);
|
||||
if (err != std::errc())
|
||||
{
|
||||
return std::make_error_code(err);
|
||||
}
|
||||
|
||||
it = next_it;
|
||||
format = current_format;
|
||||
++component_count;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
void base_3dtk_loader::transform_point_cloud(
|
||||
std::span<components::point_cloud_vertex::position::value_type> points,
|
||||
const glm::mat4& pose
|
||||
) {
|
||||
for (auto& [ x, y, z ] : points) {
|
||||
auto vec = glm::vec4{ x, y, z, 1.0f };
|
||||
vec = pose * vec;
|
||||
x = vec.x;
|
||||
y = vec.y;
|
||||
z = vec.z;
|
||||
}
|
||||
}
|
||||
36
source/assets/data_loaders/glsl_loader.cpp
Normal file
36
source/assets/data_loaders/glsl_loader.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#include "assets/data_loaders/glsl_loader.hpp"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
std::error_code glsl_loader::load(
|
||||
const std::filesystem::path& filename,
|
||||
std::string& source
|
||||
) {
|
||||
|
||||
auto file = std::ifstream(filename);
|
||||
if (not file.is_open())
|
||||
{
|
||||
return std::make_error_code(std::errc::no_such_file_or_directory);
|
||||
}
|
||||
|
||||
file.seekg(0, std::ios::end);
|
||||
const auto size = file.tellg();
|
||||
|
||||
if (size == 0 or size == std::numeric_limits<std::streamsize>::max())
|
||||
{
|
||||
return std::make_error_code(std::errc::invalid_seek);
|
||||
}
|
||||
|
||||
source.reserve(size);
|
||||
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
source.assign(
|
||||
std::istreambuf_iterator<char>(file),
|
||||
std::istreambuf_iterator<char>()
|
||||
);
|
||||
|
||||
file.close();
|
||||
|
||||
return {};
|
||||
}
|
||||
302
source/assets/data_loaders/kitti_loader.cpp
Normal file
302
source/assets/data_loaders/kitti_loader.cpp
Normal file
@@ -0,0 +1,302 @@
|
||||
#include "assets/data_loaders/kitti_loader.hpp"
|
||||
|
||||
#include "glm/glm.hpp"
|
||||
|
||||
#include <charconv>
|
||||
#include <fstream>
|
||||
#include <glm/ext/matrix_transform.hpp>
|
||||
|
||||
#include "assets/components/point_cloud_vertex_components.hpp"
|
||||
#include "util/binary_ifstream.hpp"
|
||||
#include "util/logger.hpp"
|
||||
|
||||
|
||||
ztu::result<std::string_view> kitti_loader::parent_directory(const std::string_view path)
|
||||
{
|
||||
const auto sep_index = path.rfind(std::filesystem::path::preferred_separator);
|
||||
|
||||
if (sep_index == std::string_view::npos)
|
||||
{
|
||||
return std::unexpected(std::make_error_code(std::errc::no_such_file_or_directory));
|
||||
}
|
||||
|
||||
return path.substr(0, sep_index);
|
||||
};
|
||||
|
||||
std::error_code kitti_loader::prefetch(
|
||||
const file_dir_list& paths,
|
||||
prefetch_queue& queue
|
||||
) {
|
||||
|
||||
// Directories can simply be passed on
|
||||
queue.kitti_pose_queue.directories.push_back(paths.directories);
|
||||
|
||||
// For files, we just forward the files directory
|
||||
for (const auto file : queue.kitti_pose_queue.files)
|
||||
{
|
||||
if (const auto base_directory = parent_directory(file).and_then(parent_directory))
|
||||
{
|
||||
queue.kitti_pose_queue.directories.push_back(*base_directory);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO remove from list
|
||||
ztu::logger::error("Malformed kitti file path: %.", file);
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::error_code kitti_loader::load(
|
||||
dynamic_point_cloud_buffer& buffer,
|
||||
const file_dir_list& paths,
|
||||
prefetch_lookup& id_lookup,
|
||||
dynamic_data_store& store,
|
||||
bool
|
||||
) {
|
||||
namespace fs = std::filesystem;
|
||||
std::error_code error;
|
||||
|
||||
std::vector<dynamic_pose_store::iterator_type> pose_its;
|
||||
pose_its.reserve(paths.files.size());
|
||||
|
||||
auto processed_filenames = ztu::string_list{};
|
||||
|
||||
auto path_buffer = fs::path{};
|
||||
|
||||
const auto preprocess_filename = [&](
|
||||
std::string_view path,
|
||||
const auto& directory,
|
||||
std::string_view filename,
|
||||
const pose_prefetch_lookup::directory_iterator& dir_it
|
||||
) {
|
||||
const auto pose_index = frame_id_from_filename(filename);
|
||||
if (not pose_index) [[unlikely]]
|
||||
{
|
||||
ztu::logger::error("Could not parse frame id from kitti file path: %.", filename);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto [ index_it, pose_id_match ] = id_lookup.poses.find_index(dir_it, *pose_index);
|
||||
|
||||
if (not pose_id_match) [[unlikely]]
|
||||
{
|
||||
ztu::logger::error("No matching pose index (%) found in directory (%).", directory, *pose_index);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto [ pose_it, pose_match ] = store.poses.find(pose_id_match);
|
||||
if (not pose_id_match) [[unlikely]]
|
||||
{
|
||||
ztu::logger::error("No matching pose found for id: %.", pose_id_match);
|
||||
return;
|
||||
}
|
||||
|
||||
processed_filenames.push_back(path);
|
||||
pose_its.push_back(pose_it);
|
||||
};
|
||||
|
||||
for (const auto file : paths.files)
|
||||
{
|
||||
path_buffer.assign(file.begin(), file.end());
|
||||
|
||||
if (not fs::is_regular_file(path_buffer))
|
||||
{
|
||||
ztu::logger::error("Given kitti file does not exist: %.", path_buffer);
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto sep_index = file.rfind(fs::path::preferred_separator);
|
||||
|
||||
if (sep_index == std::string_view::npos) [[unlikely]]
|
||||
{
|
||||
ztu::logger::error("Could not parse frame directory from kitti file path: %.", file);
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto base_directory = parent_directory(file.substr(0, sep_index));
|
||||
if (not base_directory) [[unlikely]]
|
||||
{
|
||||
ztu::logger::error("Could not parse base directory from kitti file path: %.", file);
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto [ dir_it, dir_match ] = id_lookup.poses.find_directory(*base_directory);
|
||||
if (not dir_match) [[unlikely]]
|
||||
{
|
||||
ztu::logger::error("No matching pose directory found for %.", file);
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto filename = file.substr(sep_index + 1);
|
||||
|
||||
preprocess_filename(
|
||||
file,
|
||||
*base_directory,
|
||||
filename,
|
||||
dir_it
|
||||
);
|
||||
}
|
||||
|
||||
for (const auto directory : paths.directories)
|
||||
{
|
||||
path_buffer.assign(directory.begin(), directory.end());
|
||||
|
||||
const auto [ dir_it, dir_match ] = id_lookup.poses.find_directory(path_buffer);
|
||||
if (not dir_match) [[unlikely]]
|
||||
{
|
||||
ztu::logger::error("No matching pose directory found for %.", path_buffer);
|
||||
continue;
|
||||
}
|
||||
|
||||
path_buffer /= frame_folder;
|
||||
|
||||
if (not fs::is_directory(path_buffer))
|
||||
{
|
||||
ztu::logger::error("Given kitti directory does not exist: %.", directory);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const auto& file : fs::directory_iterator{ path_buffer })
|
||||
{
|
||||
const auto file_path = std::string_view{ file.path().c_str() };
|
||||
|
||||
const auto extension_begin = file_path.rfind('.');
|
||||
|
||||
if (extension_begin == std::string_view::npos or file_path.substr(extension_begin) != ".bin")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto filename_begin = file_path.rfind(fs::path::preferred_separator, extension_begin);
|
||||
filename_begin = filename_begin == std::string_view::npos ? 0 : filename_begin + 1;
|
||||
|
||||
const auto filename_only = file_path.substr(filename_begin);
|
||||
|
||||
const auto pose_index = frame_id_from_filename(filename_only);
|
||||
if (not pose_index) [[unlikely]]
|
||||
{
|
||||
ztu::logger::error("Could not parse frame id from kitti filename: %.", filename_only);
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto [ index_it, pose_id_match ] = id_lookup.poses.find_index(dir_it, *pose_index);
|
||||
|
||||
if (not pose_id_match) [[unlikely]]
|
||||
{
|
||||
ztu::logger::error("No matching pose index (%) found in directory (%).", directory, *pose_index);
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto [ pose_it, pose_match ] = store.poses.find(pose_id_match);
|
||||
if (not pose_id_match) [[unlikely]]
|
||||
{
|
||||
ztu::logger::error("No matching pose found for id: %.", pose_id_match);
|
||||
continue;
|
||||
}
|
||||
|
||||
processed_filenames.push_back(file_path);
|
||||
pose_its.push_back(pose_it);
|
||||
|
||||
preprocess_filename(
|
||||
file_path,
|
||||
directory,
|
||||
filename_only,
|
||||
dir_it
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (const auto [ filename, pose_it ] : std::ranges::views::zip_view(processed_filenames, pose_its))
|
||||
{
|
||||
buffer.clear();
|
||||
|
||||
if ((error = load_point_file(filename, buffer)))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
transform_point_cloud(buffer.positions(), *pose_it);
|
||||
|
||||
store.point_clouds.add(buffer);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void kitti_loader::transform_point_cloud(
|
||||
std::span<components::point_cloud_vertex::position> points,
|
||||
const glm::mat4& pose
|
||||
) {
|
||||
for (auto& [ x, y, z ] : points) {
|
||||
auto vec = glm::vec4{ x, y, z, 1.0f };
|
||||
vec = pose * vec;
|
||||
x = vec.x;
|
||||
y = vec.y;
|
||||
z = vec.z;
|
||||
}
|
||||
}
|
||||
|
||||
std::error_code kitti_loader::load_point_file(
|
||||
const std::filesystem::path& filename,
|
||||
dynamic_point_cloud_buffer& point_cloud
|
||||
) {
|
||||
|
||||
auto in = binary_ifstream{};
|
||||
|
||||
auto error = std::error_code{};
|
||||
|
||||
if ((error == in.open(filename, true)))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
const auto read_vector = [&in](auto& vector) -> std::error_code
|
||||
{
|
||||
for (auto& component : vector)
|
||||
{
|
||||
float component32;
|
||||
if (const auto e = in.read_ieee754<std::endian::little>(component32))
|
||||
{
|
||||
return e;
|
||||
}
|
||||
component = component32;
|
||||
}
|
||||
return {};
|
||||
};
|
||||
|
||||
components::point_cloud_vertex::position position;
|
||||
|
||||
auto& positions = point_cloud.positions();
|
||||
|
||||
while (not ((error = read_vector(position)))) {
|
||||
positions.push_back(position);
|
||||
if ((error = in.skip<float>())) // TODO what am I skipping here?!?
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (static_cast<std::errc>(error.value()) != std::errc::result_out_of_range)
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
ztu::result<std::size_t> kitti_loader::frame_id_from_filename(
|
||||
std::string_view filename
|
||||
) {
|
||||
std::size_t id;
|
||||
const auto result = std::from_chars(filename.cbegin(), filename.cend(), id);
|
||||
|
||||
if (result.ec != std::errc{})
|
||||
{
|
||||
return std::unexpected(std::make_error_code(result.ec));
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
163
source/assets/data_loaders/kitti_pose_loader.cpp
Normal file
163
source/assets/data_loaders/kitti_pose_loader.cpp
Normal file
@@ -0,0 +1,163 @@
|
||||
#include "assets/data_loaders/kitti_pose_loader.hpp"
|
||||
|
||||
#include "assets/dynamic_read_buffers/dynamic_pose_buffer.hpp"
|
||||
#include <fstream>
|
||||
#include <glm/ext/matrix_transform.hpp>
|
||||
#include "util/logger.hpp"
|
||||
|
||||
inline std::error_code kitti_pose_loader::parse_pose(
|
||||
std::ifstream& in,
|
||||
dynamic_pose_buffer& pose
|
||||
) {
|
||||
for (dynamic_pose_buffer::length_type row{}; row != 3; ++row)
|
||||
{
|
||||
for (dynamic_pose_buffer::length_type col{}; col != 4; ++col)
|
||||
{
|
||||
if (not (in >> pose[row][col]))
|
||||
{
|
||||
return std::make_error_code(std::errc::result_out_of_range);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
std::error_code kitti_pose_loader::prefetch(
|
||||
const file_dir_list& paths,
|
||||
prefetch_queue& queue
|
||||
) {
|
||||
// Nothing to be done here
|
||||
}
|
||||
|
||||
std::error_code kitti_pose_loader::load(
|
||||
dynamic_pose_buffer& buffer,
|
||||
const file_dir_list& paths,
|
||||
prefetch_lookup& id_lookup,
|
||||
dynamic_data_store& store,
|
||||
bool pedantic
|
||||
) {
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
auto path_buffer = fs::path{};
|
||||
auto in = std::ifstream{}; // TODO disable exceptions (for other loaders as well)
|
||||
auto pose_buffer = dynamic_pose_buffer{};
|
||||
|
||||
pose_buffer = glm::identity<glm::mat4>();
|
||||
|
||||
auto processed_filenames = ztu::string_list{};
|
||||
|
||||
processed_filenames.reserve(
|
||||
paths.files.character_count() +
|
||||
paths.directories.character_count() +
|
||||
paths.directories.size() * pose_filename.size(),
|
||||
paths.files.size() + paths.directories.size()
|
||||
);
|
||||
|
||||
const auto preprocess_file = [&]()
|
||||
{
|
||||
if (not fs::is_regular_file(path_buffer))
|
||||
{
|
||||
ztu::logger::error("Kitti pose file does not exist: %", path_buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
processed_filenames.push_back(path_buffer.c_str());
|
||||
};
|
||||
|
||||
for (const auto directory : paths.directories) {
|
||||
path_buffer.assign(directory.begin(), directory.end());
|
||||
path_buffer /= "pose.txt";
|
||||
preprocess_file();
|
||||
}
|
||||
|
||||
for (const auto file : paths.files) {
|
||||
path_buffer.assign(file.begin(), file.end());
|
||||
preprocess_file();
|
||||
}
|
||||
|
||||
for (const auto filename : processed_filenames)
|
||||
{
|
||||
in.open(filename.data()); // Safe because string list adds null terminator
|
||||
if (not in.is_open())
|
||||
{
|
||||
ztu::logger::error("Cannot open kitti pose file %", path_buffer);
|
||||
continue;
|
||||
}
|
||||
|
||||
in >> std::skipws;
|
||||
|
||||
for (auto i = pose_prefetch_lookup::index_type{}; in.peek() != std::ifstream::traits_type::eof(); ++i)
|
||||
{
|
||||
if (const auto error = parse_pose(in, pose_buffer))
|
||||
{
|
||||
ztu::logger::error(
|
||||
"Error occurred while parsing kitti pose % in file %: [%] %",
|
||||
i,
|
||||
path_buffer,
|
||||
error.category().name(),
|
||||
error.message()
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto id = store.poses.add(pose_buffer);
|
||||
|
||||
// TODO if (not) removing the path separator creates issues.
|
||||
const auto directory = filename.substr(0, filename.length() - pose_filename.length());
|
||||
|
||||
id_lookup.poses.emplace(directory, i, id);
|
||||
}
|
||||
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
|
||||
void kitti_pose_loader::load(
|
||||
const ztu::string_list& directories,
|
||||
dynamic_pose_store& store,
|
||||
pose_prefetch_lookup& id_lookup
|
||||
) {
|
||||
|
||||
auto filename_buffer = std::filesystem::path{};
|
||||
auto in = std::ifstream{}; // TODO disable exceptions (for other loaders as well)
|
||||
auto pose_buffer = dynamic_pose_buffer{};
|
||||
|
||||
pose_buffer = glm::identity<glm::mat4>();
|
||||
|
||||
for (const auto directory : directories)
|
||||
{
|
||||
filename_buffer = directory;
|
||||
filename_buffer /= "pose.txt";
|
||||
|
||||
in.open(filename_buffer);
|
||||
if (not in.is_open())
|
||||
{
|
||||
ztu::logger::error("Cannot open kitti pose file %", filename_buffer);
|
||||
continue;
|
||||
}
|
||||
|
||||
in >> std::skipws;
|
||||
|
||||
for (auto i = pose_prefetch_lookup::index_type{}; in.peek() != std::ifstream::traits_type::eof(); ++i)
|
||||
{
|
||||
if (const auto error = parse_pose(in, pose_buffer))
|
||||
{
|
||||
ztu::logger::error(
|
||||
"Error occurred while parsing kitti pose % in file %: [%] %",
|
||||
i,
|
||||
filename_buffer,
|
||||
error.category().name(),
|
||||
error.message()
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto id = store.add(pose_buffer);
|
||||
id_lookup.emplace(directory, i, id);
|
||||
}
|
||||
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
398
source/assets/data_loaders/mtl_loader.cpp
Normal file
398
source/assets/data_loaders/mtl_loader.cpp
Normal file
@@ -0,0 +1,398 @@
|
||||
#include "assets/data_loaders/mtl_loader.hpp"
|
||||
|
||||
#include <charconv>
|
||||
#include <fstream>
|
||||
#include <array>
|
||||
|
||||
#include "util/logger.hpp"
|
||||
#include "util/for_each.hpp"
|
||||
#include "util/line_parser.hpp"
|
||||
|
||||
#include "assets/dynamic_data_loaders/dynamic_texture_loader.hpp"
|
||||
|
||||
namespace mtl_loader_error {
|
||||
|
||||
struct category : std::error_category {
|
||||
[[nodiscard]] const char* name() const noexcept override {
|
||||
return "connector";
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string message(int ev) const override {
|
||||
switch (static_cast<codes>(ev)) {
|
||||
using enum codes;
|
||||
case mtl_cannot_open_file:
|
||||
return "Cannot open mtl file.";
|
||||
case mtl_cannot_open_texture:
|
||||
return "Cannot open texture file.";
|
||||
case mtl_malformed_ambient_color:
|
||||
return "File contains malformed 'Ka' statement.";
|
||||
case mtl_malformed_diffuse_color:
|
||||
return "File contains malformed 'Kd' statement.";
|
||||
case mtl_malformed_specular_color:
|
||||
return "File contains malformed 'Ks' statement.";
|
||||
case mtl_malformed_specular_exponent:
|
||||
return "File contains malformed 'Ns' statement.";
|
||||
case mtl_malformed_dissolve:
|
||||
return "File contains malformed 'd' statement.";
|
||||
case mlt_unknown_line_begin:
|
||||
return "Unknown mtl line begin";
|
||||
default:
|
||||
using namespace std::string_literals;
|
||||
return "unrecognized error ("s + std::to_string(ev) + ")";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mesh_loader_error
|
||||
|
||||
inline std::error_category& connector_error_category() {
|
||||
static mtl_loader_error::category category;
|
||||
return category;
|
||||
}
|
||||
|
||||
namespace mtl_loader_error {
|
||||
|
||||
inline std::error_code make_error_code(codes e) {
|
||||
return { static_cast<int>(e), connector_error_category() };
|
||||
}
|
||||
|
||||
} // namespace mtl_loader_error
|
||||
|
||||
|
||||
template<typename T, std::size_t Count>
|
||||
std::errc parse_numeric_vector(std::string_view param, std::array<T, Count>& values) {
|
||||
auto it = param.begin(), end = param.end();
|
||||
|
||||
for (auto& value : values)
|
||||
{
|
||||
if (it >= end)
|
||||
{
|
||||
return std::errc::invalid_argument;
|
||||
}
|
||||
|
||||
const auto [ptr, ec] = std::from_chars(it, end, value);
|
||||
|
||||
if (ec != std::errc{})
|
||||
{
|
||||
return ec;
|
||||
}
|
||||
|
||||
it = ptr + 1; // skip space in between components
|
||||
}
|
||||
|
||||
return {};
|
||||
};
|
||||
|
||||
std::optional<dynamic_material_store::id_type> mtl_loader::find_id(std::string_view name)
|
||||
{
|
||||
const auto it = m_id_lookup.find(name);
|
||||
|
||||
if (it == m_id_lookup.end())
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void mtl_loader::clear_name_lookup() {
|
||||
m_id_lookup.clear();
|
||||
}
|
||||
|
||||
std::error_code mtl_loader::load_directory(
|
||||
dynamic_data_loader_ctx& ctx,
|
||||
dynamic_material_store& store,
|
||||
components::material::flags enabled_components,
|
||||
const std::filesystem::path& path,
|
||||
const bool pedantic
|
||||
) {
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
if (not fs::exists(path))
|
||||
{
|
||||
return make_error_code(std::errc::no_such_file_or_directory);
|
||||
}
|
||||
|
||||
for (const auto& file : fs::directory_iterator{ path })
|
||||
{
|
||||
const auto& file_path = file.path();
|
||||
|
||||
if (file_path.extension() != ".obj")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (const auto e = load(
|
||||
ctx,
|
||||
store,
|
||||
enabled_components,
|
||||
path,
|
||||
pedantic
|
||||
)) {
|
||||
ztu::logger::error(
|
||||
"Error while loading obj file '%': [%] %",
|
||||
file_path,
|
||||
e.category().name(),
|
||||
e.message()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::error_code mtl_loader::load(
|
||||
dynamic_data_loader_ctx& ctx,
|
||||
dynamic_material_store& store,
|
||||
components::material::flags enabled_components,
|
||||
const std::filesystem::path& filename,
|
||||
const bool pedantic
|
||||
) {
|
||||
using mtl_loader_error::codes;
|
||||
using mtl_loader_error::make_error_code;
|
||||
|
||||
using flags = components::material::flags;
|
||||
|
||||
const auto component_disabled = [&](const components::material::flags component) {
|
||||
return (enabled_components & component) == flags::none;
|
||||
};
|
||||
|
||||
// TODO unroll stuff
|
||||
const auto textures_disabled = component_disabled(flags::ambient_filter_texture);
|
||||
const auto surface_properties_disabled = component_disabled(flags::surface_properties);
|
||||
const auto transparencies_disabled = component_disabled(flags::transparency);
|
||||
|
||||
auto in = std::ifstream{ filename };
|
||||
if (not in.is_open()) {
|
||||
return make_error_code(codes::mtl_cannot_open_file);
|
||||
}
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
const auto directory = fs::canonical(fs::path(filename).parent_path());
|
||||
|
||||
auto name = std::string{};
|
||||
auto material = dynamic_material_data{};
|
||||
|
||||
const auto push_material = [&]()
|
||||
{
|
||||
if (not name.empty())
|
||||
{
|
||||
const auto id = store.add(std::move(material));
|
||||
m_id_lookup.emplace(std::move(name), id);
|
||||
}
|
||||
name = std::string{};
|
||||
material = dynamic_material_data{};
|
||||
};
|
||||
|
||||
const auto load_texture = [&](
|
||||
const std::string_view path,
|
||||
std::string_view texture_type_name,
|
||||
auto&& f
|
||||
) {
|
||||
auto texture_filename = fs::path(path);
|
||||
if (texture_filename.is_relative())
|
||||
{
|
||||
texture_filename = directory / texture_filename;
|
||||
}
|
||||
|
||||
const auto extension = texture_filename.extension().string();
|
||||
|
||||
auto texture_type = std::string_view{ extension };
|
||||
if (not texture_type.empty() and texture_type.front() == '.')
|
||||
{
|
||||
texture_type = texture_type.substr(1);
|
||||
}
|
||||
|
||||
if (const auto loader_id = ctx.texture_loader.find_loader(texture_type))
|
||||
{
|
||||
if (auto res = ctx.texture_loader.read(
|
||||
ctx,
|
||||
*loader_id,
|
||||
texture_filename,
|
||||
pedantic
|
||||
)) {
|
||||
f(*res);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto error = res.error();
|
||||
ztu::logger::warn(
|
||||
"Error while loading % texture '%': [%] %",
|
||||
texture_type_name,
|
||||
path,
|
||||
error.category().name(),
|
||||
error.message()
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ztu::logger::warn(
|
||||
"Failed to load % texture '%' because extension is not supported.",
|
||||
texture_type_name,
|
||||
path
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const auto ec = ztu::parse_lines<codes>(
|
||||
in,
|
||||
pedantic,
|
||||
ztu::make_line_parser("newmtl ", ztu::is_not_repeating, [&](const auto& param)
|
||||
{
|
||||
push_material();
|
||||
name = param;
|
||||
|
||||
return codes::ok;
|
||||
}),
|
||||
ztu::make_line_parser("Ka ", ztu::is_not_repeating, [&](const auto& param)
|
||||
{
|
||||
if (surface_properties_disabled) return codes::ok;
|
||||
|
||||
auto& properties = material.initialized_surface_properties();
|
||||
if (parse_numeric_vector(param, properties.ambient_filter) != std::errc{}) [[unlikely]]
|
||||
{
|
||||
return codes::mtl_malformed_ambient_color;
|
||||
}
|
||||
|
||||
material.components() |= flags::surface_properties;
|
||||
|
||||
return codes::ok;
|
||||
}),
|
||||
ztu::make_line_parser("Kd ", ztu::is_not_repeating, [&](const auto& param)
|
||||
{
|
||||
if (surface_properties_disabled) return codes::ok;
|
||||
|
||||
auto& properties = material.initialized_surface_properties();
|
||||
if (parse_numeric_vector(param, properties.diffuse_filter) != std::errc{}) [[unlikely]]
|
||||
{
|
||||
return codes::mtl_malformed_diffuse_color;
|
||||
}
|
||||
|
||||
material.components() |= flags::surface_properties;
|
||||
|
||||
return codes::ok;
|
||||
}),
|
||||
ztu::make_line_parser("Ks ", ztu::is_not_repeating, [&](const auto& param)
|
||||
{
|
||||
if (surface_properties_disabled) return codes::ok;
|
||||
|
||||
auto& properties = material.initialized_surface_properties();
|
||||
if (parse_numeric_vector(param, properties.specular_filter) != std::errc{}) [[unlikely]]
|
||||
{
|
||||
return codes::mtl_malformed_specular_color;
|
||||
}
|
||||
|
||||
material.components() |= flags::surface_properties;
|
||||
|
||||
return codes::ok;
|
||||
}),
|
||||
ztu::make_line_parser("Ns ", ztu::is_not_repeating, [&](const auto& param)
|
||||
{
|
||||
if (surface_properties_disabled) return codes::ok;
|
||||
|
||||
auto& properties = material.initialized_surface_properties();
|
||||
std::array<float, 1> shininess{};
|
||||
if (parse_numeric_vector(param, shininess) != std::errc{}) [[unlikely]]
|
||||
{
|
||||
return codes::mtl_malformed_specular_exponent;
|
||||
}
|
||||
|
||||
properties.shininess = shininess.front();
|
||||
material.components() |= flags::surface_properties;
|
||||
|
||||
return codes::ok;
|
||||
}),
|
||||
ztu::make_line_parser("d ", ztu::is_not_repeating, [&](const auto& param)
|
||||
{
|
||||
if (transparencies_disabled) return codes::ok;
|
||||
|
||||
std::array<float, 1> transparency{};
|
||||
if (parse_numeric_vector(param, transparency) != std::errc{}) [[unlikely]]
|
||||
{
|
||||
return codes::mtl_malformed_dissolve;
|
||||
}
|
||||
|
||||
material.transparency().emplace(transparency.front());
|
||||
material.components() |= flags::transparency;
|
||||
|
||||
return codes::ok;
|
||||
}),
|
||||
ztu::make_line_parser("map_Ka ", ztu::is_not_repeating, [&](const auto& param)
|
||||
{
|
||||
if (textures_disabled) return codes::ok;
|
||||
|
||||
load_texture(param, "ambient color", [&](const auto id) {
|
||||
material.ambient_color_texture_id() = id;
|
||||
material.components() |= flags::ambient_filter_texture;
|
||||
});
|
||||
|
||||
return codes::ok;
|
||||
}),
|
||||
ztu::make_line_parser("map_Kd ", ztu::is_not_repeating, [&](const auto& param)
|
||||
{
|
||||
if (textures_disabled) return codes::ok;
|
||||
|
||||
load_texture(param, "diffuse color", [&](const auto id) {
|
||||
material.diffuse_color_texture_id() = id;
|
||||
material.components() |= flags::diffuse_filter_texture;
|
||||
});
|
||||
|
||||
return codes::ok;
|
||||
}),
|
||||
ztu::make_line_parser("map_Ks ", ztu::is_not_repeating, [&](const auto& param)
|
||||
{
|
||||
if (textures_disabled) return codes::ok;
|
||||
|
||||
load_texture(param, "specular color", [&](const auto id) {
|
||||
material.specular_color_texture_id() = id;
|
||||
material.components() |= flags::specular_filter_texture;
|
||||
});
|
||||
|
||||
return codes::ok;
|
||||
}),
|
||||
ztu::make_line_parser("map_Ns ", ztu::is_not_repeating, [&](const auto& param)
|
||||
{
|
||||
if (textures_disabled) return codes::ok;
|
||||
|
||||
load_texture(param, "shininess", [&](const auto id) {
|
||||
material.shininess_texture_id() = id;
|
||||
material.components() |= flags::shininess_texture;
|
||||
});
|
||||
|
||||
return codes::ok;
|
||||
}),
|
||||
ztu::make_line_parser("map_d ", ztu::is_not_repeating, [&](const auto& param)
|
||||
{
|
||||
if (textures_disabled) return codes::ok;
|
||||
|
||||
load_texture(param, "alpha", [&](const auto id) {
|
||||
material.alpha_texture_id() = id;
|
||||
material.components() |= flags::alpha_texture;
|
||||
});
|
||||
|
||||
return codes::ok;
|
||||
}),
|
||||
ztu::make_line_parser("bump ", ztu::is_not_repeating, [&](const auto& param)
|
||||
{
|
||||
if (textures_disabled) return codes::ok;
|
||||
|
||||
load_texture(param, "bump", [&](const auto id) {
|
||||
material.bump_texture_id() = id;
|
||||
material.components() |= flags::bump_texture;
|
||||
});
|
||||
|
||||
return codes::ok;
|
||||
})
|
||||
);
|
||||
|
||||
if (ec != codes::ok)
|
||||
{
|
||||
return make_error_code(ec);
|
||||
}
|
||||
|
||||
push_material();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
450
source/assets/data_loaders/obj_loader.cpp
Executable file
450
source/assets/data_loaders/obj_loader.cpp
Executable file
@@ -0,0 +1,450 @@
|
||||
#include "assets/data_loaders/obj_loader.hpp"
|
||||
|
||||
#include <charconv>
|
||||
#include <fstream>
|
||||
#include <array>
|
||||
|
||||
#include "assets/components/mesh_vertex_components.hpp"
|
||||
#include "assets/dynamic_data_loaders/dynamic_material_loader.hpp"
|
||||
|
||||
#include "util/logger.hpp"
|
||||
#include "util/for_each.hpp"
|
||||
#include "util/uix.hpp"
|
||||
#include <set>
|
||||
|
||||
#include "util/line_parser.hpp"
|
||||
|
||||
namespace obj_loader_error {
|
||||
|
||||
struct category : std::error_category {
|
||||
[[nodiscard]] const char* name() const noexcept override {
|
||||
return "connector";
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string message(int ev) const override {
|
||||
switch (static_cast<codes>(ev)) {
|
||||
using enum codes;
|
||||
case obj_cannot_open_file:
|
||||
return "Cannot open given obj file.";
|
||||
case obj_malformed_vertex:
|
||||
return "File contains malformed 'v' statement.";
|
||||
case obj_malformed_texture_coordinate:
|
||||
return "File contains malformed 'vt' statement.";
|
||||
case obj_malformed_normal:
|
||||
return "File contains malformed 'vn' statement.";
|
||||
case obj_malformed_face:
|
||||
return "File contains malformed 'f' statement.";
|
||||
case obj_face_index_out_of_range:
|
||||
return "Face index out of range.";
|
||||
case obj_unknown_line_begin:
|
||||
return "Unknown obj line begin.";
|
||||
default:
|
||||
using namespace std::string_literals;
|
||||
return "unrecognized error ("s + std::to_string(ev) + ")";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mesh_loader_error
|
||||
|
||||
inline std::error_category& connector_error_category() {
|
||||
static obj_loader_error::category category;
|
||||
return category;
|
||||
}
|
||||
|
||||
namespace obj_loader_error {
|
||||
|
||||
inline std::error_code make_error_code(codes e) {
|
||||
return { static_cast<int>(e), connector_error_category() };
|
||||
}
|
||||
|
||||
} // namespace mesh_loader_error
|
||||
|
||||
|
||||
using vertex_type = std::array<dynamic_mesh_data::index_type, 3>;
|
||||
|
||||
struct indexed_vertex_type {
|
||||
vertex_type vertex;
|
||||
ztu::u32 buffer_index;
|
||||
|
||||
friend auto operator<=>(const indexed_vertex_type& a, const indexed_vertex_type& b) {
|
||||
return a.vertex <=> b.vertex;
|
||||
}
|
||||
|
||||
bool operator==(const indexed_vertex_type& other) const noexcept {
|
||||
return other.vertex == vertex;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// TODO add compile time selection and unrolling
|
||||
template<typename T, std::size_t Count>
|
||||
std::errc parse_numeric_vector(std::string_view param, std::array<T, Count>& values) {
|
||||
auto it = param.begin(), end = param.end();
|
||||
|
||||
for (auto& value : values)
|
||||
{
|
||||
if (it >= end)
|
||||
{
|
||||
return std::errc::invalid_argument;
|
||||
}
|
||||
|
||||
const auto [ptr, ec] = std::from_chars(it, end, value);
|
||||
|
||||
if (ec != std::errc{})
|
||||
{
|
||||
return ec;
|
||||
}
|
||||
|
||||
it = ptr + 1; // skip space in between components
|
||||
}
|
||||
|
||||
return {};
|
||||
};
|
||||
|
||||
std::error_code obj_loader::load_directory(
|
||||
dynamic_data_loader_ctx& ctx,
|
||||
dynamic_mesh_store& store,
|
||||
components::mesh_vertex::flags enabled_components,
|
||||
const std::filesystem::path& path,
|
||||
const bool pedantic
|
||||
) {
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
if (not fs::exists(path))
|
||||
{
|
||||
return make_error_code(std::errc::no_such_file_or_directory);
|
||||
}
|
||||
|
||||
for (const auto& file : fs::directory_iterator{ path })
|
||||
{
|
||||
const auto& file_path = file.path();
|
||||
|
||||
if (file_path.extension() != ".obj")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (const auto e = load(
|
||||
ctx,
|
||||
store,
|
||||
enabled_components,
|
||||
path,
|
||||
pedantic
|
||||
)) {
|
||||
ztu::logger::error(
|
||||
"Error while loading obj file '%': [%] %",
|
||||
file_path,
|
||||
e.category().name(),
|
||||
e.message()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
// TODO refactor so there is a function like parse_normals etc.
|
||||
|
||||
std::error_code obj_loader::load(
|
||||
dynamic_data_loader_ctx& ctx,
|
||||
dynamic_mesh_store& store,
|
||||
components::mesh_vertex::flags enabled_components,
|
||||
const std::filesystem::path& filename,
|
||||
const bool pedantic
|
||||
) {
|
||||
using obj_loader_error::codes;
|
||||
using obj_loader_error::make_error_code;
|
||||
|
||||
auto in = std::ifstream{ filename };
|
||||
if (not in.is_open()) {
|
||||
return make_error_code(codes::obj_cannot_open_file);
|
||||
}
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
const auto directory = fs::path(filename).parent_path();
|
||||
|
||||
// Each vertex of a face can represent a unique combination of vertex-/texture-/normal-coordinates.
|
||||
// But some combinations may occur more than once, for example on every corner of a cube 3 triangles will
|
||||
// reference the exact same corner vertex.
|
||||
// To get the best rendering performance and lowest final memory footprint these duplicates
|
||||
// need to be removed. So this std::set lookup is used to identify the aforementioned duplicates
|
||||
// and only push unique combinations to the buffers.
|
||||
std::set<indexed_vertex_type> vertex_ids;
|
||||
|
||||
auto mesh = dynamic_mesh_data{};
|
||||
|
||||
// Buffers
|
||||
auto position_buffer = mesh.positions();
|
||||
auto normal_buffer = mesh.normals();
|
||||
auto tex_coord_buffer = mesh.tex_coords();
|
||||
|
||||
std::unordered_map<std::string, ztu::u32> material_name_lookup;
|
||||
|
||||
|
||||
constexpr auto mtl_loader_id = *ctx.material_loader.find_loader_static("mtl");
|
||||
mtl_loader& material_loader = ctx.material_loader.get_loader<mtl_loader_id>();
|
||||
|
||||
material_loader.clear_name_lookup();
|
||||
|
||||
std::string material_name;
|
||||
|
||||
const auto push_mesh = [&](const bool clear_buffers = false) {
|
||||
|
||||
if (not mesh.positions().empty())
|
||||
{
|
||||
// Copy buffers instead of moving to keep capacity for further parsing
|
||||
// and have the final buffers be shrunk to size.
|
||||
if (not material_name.empty()) {
|
||||
if (const auto id = material_loader.find_id(material_name))
|
||||
{
|
||||
mesh.material_id() = *id;
|
||||
}
|
||||
else
|
||||
{
|
||||
ztu::logger::warn(
|
||||
"Could not find material '%'.",
|
||||
material_name
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ztu::logger::debug("Parsed % positions.", mesh.positions().size());
|
||||
ztu::logger::debug("Parsed % normals.", mesh.normals().size());
|
||||
ztu::logger::debug("Parsed % tex_coords.", mesh.tex_coords().size());
|
||||
|
||||
if (not mesh.positions().empty())
|
||||
{
|
||||
mesh.components() |= components::mesh_vertex::flags::position;
|
||||
}
|
||||
|
||||
if (not mesh.normals().empty())
|
||||
{
|
||||
mesh.components() |= components::mesh_vertex::flags::normal;
|
||||
}
|
||||
|
||||
if (not mesh.tex_coords().empty())
|
||||
{
|
||||
mesh.components() |= components::mesh_vertex::flags::tex_coord;
|
||||
}
|
||||
|
||||
ztu::logger::debug("Pushing obj mesh with % triangles.", mesh.triangles().size());
|
||||
|
||||
store.add(std::move(mesh));
|
||||
}
|
||||
|
||||
if (clear_buffers)
|
||||
{
|
||||
position_buffer.clear();
|
||||
normal_buffer.clear();
|
||||
tex_coord_buffer.clear();
|
||||
}
|
||||
|
||||
mesh = dynamic_mesh_data{};
|
||||
|
||||
vertex_ids.clear();
|
||||
material_name.clear();
|
||||
};
|
||||
|
||||
const auto find_or_push_vertex = [&](const vertex_type& vertex) -> ztu::u32 {
|
||||
|
||||
auto indexed_vid = indexed_vertex_type{
|
||||
.vertex = vertex,
|
||||
.buffer_index = static_cast<ztu::u32>(mesh.positions().size())
|
||||
};
|
||||
|
||||
// Search through sorted lookup to check if index combination is unique
|
||||
const auto [ id_it, unique ] = vertex_ids.insert(indexed_vid);
|
||||
|
||||
if (unique)
|
||||
{
|
||||
const auto& [ position_index, tex_coord_index, normal_index ] = vertex;
|
||||
|
||||
if (position_index < position_buffer.size())
|
||||
{
|
||||
mesh.positions().emplace_back(position_buffer[position_index]);
|
||||
}
|
||||
|
||||
if (normal_index < normal_buffer.size())
|
||||
{
|
||||
mesh.normals().emplace_back(normal_buffer[normal_index]);
|
||||
}
|
||||
|
||||
if (tex_coord_index < tex_coord_buffer.size())
|
||||
{
|
||||
mesh.tex_coords().emplace_back(tex_coord_buffer[tex_coord_index]);
|
||||
}
|
||||
}
|
||||
|
||||
return id_it->buffer_index;
|
||||
};
|
||||
|
||||
using flags = components::mesh_vertex::flags;
|
||||
|
||||
const auto component_disabled = [&](const flags component) {
|
||||
return (enabled_components & component) == flags::none;
|
||||
};
|
||||
|
||||
const auto positions_disabled = component_disabled(flags::position);
|
||||
const auto normals_disabled = component_disabled(flags::normal);
|
||||
const auto tex_coords_disabled = component_disabled(flags::tex_coord);
|
||||
|
||||
const auto ec = ztu::parse_lines<codes>(
|
||||
in,
|
||||
pedantic,
|
||||
ztu::make_line_parser("v ", ztu::is_repeating, [&](const auto& param)
|
||||
{
|
||||
if (positions_disabled) return codes::ok;
|
||||
|
||||
components::mesh_vertex::position position;
|
||||
if (parse_numeric_vector(param, position) != std::errc{}) [[unlikely]]
|
||||
{
|
||||
return codes::obj_malformed_vertex;
|
||||
}
|
||||
|
||||
position_buffer.push_back(position);
|
||||
|
||||
return codes::ok;
|
||||
}),
|
||||
ztu::make_line_parser("vt ", ztu::is_repeating, [&](const auto& param) {
|
||||
if (tex_coords_disabled) return codes::ok;
|
||||
|
||||
components::mesh_vertex::tex_coord coord;
|
||||
if (parse_numeric_vector(param, coord) != std::errc{}) [[unlikely]]
|
||||
{
|
||||
return codes::obj_malformed_texture_coordinate;
|
||||
}
|
||||
|
||||
tex_coord_buffer.push_back(coord);
|
||||
|
||||
return codes::ok;
|
||||
}),
|
||||
ztu::make_line_parser("vn ", ztu::is_repeating, [&](const auto& param)
|
||||
{
|
||||
if (normals_disabled) return codes::ok;
|
||||
|
||||
components::mesh_vertex::normal normal;
|
||||
if (parse_numeric_vector(param, normal) != std::errc{}) [[unlikely]]
|
||||
{
|
||||
return codes::obj_malformed_normal;
|
||||
}
|
||||
|
||||
normal_buffer.push_back(normal);
|
||||
|
||||
return codes::ok;
|
||||
}),
|
||||
ztu::make_line_parser("o ", ztu::is_not_repeating, [&](const auto&)
|
||||
{
|
||||
push_mesh(); // Name is currently ignored
|
||||
return codes::ok;
|
||||
}),
|
||||
ztu::make_line_parser("f ", ztu::is_repeating, [&](const auto& param)
|
||||
{
|
||||
const auto begin = param.begin().base();
|
||||
const auto end = param.end().base();
|
||||
|
||||
auto vertex = vertex_type{};
|
||||
|
||||
ztu::u32 first_index{}, prev_index{};
|
||||
|
||||
auto vertex_count = std::size_t{};
|
||||
|
||||
for (auto it = begin; it <= end; ++it)
|
||||
{
|
||||
|
||||
for (auto& component_index : vertex)
|
||||
{
|
||||
if (it != end and *it == '/')
|
||||
{
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto [ptr, ec] = std::from_chars(it, end, component_index);
|
||||
if (ec != std::errc()) [[unlikely]]
|
||||
{
|
||||
// Discard whole face if one index is malformed
|
||||
return codes::obj_malformed_face;
|
||||
}
|
||||
|
||||
--component_index; // Indices start at one
|
||||
it = ptr;
|
||||
|
||||
if (it == end or *it != '/')
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
++it;
|
||||
}
|
||||
|
||||
++vertex_count;
|
||||
|
||||
if (it != end and *it != ' ') [[unlikely]]
|
||||
{
|
||||
return codes::obj_malformed_face;
|
||||
}
|
||||
|
||||
const auto curr_index = find_or_push_vertex(vertex);
|
||||
|
||||
if (vertex_count >= 3)
|
||||
{
|
||||
auto& triangle = mesh.triangles().emplace_back();
|
||||
triangle[0] = first_index;
|
||||
triangle[1] = prev_index;
|
||||
triangle[2] = curr_index;
|
||||
}
|
||||
else if (vertex_count == 1)
|
||||
{
|
||||
first_index = curr_index;
|
||||
}
|
||||
|
||||
prev_index = curr_index;
|
||||
}
|
||||
|
||||
return codes::ok;
|
||||
}),
|
||||
ztu::make_line_parser("usemtl ", ztu::is_not_repeating, [&](const auto& param)
|
||||
{
|
||||
push_mesh(false);
|
||||
|
||||
material_name = param;
|
||||
|
||||
return codes::ok;
|
||||
}),
|
||||
ztu::make_line_parser("mtllib ", ztu::is_not_repeating, [&](const auto& param)
|
||||
{
|
||||
auto material_filename = fs::path(param);
|
||||
if (material_filename.is_relative())
|
||||
{
|
||||
material_filename = directory / material_filename;
|
||||
}
|
||||
|
||||
if (const auto error = ctx.material_loader.read(
|
||||
ctx,
|
||||
mtl_loader_id,
|
||||
material_filename,
|
||||
pedantic
|
||||
)) {
|
||||
ztu::logger::warn(
|
||||
"Error occurred while loading mtl files '%': [%] %",
|
||||
material_filename,
|
||||
error.category().name(),
|
||||
error.message()
|
||||
);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
material_loader.clear_name_lookup();
|
||||
|
||||
if (ec != codes::ok)
|
||||
{
|
||||
return make_error_code(ec);
|
||||
}
|
||||
|
||||
push_mesh();
|
||||
|
||||
return {};
|
||||
}
|
||||
253
source/assets/data_loaders/stl_loader.cpp
Normal file
253
source/assets/data_loaders/stl_loader.cpp
Normal file
@@ -0,0 +1,253 @@
|
||||
#include "assets/data_loaders/stl_loader.hpp"
|
||||
|
||||
#include "util/binary_ifstream.hpp"
|
||||
#include "util/unroll_bool_template.hpp"
|
||||
#include "util/logger.hpp"
|
||||
|
||||
template<bool Normals>
|
||||
std::error_code read_body(
|
||||
binary_ifstream& in,
|
||||
const std::uint32_t expected_triangle_count,
|
||||
std::vector<components::mesh_vertex::position>& positions,
|
||||
std::vector<components::mesh_vertex::normal>& normals,
|
||||
std::vector<std::array<ztu::u32, 3>>& triangles
|
||||
) {
|
||||
|
||||
const auto read_vector = [&in](auto& vector) -> std::error_code
|
||||
{
|
||||
for (auto& component : vector)
|
||||
{
|
||||
float component32;
|
||||
if (const auto e = in.read_ieee754<std::endian::little>(component32))
|
||||
{
|
||||
return e;
|
||||
}
|
||||
component = component32;
|
||||
}
|
||||
return {};
|
||||
};
|
||||
|
||||
for (std::uint32_t i{}; i != expected_triangle_count; ++i) {
|
||||
|
||||
auto normal = components::mesh_vertex::normal{};
|
||||
if constexpr (Normals)
|
||||
{
|
||||
if (const auto e = read_vector(normal))
|
||||
{
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
auto triangle = std::array<ztu::u32, 3>{};
|
||||
|
||||
for (auto& index : triangle) {
|
||||
|
||||
auto position = components::mesh_vertex::position{};
|
||||
if (const auto e = read_vector(position))
|
||||
{
|
||||
return e;
|
||||
}
|
||||
|
||||
// TODO implement unique insert correctly
|
||||
/*// Insert vertices sorted, to efficiently remove duplicates.
|
||||
const auto it = std::ranges::upper_bound(positions, position);
|
||||
|
||||
// Set index before `it` is invalidated by insert.
|
||||
index = it - positions.begin();
|
||||
|
||||
if (it != positions.begin() and *std::prev(it) == position)
|
||||
{
|
||||
--index;
|
||||
}
|
||||
else
|
||||
{
|
||||
positions.insert(it, position);
|
||||
if constexpr (Normals)
|
||||
{
|
||||
normals.insert(normals.begin() + index, normal);
|
||||
}
|
||||
}*/
|
||||
|
||||
index = positions.size();
|
||||
positions.push_back(position);
|
||||
|
||||
if constexpr (Normals)
|
||||
{
|
||||
normals.push_back(normal);
|
||||
}
|
||||
}
|
||||
|
||||
triangles.push_back(triangle);
|
||||
|
||||
// Skip attribute bytes
|
||||
if (const auto e = in.skip<std::uint16_t>())
|
||||
{
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::error_code stl_loader::read_directory(
|
||||
const std::filesystem::path& path,
|
||||
std::vector<dynamic_mesh_data>& meshes,
|
||||
components::mesh_vertex::flags enabled_components::mesh_vertexs,
|
||||
std::vector<dynamic_material_data>& materials,
|
||||
material_component::flags enabled_material_components,
|
||||
const ztu::u32 base_material_id,
|
||||
bool pedantic
|
||||
) {
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
if (not fs::exists(path)) {
|
||||
return make_error_code(std::errc::no_such_file_or_directory);
|
||||
}
|
||||
|
||||
for (const auto& file : fs::directory_iterator{ path / "frames" })
|
||||
{
|
||||
const auto& file_path = file.path();
|
||||
|
||||
if (file_path.extension() != ".stl")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (const auto e = read(
|
||||
file_path,
|
||||
meshes,
|
||||
enabled_components::mesh_vertexs,
|
||||
materials,
|
||||
enabled_material_components,
|
||||
base_material_id,
|
||||
pedantic
|
||||
)) {
|
||||
ztu::logger::error(
|
||||
"Error while loading stl file '%': [%] %",
|
||||
file_path,
|
||||
e.category().name(),
|
||||
e.message()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
std::error_code stl_loader::read(
|
||||
const std::filesystem::path& filename,
|
||||
std::vector<dynamic_mesh_data>& meshes,
|
||||
components::mesh_vertex::flags enabled_components::mesh_vertexs,
|
||||
std::vector<dynamic_material_data>&,
|
||||
material_component::flags,
|
||||
ztu::u32,
|
||||
const bool pedantic
|
||||
) {
|
||||
auto error = std::error_code{};
|
||||
|
||||
auto in = binary_ifstream{};
|
||||
|
||||
if ((error = in.open(filename, true)))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
auto header_bytes_left = static_cast<binary_ifstream::size_type>(80);
|
||||
|
||||
if (pedantic)
|
||||
{
|
||||
// Check if ASCII file was provided, these start with a specific character sequence.
|
||||
|
||||
static constexpr auto ascii_magic_string = std::string_view("solid");
|
||||
|
||||
auto magic_bytes = std::array<binary_ifstream::char_type, ascii_magic_string.size()>{};
|
||||
|
||||
if ((error = in.read(magic_bytes)))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
const auto magic_string = std::string_view(
|
||||
reinterpret_cast<const char*>(magic_bytes.data()),
|
||||
magic_bytes.size()
|
||||
);
|
||||
|
||||
if (magic_string == ascii_magic_string)
|
||||
{
|
||||
return std::make_error_code(std::errc::illegal_byte_sequence);
|
||||
}
|
||||
|
||||
header_bytes_left -= ascii_magic_string.size();
|
||||
}
|
||||
|
||||
// Ignore (rest of) header.
|
||||
if ((error = in.skip(header_bytes_left)))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
// Read number of bytes
|
||||
auto expected_triangle_count = std::uint32_t{};
|
||||
|
||||
if ((error = in.read<std::endian::little>(expected_triangle_count)))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
// Use separate mesh for parsing, so original mesh is only overwritten
|
||||
// if no errors occurred. This also guarantees unused reserved memory
|
||||
// is freed immediately in case of an error.
|
||||
auto mesh = dynamic_mesh_data{};
|
||||
|
||||
auto& positions = mesh.positions();
|
||||
auto& normals = mesh.normals();
|
||||
auto& triangles = mesh.triangles();
|
||||
auto& material_id = mesh.material_id();
|
||||
|
||||
material_id = 0; // Set to default material
|
||||
|
||||
positions.reserve(expected_triangle_count * 3);
|
||||
normals.reserve(expected_triangle_count);
|
||||
triangles.reserve(expected_triangle_count);
|
||||
|
||||
const auto normals_enabled = (
|
||||
(enabled_components::mesh_vertexs & components::mesh_vertex::flags::normal) != components::mesh_vertex::flags::none
|
||||
);
|
||||
|
||||
error = unroll_bool_function_template([&]<bool Normals>() {
|
||||
return read_body<Normals>(
|
||||
in,expected_triangle_count,
|
||||
positions,
|
||||
normals,
|
||||
triangles
|
||||
);
|
||||
}, normals_enabled);
|
||||
|
||||
// Free any unused reserved memory
|
||||
positions.shrink_to_fit();
|
||||
normals.shrink_to_fit();
|
||||
triangles.shrink_to_fit();
|
||||
|
||||
if (error)
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
ztu::logger::debug("Normal count: %", normals.size());
|
||||
|
||||
if (not positions.empty())
|
||||
{
|
||||
mesh.components() |= components::mesh_vertex::flags::position;
|
||||
}
|
||||
|
||||
if (not normals.empty())
|
||||
{
|
||||
ztu::logger::debug("Enabling normals!!!");
|
||||
mesh.components() |= components::mesh_vertex::flags::normal;
|
||||
}
|
||||
|
||||
meshes.emplace_back(std::move(mesh));
|
||||
|
||||
return {};
|
||||
}
|
||||
136
source/assets/data_loaders/threedtk_pose_loader.cpp
Normal file
136
source/assets/data_loaders/threedtk_pose_loader.cpp
Normal file
@@ -0,0 +1,136 @@
|
||||
#include "assets/data_loaders/threedtk_pose_loader.hpp"
|
||||
|
||||
#include "assets/dynamic_read_buffers/dynamic_pose_buffer.hpp"
|
||||
|
||||
#include <fstream>
|
||||
#include <glm/ext/matrix_transform.hpp>
|
||||
#include <glm/gtx/euler_angles.hpp>
|
||||
|
||||
#include "util/logger.hpp"
|
||||
|
||||
|
||||
|
||||
inline std::error_code threedtk_pose_loader::parse_transform_info(
|
||||
std::ifstream& in,
|
||||
std::string& line,
|
||||
std::array<glm::vec3, 2>& transform_info
|
||||
) {
|
||||
|
||||
for (std::size_t row{}; row != 2; ++row) {
|
||||
|
||||
std::getline(in, line);
|
||||
|
||||
auto it = line.cbegin().base();
|
||||
auto end = line.cend().base();
|
||||
|
||||
for (glm::vec3::length_type col{}; col != 3; ++col) {
|
||||
|
||||
const auto [ ptr, ec ] = std::from_chars(
|
||||
it, end,
|
||||
transform_info[row][col],
|
||||
std::chars_format::general
|
||||
);
|
||||
|
||||
if (ec != std::errc{}) {
|
||||
return std::make_error_code(ec);
|
||||
}
|
||||
|
||||
it = ptr + 1; // skip space in between components
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline ztu::result<pose_prefetch_lookup::index_type> threedtk_pose_loader::parse_index(
|
||||
const std::string_view filename
|
||||
) {
|
||||
static constexpr auto prefix = std::string_view{ "scan" };
|
||||
|
||||
auto name_view = filename.substr(0, name_view.find('.'));
|
||||
|
||||
if (name_view.length() <= prefix.length()) [[unlikely]]
|
||||
{
|
||||
return std::make_error_code(std::errc::invalid_argument);
|
||||
}
|
||||
|
||||
name_view = name_view.substr(prefix.length());
|
||||
|
||||
pose_prefetch_lookup::index_type index;
|
||||
|
||||
const auto res = std::from_chars(name_view.begin(), name_view.end(), index);
|
||||
|
||||
if (res.ec != std::errc{}) [[unlikely]]
|
||||
{
|
||||
return std::make_error_code(res.ec);
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
|
||||
void threedtk_pose_loader::load(
|
||||
const ztu::string_list& filenames,
|
||||
dynamic_pose_store& store,
|
||||
pose_prefetch_lookup& id_lookup
|
||||
) {
|
||||
|
||||
auto filename_buffer = std::string{};
|
||||
auto in = std::ifstream{};
|
||||
auto line = std::string{};
|
||||
auto pose_buffer = dynamic_pose_buffer{};
|
||||
|
||||
for (const auto filename : filenames)
|
||||
{
|
||||
|
||||
pose_prefetch_lookup::index_type index;
|
||||
if (const auto res = parse_index(filename))
|
||||
{
|
||||
index = *res;
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto error = res.error();
|
||||
ztu::logger::error(
|
||||
"Error while parsing 3dtk pose file index %: [%] %",
|
||||
filename,
|
||||
error.category().name(),
|
||||
error.message()
|
||||
);
|
||||
}
|
||||
|
||||
filename_buffer = filename;
|
||||
in.open(filename_buffer.c_str());
|
||||
if (not in.is_open()) {
|
||||
ztu::logger::error("Cannot open 3dtk pose file %", filename);
|
||||
continue;
|
||||
}
|
||||
|
||||
std::array<glm::vec3, 2> transform_info{};
|
||||
|
||||
const auto error = parse_transform_info(in, line, transform_info);
|
||||
|
||||
in.close();
|
||||
|
||||
if (error)
|
||||
{
|
||||
ztu::logger::error(
|
||||
"Error while parsing 3dtk pose file %: [%] %",
|
||||
filename,
|
||||
error.category().name(),
|
||||
error.message()
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& translation = transform_info[0];
|
||||
auto& angles = transform_info[1];
|
||||
angles *= static_cast<float>(M_PI / 180.0);
|
||||
|
||||
pose_buffer = (
|
||||
glm::translate(glm::identity<glm::mat4>(), translation) *
|
||||
glm::eulerAngleXYZ(angles[0], angles[1], angles[2])
|
||||
);
|
||||
|
||||
const auto id = store.add(pose_buffer);
|
||||
id_lookup.emplace(filename, index, id);
|
||||
}
|
||||
}
|
||||
41
source/assets/dynamic_data_loaders/dynamic_mesh_loader.cpp
Normal file
41
source/assets/dynamic_data_loaders/dynamic_mesh_loader.cpp
Normal 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
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
122
source/assets/dynamic_data_loaders/dynamic_texture_loader.cpp
Normal file
122
source/assets/dynamic_data_loaders/dynamic_texture_loader.cpp
Normal 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 {};
|
||||
}
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
26
source/assets/dynamic_data_stores/dynamic_material_store.cpp
Normal file
26
source/assets/dynamic_data_stores/dynamic_material_store.cpp
Normal 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();
|
||||
}
|
||||
31
source/assets/dynamic_data_stores/dynamic_mesh_store.cpp
Normal file
31
source/assets/dynamic_data_stores/dynamic_mesh_store.cpp
Normal 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();
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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() };
|
||||
}
|
||||
@@ -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() };
|
||||
}
|
||||
@@ -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() };
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
73
source/assets/dynamic_read_buffers/dynamic_mesh_buffer.ipp
Normal file
73
source/assets/dynamic_read_buffers/dynamic_mesh_buffer.ipp
Normal 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;
|
||||
}
|
||||
148
source/assets/dynamic_read_buffers/dynamic_model_buffer.ipp
Normal file
148
source/assets/dynamic_read_buffers/dynamic_model_buffer.ipp
Normal 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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
151
source/assets/dynamic_read_buffers/dynamic_texture_buffer.ipp
Normal file
151
source/assets/dynamic_read_buffers/dynamic_texture_buffer.ipp
Normal 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());
|
||||
}
|
||||
139
source/assets/prefetch_lookups/pose_prefetch_lookup.cpp
Normal file
139
source/assets/prefetch_lookups/pose_prefetch_lookup.cpp
Normal 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);
|
||||
}
|
||||
82
source/geometry/normal_estimation.cpp
Normal file
82
source/geometry/normal_estimation.cpp
Normal file
@@ -0,0 +1,82 @@
|
||||
#include "geometry/normal_estimation.hpp"
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <algorithm>
|
||||
|
||||
void estimate_normals(
|
||||
std::span<const components::mesh_vertex::position> vertices,
|
||||
std::span<const std::array<ztu::u32, 3>> triangles,
|
||||
std::vector<components::mesh_vertex::normal>& normals
|
||||
) {
|
||||
|
||||
normals.resize(vertices.size());
|
||||
|
||||
std::ranges::fill(normals, std::array{ 0.0f, 0.0f, 0.0f });
|
||||
|
||||
for (const auto& triangle : triangles)
|
||||
{
|
||||
auto abc = std::array<glm::vec3, 3>{};
|
||||
|
||||
std::ranges::transform(
|
||||
triangle,
|
||||
abc.begin(),
|
||||
[&](const auto& index)
|
||||
{
|
||||
const auto& [ x, y, z ] = vertices[index];
|
||||
return glm::vec3{ x, y, z };
|
||||
}
|
||||
);
|
||||
|
||||
const auto [ A, B, C ] = abc;
|
||||
|
||||
// TODO normalization can be done more efficiently
|
||||
const auto normal = glm::normalize(glm::vec3{
|
||||
A.y * B.z - A.z * B.y,
|
||||
A.z * B.x - A.x * B.z,
|
||||
A.x * B.y - A.y * B.x
|
||||
});
|
||||
|
||||
const auto a_length = glm::length(B - C);
|
||||
const auto b_length = glm::length(C - A);
|
||||
const auto c_length = glm::length(A - B);
|
||||
|
||||
const auto area = 0.25f * std::sqrt(
|
||||
(a_length + b_length + c_length) *
|
||||
(-a_length + b_length + c_length) *
|
||||
(a_length - b_length + c_length) *
|
||||
(a_length + b_length - c_length)
|
||||
);
|
||||
|
||||
const auto weighted_normal = normal * area;
|
||||
|
||||
for (const auto& index : triangle)
|
||||
{
|
||||
auto& normal_avg = normals[index];
|
||||
for (int i{}; i != 3; ++i)
|
||||
{
|
||||
normal_avg[i] += weighted_normal[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
using normal_component_type = components::mesh_vertex::normal::component_type;
|
||||
constexpr auto epsilon = std::numeric_limits<normal_component_type>::epsilon();
|
||||
|
||||
for (auto& [ x, y, z ] : normals)
|
||||
{
|
||||
const auto length = glm::length(glm::vec3{ x, y, z });
|
||||
if (length <= epsilon)
|
||||
{
|
||||
x = 0;
|
||||
y = 1;
|
||||
z = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto scale = 1.0f / length;
|
||||
x *= scale;
|
||||
y *= scale;
|
||||
z *= scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
129
source/opengl/data/material_data.ipp
Normal file
129
source/opengl/data/material_data.ipp
Normal file
@@ -0,0 +1,129 @@
|
||||
#ifndef INCLUDE_MATERIAL_DATA_IMPLEMENTATION
|
||||
# error Never include this file directly include 'material_data.hpp'
|
||||
#endif
|
||||
|
||||
namespace zgl
|
||||
{
|
||||
inline material_data::material_data(
|
||||
const std::optional<texture_handle>& texture_handle,
|
||||
const std::optional<surface_properties_handle>& surface_properties_handle,
|
||||
const std::optional<alpha_handle>& alpha_handle,
|
||||
std::optional<texture_data>&& texture_data,
|
||||
const material_component::flags components
|
||||
) :
|
||||
m_handle{
|
||||
.texture = texture_handle,
|
||||
.surface_properties = surface_properties_handle,
|
||||
.alpha = alpha_handle
|
||||
},
|
||||
m_texture_data{ std::move(texture_data) },
|
||||
m_component_types{ components } {}
|
||||
|
||||
inline material_data::material_data(material_data&& other) noexcept
|
||||
{
|
||||
m_handle = other.m_handle;
|
||||
m_texture_data = std::move(other.m_texture_data);
|
||||
m_component_types = other.m_component_types;
|
||||
|
||||
other.m_handle.texture = std::nullopt;
|
||||
other.m_handle.surface_properties = std::nullopt;
|
||||
other.m_handle.alpha = std::nullopt;
|
||||
other.m_component_types = material_component::flags::none;
|
||||
}
|
||||
|
||||
inline material_data& material_data::operator=(material_data&& other) noexcept
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
this->~material_data();
|
||||
|
||||
m_handle = other.m_handle;
|
||||
m_texture_data = std::move(other.m_texture_data);
|
||||
m_component_types = other.m_component_types;
|
||||
|
||||
other.m_handle.texture = std::nullopt;
|
||||
other.m_handle.surface_properties = std::nullopt;
|
||||
other.m_handle.alpha = std::nullopt;
|
||||
other.m_component_types = material_component::flags::none;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
inline material_handle material_data::handle() const
|
||||
{
|
||||
return m_handle;
|
||||
}
|
||||
|
||||
inline material_component::flags material_data::components() const
|
||||
{
|
||||
return m_component_types;
|
||||
}
|
||||
|
||||
|
||||
inline std::error_code material_data::build_from(
|
||||
const std::optional<material_component::texture::value_type>& texture_opt,
|
||||
const std::optional<material_component::surface_properties::value_type>& surface_properties_opt,
|
||||
const std::optional<material_component::transparency::value_type>& transparency_opt,
|
||||
const material_component::flags components,
|
||||
material_data& dst_data
|
||||
) {
|
||||
|
||||
auto texture_data_opt = std::optional<texture_data>{ std::nullopt };
|
||||
auto texture_handle_opt = std::optional<texture_handle>{ std::nullopt };
|
||||
if (texture_opt)
|
||||
{
|
||||
const auto& texture = *texture_opt;
|
||||
auto texture_data = zgl::texture_data{};
|
||||
|
||||
if (const auto e = texture_data::build_from(
|
||||
std::span(texture.cbegin(), texture.cend()),
|
||||
GL_RGBA, GL_UNSIGNED_BYTE,
|
||||
texture.width(),
|
||||
texture.height(),
|
||||
texture_data
|
||||
)) {
|
||||
return e;
|
||||
}
|
||||
|
||||
texture_handle_opt.emplace(texture_data.handle());
|
||||
texture_data_opt.emplace(std::move(texture_data));
|
||||
}
|
||||
|
||||
auto surface_properties_data_opt = std::optional<surface_properties_handle>{ std::nullopt };
|
||||
if (surface_properties_opt)
|
||||
{
|
||||
const auto& [ ambient, diffuse, specular, shininess ] = *surface_properties_opt;
|
||||
surface_properties_data_opt.emplace(
|
||||
glm::vec3{ ambient[0], ambient[1], ambient[2] },
|
||||
glm::vec3{ diffuse[0], diffuse[1], diffuse[2] },
|
||||
glm::vec3{ specular[0], specular[1], specular[2] },
|
||||
shininess
|
||||
);
|
||||
}
|
||||
|
||||
auto alpha_data_opt = std::optional<alpha_handle>{ std::nullopt };
|
||||
if (transparency_opt)
|
||||
{
|
||||
alpha_data_opt.emplace(1.0f - *transparency_opt);
|
||||
}
|
||||
|
||||
|
||||
/*dst_data = material_data{
|
||||
texture_handle_opt,
|
||||
surface_properties_data_opt,
|
||||
alpha_data_opt,
|
||||
std::move(texture_data_opt),
|
||||
components
|
||||
};*/
|
||||
|
||||
dst_data.m_handle.texture = texture_handle_opt;
|
||||
dst_data.m_handle.surface_properties = surface_properties_data_opt;
|
||||
dst_data.m_handle.alpha = alpha_data_opt;
|
||||
dst_data.m_texture_data = std::move(texture_data_opt);
|
||||
dst_data.m_component_types = components;
|
||||
|
||||
return {};
|
||||
}
|
||||
}
|
||||
119
source/opengl/data/mesh_data.cpp
Normal file
119
source/opengl/data/mesh_data.cpp
Normal file
@@ -0,0 +1,119 @@
|
||||
#include "opengl/data/mesh_data.hpp"
|
||||
|
||||
#include "opengl/type_utils.hpp"
|
||||
#include "opengl/error.hpp"
|
||||
|
||||
#include "GL/glew.h"
|
||||
#include <algorithm>
|
||||
#include "util/logger.hpp" // TODO remove
|
||||
|
||||
|
||||
std::error_code zgl::mesh_data::build_from(
|
||||
const std::span<const ztu::u8> vertex_buffer,
|
||||
const std::span<const GLenum> component_types,
|
||||
const std::span<const GLint> component_lengths,
|
||||
const GLsizei stride,
|
||||
const std::span<const ztu::u32> index_buffer,
|
||||
const ztu::u32 material_id,
|
||||
const components::mesh_vertex::flags components,
|
||||
mesh_data& data
|
||||
) {
|
||||
if (not std::ranges::all_of(component_types, type_utils::is_valid_type))
|
||||
{
|
||||
ztu::logger::debug("not all types valid.");
|
||||
return std::make_error_code(std::errc::invalid_argument);
|
||||
}
|
||||
|
||||
const auto vertices_byte_count = vertex_buffer.size() * sizeof(std::uint8_t);
|
||||
if (vertices_byte_count > std::numeric_limits<GLsizeiptr>::max())
|
||||
{
|
||||
return std::make_error_code(std::errc::value_too_large);
|
||||
}
|
||||
|
||||
const auto indices_byte_count = index_buffer.size() * sizeof(ztu::u32);
|
||||
if (indices_byte_count > std::numeric_limits<GLsizeiptr>::max())
|
||||
{
|
||||
return std::make_error_code(std::errc::value_too_large);
|
||||
}
|
||||
|
||||
auto error = std::error_code{};
|
||||
|
||||
auto check_error = [&error, ec = static_cast<GLenum>(GL_NO_ERROR)]() mutable -> const std::error_code&
|
||||
{
|
||||
ec = glGetError();
|
||||
if (ec != GL_NO_ERROR)
|
||||
{
|
||||
error = make_error_code(ec);
|
||||
}
|
||||
return error;
|
||||
};
|
||||
|
||||
GLuint vao_id;
|
||||
glGenVertexArrays(1, &vao_id);
|
||||
glBindVertexArray(vao_id);
|
||||
if (check_error()) return error;
|
||||
|
||||
ztu::logger::debug("Created mesh vao: % valid: %", vao_id, (bool)glIsVertexArray(vao_id));
|
||||
|
||||
GLuint vertex_buffer_id;
|
||||
glGenBuffers(1, &vertex_buffer_id);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_id);
|
||||
glBufferData(
|
||||
GL_ARRAY_BUFFER,
|
||||
static_cast<GLsizeiptr>(vertices_byte_count),
|
||||
vertex_buffer.data(),
|
||||
GL_STATIC_DRAW
|
||||
);
|
||||
if (check_error()) return error;
|
||||
|
||||
GLuint index_buffer_id;
|
||||
glGenBuffers(1, &index_buffer_id);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer_id);
|
||||
glBufferData(
|
||||
GL_ELEMENT_ARRAY_BUFFER,
|
||||
static_cast<GLsizeiptr>(indices_byte_count),
|
||||
index_buffer.data(),
|
||||
GL_STATIC_DRAW
|
||||
);
|
||||
if (check_error()) return error;
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_id);
|
||||
if (check_error()) return error;
|
||||
|
||||
auto offset = GLsizei{ 0 };
|
||||
|
||||
for (std::size_t i{}; i != component_types.size(); ++i)
|
||||
{
|
||||
const auto type = component_types[i];
|
||||
const auto length = component_lengths[i];
|
||||
const auto byte_count = type_utils::size_of(type);
|
||||
|
||||
glVertexAttribPointer(
|
||||
i,
|
||||
length,
|
||||
type,
|
||||
GL_FALSE,
|
||||
stride,
|
||||
reinterpret_cast<GLvoid*>(offset)
|
||||
);
|
||||
|
||||
glEnableVertexAttribArray(i);
|
||||
if (check_error()) return error;
|
||||
|
||||
offset += length * byte_count;
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
|
||||
data = mesh_data(
|
||||
vertex_buffer_id,
|
||||
index_buffer_id,
|
||||
vao_id,
|
||||
material_id,
|
||||
components,
|
||||
index_buffer.size()
|
||||
);
|
||||
|
||||
return {};
|
||||
}
|
||||
90
source/opengl/data/mesh_data.ipp
Normal file
90
source/opengl/data/mesh_data.ipp
Normal file
@@ -0,0 +1,90 @@
|
||||
#ifndef INCLUDE_MESH_DATA_IMPLEMENTATION
|
||||
# error Never include this file directly include 'mesh_data.hpp'
|
||||
#endif
|
||||
|
||||
namespace zgl
|
||||
{
|
||||
inline mesh_data::mesh_data(
|
||||
const GLuint vertex_vbo_id,
|
||||
const GLuint index_vbo_id,
|
||||
const GLuint vao_id,
|
||||
const ztu::u32 material_id,
|
||||
const components::mesh_vertex::flags components,
|
||||
const GLsizei index_count
|
||||
) :
|
||||
m_handle{
|
||||
.vao_id = vao_id,
|
||||
.index_count = index_count
|
||||
},
|
||||
m_vertex_vbo_id{ vertex_vbo_id },
|
||||
m_index_vbo_id{ index_vbo_id },
|
||||
m_material_id{ material_id },
|
||||
m_component_types{ components } {}
|
||||
|
||||
inline mesh_data& mesh_data::operator=(mesh_data&& other) noexcept
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
this->~mesh_data();
|
||||
|
||||
m_handle = other.m_handle;
|
||||
m_vertex_vbo_id = other.m_vertex_vbo_id;
|
||||
m_index_vbo_id = other.m_index_vbo_id;
|
||||
m_material_id = other.m_material_id;
|
||||
m_component_types = other.m_component_types;
|
||||
|
||||
other.m_handle.vao_id = 0;
|
||||
other.m_handle.index_count = 0;
|
||||
other.m_vertex_vbo_id = 0;
|
||||
other.m_index_vbo_id = 0;
|
||||
other.m_material_id = 0;
|
||||
other.m_component_types = components::mesh_vertex::flags::none;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline mesh_data::mesh_data(mesh_data&& other) noexcept :
|
||||
m_handle{ other.m_handle },
|
||||
m_vertex_vbo_id{ other.m_vertex_vbo_id },
|
||||
m_index_vbo_id{ other.m_index_vbo_id },
|
||||
m_material_id{ other.m_material_id },
|
||||
m_component_types{ other.m_component_types }
|
||||
{
|
||||
other.m_handle.vao_id = 0;
|
||||
other.m_handle.index_count = 0;
|
||||
other.m_vertex_vbo_id = 0;
|
||||
other.m_index_vbo_id = 0;
|
||||
other.m_material_id = 0;
|
||||
other.m_component_types = components::mesh_vertex::flags::none;
|
||||
}
|
||||
|
||||
inline mesh_data::~mesh_data() {
|
||||
if (m_vertex_vbo_id) {
|
||||
glDeleteBuffers(1, &m_vertex_vbo_id);
|
||||
}
|
||||
if (m_index_vbo_id) {
|
||||
glDeleteBuffers(1, &m_index_vbo_id);
|
||||
}
|
||||
if (m_handle.vao_id) {
|
||||
glDeleteVertexArrays(1, &m_handle.vao_id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline mesh_handle mesh_data::handle() const
|
||||
{
|
||||
return m_handle;
|
||||
}
|
||||
|
||||
inline components::mesh_vertex::flags mesh_data::components() const
|
||||
{
|
||||
return m_component_types;
|
||||
}
|
||||
|
||||
inline ztu::u32 mesh_data::material_id() const
|
||||
{
|
||||
return m_material_id;
|
||||
}
|
||||
|
||||
}
|
||||
99
source/opengl/data/point_cloud_data.cpp
Normal file
99
source/opengl/data/point_cloud_data.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
#include "opengl/data/point_cloud_data.hpp"
|
||||
|
||||
#include "opengl/type_utils.hpp"
|
||||
#include "opengl/error.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include "GL/glew.h"
|
||||
|
||||
std::error_code zgl::point_cloud_data::build_from(
|
||||
std::span<const std::uint8_t> point_buffer,
|
||||
std::span<const GLenum> component_types,
|
||||
std::span<const GLint> component_lengths,
|
||||
GLsizei stride,
|
||||
point_cloud_data& data
|
||||
) {
|
||||
if (not std::ranges::all_of(component_types, type_utils::is_valid_type))
|
||||
{
|
||||
return std::make_error_code(std::errc::invalid_argument);
|
||||
}
|
||||
|
||||
const auto points_byte_count = point_buffer.size() * sizeof(std::uint8_t);
|
||||
if (points_byte_count > std::numeric_limits<GLsizeiptr>::max())
|
||||
{
|
||||
return std::make_error_code(std::errc::value_too_large);
|
||||
}
|
||||
|
||||
auto error = std::error_code{};
|
||||
|
||||
auto check_error = [&error, ec = static_cast<GLenum>(GL_NO_ERROR)]() mutable -> const std::error_code&
|
||||
{
|
||||
ec = glGetError();
|
||||
if (ec != GL_NO_ERROR) {
|
||||
error = make_error_code(ec);
|
||||
}
|
||||
return error;
|
||||
};
|
||||
|
||||
ztu::u32 vao_id;
|
||||
glGenVertexArrays(1, &vao_id);
|
||||
if (check_error()) return error;
|
||||
|
||||
glBindVertexArray(vao_id);
|
||||
if (check_error()) return error;
|
||||
|
||||
ztu::u32 vertex_buffer_id;
|
||||
glGenBuffers(1, &vertex_buffer_id);
|
||||
if (check_error()) return error;
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_id);
|
||||
if (check_error()) return error;
|
||||
|
||||
glBufferData(
|
||||
GL_ARRAY_BUFFER,
|
||||
static_cast<GLsizeiptr>(points_byte_count),
|
||||
point_buffer.data(),
|
||||
GL_STATIC_DRAW
|
||||
);
|
||||
if (check_error()) return error;
|
||||
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_id);
|
||||
if (check_error()) return error;
|
||||
|
||||
auto offset = GLsizei{ 0 };
|
||||
|
||||
for (std::size_t i{}; i != component_types.size(); ++i)
|
||||
{
|
||||
const auto type = component_types[i];
|
||||
const auto length = component_lengths[i];
|
||||
const auto byte_count = type_utils::size_of(type);
|
||||
|
||||
glVertexAttribPointer(
|
||||
i,
|
||||
length,
|
||||
type,
|
||||
GL_FALSE,
|
||||
stride,
|
||||
reinterpret_cast<GLvoid*>(offset)
|
||||
);
|
||||
if (check_error()) return error;
|
||||
glEnableVertexAttribArray(i);
|
||||
if (check_error()) return error;
|
||||
|
||||
offset += length * byte_count;
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
if (check_error()) return error;
|
||||
glBindVertexArray(0);
|
||||
if (check_error()) return error;
|
||||
|
||||
data = point_cloud_data(
|
||||
vertex_buffer_id,
|
||||
vao_id,
|
||||
point_buffer.size()
|
||||
);
|
||||
|
||||
return {};
|
||||
}
|
||||
66
source/opengl/data/point_cloud_data.ipp
Normal file
66
source/opengl/data/point_cloud_data.ipp
Normal file
@@ -0,0 +1,66 @@
|
||||
#ifndef INCLUDE_POINT_CLOUD_DATA_IMPLEMENTATION
|
||||
# error Never include this file directly include 'point_cloud_data.hpp'
|
||||
#endif
|
||||
|
||||
|
||||
namespace zgl
|
||||
{
|
||||
inline point_cloud_data::point_cloud_data(
|
||||
const GLuint vertex_vbo_id,
|
||||
const GLuint vao_id,
|
||||
const GLsizei point_count
|
||||
) :
|
||||
m_handle{
|
||||
.vao_id = vao_id,
|
||||
.point_count = point_count
|
||||
},
|
||||
m_vertex_vbo_id{ vertex_vbo_id } {}
|
||||
|
||||
inline point_cloud_data::point_cloud_data(point_cloud_data&& other) noexcept :
|
||||
m_handle{ other.m_handle },
|
||||
m_vertex_vbo_id{ other.m_vertex_vbo_id }
|
||||
{
|
||||
other.m_handle.vao_id = 0;
|
||||
other.m_handle.point_count = 0;
|
||||
other.m_vertex_vbo_id = 0;
|
||||
}
|
||||
|
||||
inline point_cloud_data& point_cloud_data::operator=(point_cloud_data&& other) noexcept
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
this->~point_cloud_data();
|
||||
|
||||
m_handle = other.m_handle;
|
||||
m_vertex_vbo_id = other.m_vertex_vbo_id;
|
||||
|
||||
other.m_handle.vao_id = 0;
|
||||
other.m_handle.point_count = 0;
|
||||
other.m_vertex_vbo_id = 0;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline point_cloud_data::~point_cloud_data()
|
||||
{
|
||||
if (m_vertex_vbo_id)
|
||||
{
|
||||
glDeleteBuffers(1, &m_vertex_vbo_id);
|
||||
}
|
||||
if (m_handle.vao_id)
|
||||
{
|
||||
glDeleteVertexArrays(1, &m_handle.vao_id);
|
||||
}
|
||||
}
|
||||
|
||||
inline point_cloud_handle point_cloud_data::handle() const
|
||||
{
|
||||
return m_handle;
|
||||
}
|
||||
|
||||
inline components::point_cloud_vertex::flags point_cloud_data::components() const
|
||||
{
|
||||
return m_component_types;
|
||||
}
|
||||
}
|
||||
46
source/opengl/data/shader_data.cpp
Executable file
46
source/opengl/data/shader_data.cpp
Executable file
@@ -0,0 +1,46 @@
|
||||
#include "opengl/data/shader_data.hpp"
|
||||
|
||||
#include "GL/glew.h"
|
||||
#include "opengl/error.hpp"
|
||||
#include "util/logger.hpp"
|
||||
|
||||
namespace zgl
|
||||
{
|
||||
std::error_code shader_data::build_from(
|
||||
const GLenum type,
|
||||
const std::string& source,
|
||||
shader_data& data
|
||||
) {
|
||||
auto shader_id = GLuint{ 0 };
|
||||
|
||||
if (not source.empty())
|
||||
{
|
||||
shader_id = glCreateShader(type);
|
||||
|
||||
// Curious choice to take lists as parameters...
|
||||
const auto first_list_element = source.c_str();
|
||||
const auto first_list_element_len = static_cast<GLint>(source.length());
|
||||
glShaderSource(shader_id, 1, &first_list_element, &first_list_element_len);
|
||||
glCompileShader(shader_id);
|
||||
|
||||
GLint success;
|
||||
glGetShaderiv(shader_id, GL_COMPILE_STATUS, &success);
|
||||
if (not success)
|
||||
{
|
||||
GLint log_length{};
|
||||
glGetShaderiv(shader_id, GL_INFO_LOG_LENGTH, &log_length);
|
||||
|
||||
auto log = std::string(log_length, ' ');
|
||||
glGetShaderInfoLog(shader_id, log_length, nullptr, log.data());
|
||||
|
||||
ztu::logger::warn("Error while compiling shader:\n%", log);
|
||||
|
||||
return std::make_error_code(std::errc::invalid_argument);
|
||||
}
|
||||
}
|
||||
|
||||
data = shader_data{ shader_id, type };
|
||||
|
||||
return {};
|
||||
}
|
||||
}
|
||||
47
source/opengl/data/shader_data.ipp
Normal file
47
source/opengl/data/shader_data.ipp
Normal file
@@ -0,0 +1,47 @@
|
||||
#ifndef INCLUDE_SHADER_DATA_IMPLEMENTATION
|
||||
# error Never include this file directly include 'shader_data.hpp'
|
||||
#endif
|
||||
|
||||
namespace zgl
|
||||
{
|
||||
inline shader_data::shader_data(const GLuint shader_id, const GLenum type)
|
||||
: m_handle{ shader_id }, m_type{ type } {}
|
||||
|
||||
|
||||
inline shader_data::shader_data(shader_data&& other) noexcept
|
||||
{
|
||||
m_handle = other.m_handle;
|
||||
m_type = other.m_type;
|
||||
other.m_handle.shader_id = 0;
|
||||
other.m_type = GL_INVALID_ENUM;
|
||||
}
|
||||
|
||||
inline shader_data& shader_data::operator=(shader_data&& other) noexcept
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
this->~shader_data();
|
||||
|
||||
m_handle = other.m_handle;
|
||||
m_type = other.m_type;
|
||||
other.m_handle.shader_id = 0;
|
||||
other.m_type = GL_INVALID_ENUM;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline shader_data::~shader_data()
|
||||
{
|
||||
if (m_handle.shader_id)
|
||||
{
|
||||
glDeleteShader(m_handle.shader_id);
|
||||
}
|
||||
m_type = GL_INVALID_ENUM;
|
||||
}
|
||||
|
||||
inline shader_handle shader_data::handle() const
|
||||
{
|
||||
return m_handle;
|
||||
}
|
||||
|
||||
}
|
||||
90
source/opengl/data/shader_program_data.cpp
Executable file
90
source/opengl/data/shader_program_data.cpp
Executable file
@@ -0,0 +1,90 @@
|
||||
|
||||
#include "opengl/data/shader_program_data.hpp"
|
||||
|
||||
#include <fstream>
|
||||
#include "GL/glew.h"
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
#include "util/for_each.hpp"
|
||||
#include "util/logger.hpp"
|
||||
#include "opengl/error.hpp"
|
||||
|
||||
namespace zgl
|
||||
{
|
||||
|
||||
std::error_code shader_program_data::build_from(
|
||||
const shader_handle& vertex_shader,
|
||||
const shader_handle& geometry_shader,
|
||||
const shader_handle& fragment_shader,
|
||||
shader_program_data& data
|
||||
) {
|
||||
auto error = std::error_code{};
|
||||
|
||||
auto check_error = [&error, ec = static_cast<GLenum>(GL_NO_ERROR)]() mutable -> std::error_code&
|
||||
{
|
||||
ec = glGetError();
|
||||
if (ec != GL_NO_ERROR) {
|
||||
error = make_error_code(ec);
|
||||
}
|
||||
return error;
|
||||
};
|
||||
|
||||
const auto program_id = glCreateProgram();
|
||||
if (check_error()) return error;
|
||||
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
constexpr auto shader_names = std::array{
|
||||
"vertex"sv, "geometry"sv, "fragment"sv
|
||||
};
|
||||
|
||||
for (const auto& [shader, name] : {
|
||||
std::tie(vertex_shader, shader_names[0]),
|
||||
std::tie(geometry_shader, shader_names[1]),
|
||||
std::tie(fragment_shader, shader_names[2])
|
||||
}) {
|
||||
if (shader.shader_id) {
|
||||
glAttachShader(program_id, shader.shader_id);
|
||||
} else {
|
||||
ztu::logger::warn("Using default % shader", name);
|
||||
}
|
||||
}
|
||||
|
||||
glLinkProgram(program_id);
|
||||
if (check_error()) return error;
|
||||
|
||||
auto status = GLint{ GL_FALSE };
|
||||
glGetProgramiv(program_id, GL_LINK_STATUS, &status);
|
||||
if (check_error()) return error;
|
||||
|
||||
if (status == GL_FALSE) {
|
||||
GLint log_length{};
|
||||
glGetShaderiv(program_id, GL_INFO_LOG_LENGTH, &log_length);
|
||||
|
||||
auto log = std::string(log_length, ' ');
|
||||
glGetProgramInfoLog(program_id, log_length, nullptr, log.data());
|
||||
|
||||
ztu::logger::warn("Error while linking program: [%] %", log_length, log);
|
||||
|
||||
return std::make_error_code(std::errc::io_error);
|
||||
}
|
||||
|
||||
|
||||
glUseProgram(0);
|
||||
|
||||
ztu::for_each::argument(
|
||||
[&](const auto& shader)
|
||||
{
|
||||
if (shader.shader_id) {
|
||||
glDetachShader(program_id, shader.shader_id);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
vertex_shader, geometry_shader, fragment_shader
|
||||
);
|
||||
|
||||
data = shader_program_data{ program_id };
|
||||
|
||||
return {};
|
||||
}
|
||||
}
|
||||
45
source/opengl/data/shader_program_data.ipp
Executable file
45
source/opengl/data/shader_program_data.ipp
Executable file
@@ -0,0 +1,45 @@
|
||||
#ifndef INCLUDE_SHADER_PROGRAM_DATA_IMPLEMENTATION
|
||||
# error Never include this file directly include 'shader_program_data.hpp'
|
||||
#endif
|
||||
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
#include "util/for_each.hpp"
|
||||
#include "util/logger.hpp"
|
||||
#include "opengl/error.hpp"
|
||||
|
||||
namespace zgl
|
||||
{
|
||||
inline shader_program_data::shader_program_data(GLuint program_id)
|
||||
: m_handle{ program_id } {}
|
||||
|
||||
|
||||
inline shader_program_data::shader_program_data(shader_program_data&& other) noexcept
|
||||
{
|
||||
m_handle = other.m_handle;
|
||||
other.m_handle.program_id = 0;
|
||||
}
|
||||
|
||||
inline shader_program_data& shader_program_data::operator=(shader_program_data&& other) noexcept
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
this->~shader_program_data();
|
||||
m_handle = other.m_handle;
|
||||
other.m_handle.program_id = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline shader_program_data::~shader_program_data()
|
||||
{
|
||||
if (m_handle.program_id) {
|
||||
glDeleteProgram(m_handle.program_id);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] inline shader_program_handle shader_program_data::handle() const
|
||||
{
|
||||
return m_handle;
|
||||
}
|
||||
}
|
||||
79
source/opengl/data/texture_data.ipp
Normal file
79
source/opengl/data/texture_data.ipp
Normal file
@@ -0,0 +1,79 @@
|
||||
#ifndef INCLUDE_TEXTURE_DATA_IMPLEMENTATION
|
||||
# error Never include this file directly include 'texture_data.hpp'
|
||||
#endif
|
||||
|
||||
namespace zgl
|
||||
{
|
||||
inline texture_data::texture_data(const GLuint texture_id)
|
||||
: m_handle{ texture_id } {}
|
||||
|
||||
inline texture_data::texture_data(texture_data&& other) noexcept
|
||||
: m_handle{ other.m_handle }
|
||||
{
|
||||
other.m_handle.texture_id = 0;
|
||||
}
|
||||
|
||||
inline texture_data& texture_data::operator=(texture_data&& other) noexcept
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
this->~texture_data();
|
||||
|
||||
m_handle = other.m_handle;
|
||||
|
||||
other.m_handle.texture_id = 0;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::error_code texture_data::build_from(
|
||||
std::span<const T> buffer,
|
||||
const GLenum format,
|
||||
const GLenum type,
|
||||
const GLsizei width,
|
||||
const GLsizei height,
|
||||
texture_data& data
|
||||
) {
|
||||
GLuint texture_id;
|
||||
|
||||
glGenTextures(1, &texture_id);
|
||||
glBindTexture(GL_TEXTURE_2D, texture_id);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
glTexImage2D(
|
||||
GL_TEXTURE_2D, 0,
|
||||
GL_RGBA8,
|
||||
width,
|
||||
height,
|
||||
0,
|
||||
format, type,
|
||||
buffer.data()
|
||||
);
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
data = texture_data(texture_id);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
inline texture_data::~texture_data()
|
||||
{
|
||||
if (m_handle.texture_id)
|
||||
{
|
||||
glDeleteTextures(1, &m_handle.texture_id);
|
||||
}
|
||||
}
|
||||
|
||||
inline texture_handle texture_data::handle() const
|
||||
{
|
||||
return { m_handle.texture_id };
|
||||
}
|
||||
}
|
||||
152
source/opengl/handles/shader_program_handle.cpp
Normal file
152
source/opengl/handles/shader_program_handle.cpp
Normal file
@@ -0,0 +1,152 @@
|
||||
#include "opengl/handles/shader_program_handle.hpp"
|
||||
|
||||
#include <string_view>
|
||||
// TODO remove
|
||||
#include "opengl/error.hpp"
|
||||
#include "util/logger.hpp"
|
||||
|
||||
namespace zgl
|
||||
{
|
||||
|
||||
shader_program_handle::attribute_support_type shader_program_handle::check_attribute_support(
|
||||
const std::span<const shader_program_variable> attributes
|
||||
) const {
|
||||
auto attribute_candidates = attribute_support_type{};
|
||||
|
||||
auto error = std::error_code{};
|
||||
auto check_error = [&error, ec = static_cast<GLenum>(GL_NO_ERROR)]() mutable -> std::error_code&
|
||||
{
|
||||
ec = glGetError();
|
||||
if (ec != GL_NO_ERROR) {
|
||||
error = make_error_code(ec);
|
||||
}
|
||||
return error;
|
||||
};
|
||||
|
||||
auto curr_attribute_flag = attribute_support_type{ 1 };
|
||||
for (const auto& attribute : attributes) {
|
||||
const auto location = glGetAttribLocation(program_id, attribute.name);
|
||||
if (location == attribute.info.location)
|
||||
{
|
||||
attribute_candidates |= curr_attribute_flag;
|
||||
}
|
||||
curr_attribute_flag <<= 1;
|
||||
}
|
||||
|
||||
auto supported_attributes = attribute_support_type{};
|
||||
|
||||
GLint count;
|
||||
glGetProgramiv(program_id, GL_ACTIVE_ATTRIBUTES, &count);
|
||||
if (check_error()) ztu::logger::error("GL_err: %", error.message());
|
||||
|
||||
for (GLint i{}; i != count and attribute_candidates; ++i)
|
||||
{
|
||||
GLenum type;
|
||||
GLint size;
|
||||
GLsizei name_length;
|
||||
auto name = std::array<char, 256>{};
|
||||
glGetActiveAttrib(
|
||||
program_id, i,
|
||||
name.size(),
|
||||
&name_length,
|
||||
&size, &type,
|
||||
name.data()
|
||||
);
|
||||
if (check_error()) ztu::logger::error("GL_err: %", error.message());
|
||||
|
||||
const auto name_view = std::string_view(name.data(), name_length);
|
||||
|
||||
auto attribute_index = attribute_support_type{};
|
||||
for (auto candidates = attribute_candidates; candidates; candidates >>= 1) {
|
||||
if (candidates & 1)
|
||||
{
|
||||
const auto& attribute = attributes[attribute_index];
|
||||
if (type == attribute.info.type and name_view == attribute.name)
|
||||
{
|
||||
const auto new_uniform_flag = attribute_support_type{ 1 } << attribute_index;
|
||||
supported_attributes |= new_uniform_flag;
|
||||
attribute_candidates ^= new_uniform_flag;
|
||||
}
|
||||
}
|
||||
++attribute_index;
|
||||
}
|
||||
}
|
||||
|
||||
return supported_attributes;
|
||||
}
|
||||
|
||||
shader_program_handle::uniform_support_type shader_program_handle::check_uniform_support(
|
||||
const std::span<const shader_program_variable> uniforms
|
||||
) const {
|
||||
auto uniform_candidates = uniform_support_type{};
|
||||
|
||||
auto error = std::error_code{};
|
||||
auto check_error = [&error, ec = static_cast<GLenum>(GL_NO_ERROR)]() mutable -> std::error_code&
|
||||
{
|
||||
ec = glGetError();
|
||||
if (ec != GL_NO_ERROR) {
|
||||
error = make_error_code(ec);
|
||||
}
|
||||
return error;
|
||||
};
|
||||
|
||||
auto curr_uniform_flag = uniform_support_type{ 1 };
|
||||
for (const auto& uniform : uniforms)
|
||||
{
|
||||
const auto location = glGetUniformLocation(program_id, uniform.name);
|
||||
|
||||
if (location == uniform.info.location)
|
||||
{
|
||||
uniform_candidates |= curr_uniform_flag;
|
||||
ztu::logger::debug("[%] '%': %.", program_id, uniform.name, location);
|
||||
}
|
||||
else
|
||||
{
|
||||
ztu::logger::debug("Expected '%' at % but was %.", uniform.name, uniform.info.location, location);
|
||||
}
|
||||
curr_uniform_flag <<= 1;
|
||||
}
|
||||
|
||||
auto supported_uniforms = uniform_support_type{};
|
||||
|
||||
GLint count;
|
||||
glGetProgramiv(program_id, GL_ACTIVE_UNIFORMS, &count);
|
||||
if (check_error()) ztu::logger::error("GL_err: %", error.message());
|
||||
|
||||
for (GLint i{}; i != count and uniform_candidates; ++i)
|
||||
{
|
||||
GLenum type;
|
||||
GLint size;
|
||||
GLsizei name_length;
|
||||
auto name = std::array<char, 256>{};
|
||||
glGetActiveUniform(
|
||||
program_id, i,
|
||||
name.size(),
|
||||
&name_length,
|
||||
&size, &type,
|
||||
name.data()
|
||||
);
|
||||
if (check_error()) ztu::logger::error("GL_err: %", error.message());
|
||||
|
||||
const auto name_view = std::string_view(name.data(), name_length);
|
||||
|
||||
auto uniform_index = uniform_support_type{};
|
||||
for (auto candidates = uniform_candidates; candidates; candidates >>= 1)
|
||||
{
|
||||
if (candidates & 1)
|
||||
{
|
||||
const auto& uniform = uniforms[uniform_index];
|
||||
if (type == uniform.info.type and name_view == uniform.name)
|
||||
{
|
||||
const auto new_uniform_flag = uniform_support_type{ 1 } << uniform_index;
|
||||
supported_uniforms |= new_uniform_flag;
|
||||
uniform_candidates ^= new_uniform_flag;
|
||||
}
|
||||
}
|
||||
++uniform_index;
|
||||
}
|
||||
}
|
||||
|
||||
return supported_uniforms;
|
||||
}
|
||||
}
|
||||
195
source/opengl/shader_program_lookup.cpp
Normal file
195
source/opengl/shader_program_lookup.cpp
Normal file
@@ -0,0 +1,195 @@
|
||||
#include "opengl/shader_program_lookup.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <bitset>
|
||||
|
||||
#include "util/logger.hpp" // TODO remove
|
||||
|
||||
namespace zgl
|
||||
{
|
||||
|
||||
|
||||
void shader_program_lookup::add(
|
||||
const shader_program_handle& shader_program_handle,
|
||||
const std::span<const shader_program_variable> all_attributes,
|
||||
const std::span<const shader_program_variable> all_uniforms
|
||||
) {
|
||||
const auto attributes = shader_program_handle.check_attribute_support(all_attributes);
|
||||
const auto uniforms = shader_program_handle.check_uniform_support(all_uniforms);
|
||||
|
||||
ztu::logger::debug("add [%] uniforms: % attributes: %",
|
||||
shader_program_handle.program_id,
|
||||
std::bitset<32>(uniforms),
|
||||
std::bitset<32>(attributes)
|
||||
);
|
||||
|
||||
const auto lower_uniform = std::ranges::lower_bound(
|
||||
m_mesh_shader_program_uniforms,
|
||||
uniforms
|
||||
);
|
||||
|
||||
const auto upper_uniform = std::find_if(
|
||||
lower_uniform, m_mesh_shader_program_uniforms.end(),
|
||||
[&](const auto& curr_uniforms) {
|
||||
return curr_uniforms > uniforms;
|
||||
}
|
||||
);
|
||||
|
||||
const auto lower_index = lower_uniform - m_mesh_shader_program_uniforms.begin();
|
||||
const auto upper_index = upper_uniform - m_mesh_shader_program_uniforms.begin();
|
||||
|
||||
const auto lower_attribute = m_mesh_shader_program_attributes.begin() + lower_index;
|
||||
const auto upper_attribute = m_mesh_shader_program_attributes.begin() + upper_index;
|
||||
|
||||
const auto attribute_it = std::upper_bound(
|
||||
lower_attribute, upper_attribute,
|
||||
attributes,
|
||||
[](const auto& attributes, const auto& entry) {
|
||||
return attributes < entry.attributes;
|
||||
}
|
||||
);
|
||||
|
||||
const auto index = attribute_it - m_mesh_shader_program_attributes.begin();
|
||||
|
||||
const auto attribute_locations = attribute_location_flags(
|
||||
attributes, all_attributes
|
||||
);
|
||||
|
||||
m_mesh_shader_program_uniforms.insert(
|
||||
m_mesh_shader_program_uniforms.begin() + index, uniforms
|
||||
);
|
||||
|
||||
m_mesh_shader_program_attributes.emplace(
|
||||
attribute_it, attributes, attribute_locations
|
||||
);
|
||||
|
||||
m_mesh_shader_programs.insert(
|
||||
m_mesh_shader_programs.begin() + index, shader_program_handle
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
std::optional<shader_program_handle> shader_program_lookup::find(
|
||||
shader_program_handle::attribute_support_type attributes,
|
||||
shader_program_handle::uniform_support_type uniforms,
|
||||
const std::span<const shader_program_variable> all_attributes
|
||||
) const {
|
||||
|
||||
const auto lower_uniform = std::ranges::lower_bound(
|
||||
m_mesh_shader_program_uniforms,
|
||||
uniforms
|
||||
);
|
||||
|
||||
if (
|
||||
lower_uniform == m_mesh_shader_program_uniforms.end() or
|
||||
*lower_uniform != uniforms
|
||||
) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const auto upper_uniform = std::find_if(
|
||||
lower_uniform, m_mesh_shader_program_uniforms.end(),
|
||||
[&](const auto& curr_uniforms) {
|
||||
return curr_uniforms > uniforms;
|
||||
}
|
||||
);
|
||||
|
||||
const auto lower_index = lower_uniform - m_mesh_shader_program_uniforms.begin();
|
||||
const auto upper_index = upper_uniform - m_mesh_shader_program_uniforms.begin();
|
||||
|
||||
const auto relevant_attributes = std::span(
|
||||
m_mesh_shader_program_attributes.begin() + lower_index,
|
||||
m_mesh_shader_program_attributes.begin() + upper_index
|
||||
);
|
||||
|
||||
const auto upper_attribute = std::upper_bound(
|
||||
relevant_attributes.begin(), relevant_attributes.end(),
|
||||
attributes,
|
||||
[](const auto& attributes, const auto& entry) {
|
||||
return attributes < entry.attributes;
|
||||
}
|
||||
);
|
||||
|
||||
auto entry_it = std::prev(upper_attribute);
|
||||
|
||||
if (
|
||||
upper_attribute != relevant_attributes.begin() and
|
||||
entry_it->attributes != attributes
|
||||
) {
|
||||
|
||||
const auto locations = attribute_location_flags(attributes, all_attributes);
|
||||
|
||||
auto found_match = false;
|
||||
|
||||
while (entry_it != relevant_attributes.begin())
|
||||
{
|
||||
--entry_it;
|
||||
|
||||
const auto& [ curr_attributes, curr_locations ] = *entry_it;
|
||||
|
||||
// The candidate may not support additional attributes
|
||||
if (curr_attributes & ~attributes)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// The candidate may not be missing 'inner' attributes,
|
||||
// as this creates location alignment problems.
|
||||
const auto higher_neighbour_matched = (curr_locations & locations) >> 1;
|
||||
const auto bubbles = higher_neighbour_matched & ~curr_locations;
|
||||
|
||||
if (not bubbles)
|
||||
{
|
||||
found_match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (not found_match)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
const auto shader_program_index = entry_it.base() - m_mesh_shader_program_attributes.begin().base();
|
||||
|
||||
return m_mesh_shader_programs[shader_program_index];
|
||||
}
|
||||
|
||||
shader_program_lookup::attribute_locations_type shader_program_lookup::attribute_location_flags(
|
||||
shader_program_handle::attribute_support_type attributes,
|
||||
std::span<const shader_program_variable> all_attributes
|
||||
) {
|
||||
auto location_flags = ztu::u32{ 0 };
|
||||
|
||||
auto index = std::size_t{};
|
||||
|
||||
while (attributes)
|
||||
{
|
||||
if (attributes & 1)
|
||||
{
|
||||
const auto location = all_attributes[index].info.location;
|
||||
location_flags |= attribute_locations_type{ 1 } << location;
|
||||
}
|
||||
|
||||
attributes >>= 1;
|
||||
++index;
|
||||
}
|
||||
|
||||
return location_flags;
|
||||
}
|
||||
|
||||
void shader_program_lookup::print() {
|
||||
for (std::size_t i{}; i != m_mesh_shader_program_uniforms.size(); ++i) {
|
||||
const auto shader = m_mesh_shader_programs[i];
|
||||
const auto uniforms = m_mesh_shader_program_uniforms[i];
|
||||
const auto [ attributes, locations ] = m_mesh_shader_program_attributes[i];
|
||||
ztu::logger::debug("[%] uniforms: % attributes: % locations: %",
|
||||
shader.program_id,
|
||||
std::bitset<32>(uniforms),
|
||||
std::bitset<32>(attributes),
|
||||
std::bitset<32>(locations)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
341
source/rendering/batch_renderers/mesh_batch_renderer.cpp
Normal file
341
source/rendering/batch_renderers/mesh_batch_renderer.cpp
Normal file
@@ -0,0 +1,341 @@
|
||||
#include "rendering/batch_renderers/mesh_batch_renderer.hpp"
|
||||
|
||||
#include "shader_program/uniforms/mesh_uniforms.hpp"
|
||||
#include "util/unroll_bool_template.hpp"
|
||||
#include "util/logger.hpp" // TODO remove
|
||||
#include <bitset> // TODOE remove
|
||||
|
||||
namespace rendering
|
||||
{
|
||||
|
||||
mesh_batch_renderer::mesh_batch_renderer(int render_mode_count)
|
||||
: m_render_mode_count{ render_mode_count } {};
|
||||
|
||||
std::pair<std::size_t, bool> mesh_batch_renderer::lookup_batch(
|
||||
const batch_components_type& batch_components
|
||||
) const {
|
||||
|
||||
const auto component_it = std::upper_bound(
|
||||
m_component_lookup.begin(), m_component_lookup.end(),
|
||||
batch_components,
|
||||
[](const auto& components, const auto& entry)
|
||||
{
|
||||
return components < entry.first;
|
||||
}
|
||||
);
|
||||
|
||||
const auto index = component_it - m_component_lookup.begin();
|
||||
|
||||
const auto match = (
|
||||
index != 0 and m_component_lookup[index - 1].first == batch_components
|
||||
);
|
||||
|
||||
return { index - static_cast<std::size_t>(match), match };
|
||||
}
|
||||
|
||||
std::optional<mesh_batch_renderer::id_type> mesh_batch_renderer::add(
|
||||
const batch_components_type& batch_component,
|
||||
const zgl::mesh_handle& mesh,
|
||||
const aabb& bounding_box,
|
||||
const zgl::model_matrix_handle& transform,
|
||||
const zgl::material_handle& material,
|
||||
const shader_program_lookups::mesh_lookup& shader_program_lookup
|
||||
) {
|
||||
const auto [ lookup_index, lookup_match ] = lookup_batch(batch_component);
|
||||
|
||||
std::size_t batch_index;
|
||||
batch_id_type batch_id;
|
||||
|
||||
if (lookup_match)
|
||||
{
|
||||
batch_index = m_component_lookup[lookup_index].second;
|
||||
batch_id = m_id_lookup[batch_index];
|
||||
}
|
||||
else
|
||||
{
|
||||
auto base_requirements = requirements::mesh::flags::position;
|
||||
|
||||
const auto [ vertex_comps, material_comps ] = batch_component;
|
||||
|
||||
// If no texture is provided, the uniform color is provided by ambient light.
|
||||
if ((material_comps & material_component::flags::texture) == material_component::flags::none)
|
||||
{
|
||||
base_requirements |= requirements::mesh::flags::uniform_color;
|
||||
}
|
||||
|
||||
ztu::logger::debug("vertex_comps: %", std::bitset<32>{ static_cast<unsigned long long>(static_cast<int>(vertex_comps)) });
|
||||
ztu::logger::debug("material_comps: %", std::bitset<32>{ static_cast<unsigned long long>(static_cast<int>(material_comps)) });
|
||||
ztu::logger::debug("lit reqs: %", std::bitset<32>{ static_cast<unsigned long long>(static_cast<int>(shader_program::capabilities::mesh::lit.uniforms)) });
|
||||
|
||||
|
||||
for (std::size_t i{}; i != requirements::mesh::all.size(); ++i)
|
||||
{
|
||||
const auto& requirement = requirements::mesh::all[i];
|
||||
|
||||
if (
|
||||
(
|
||||
requirement.vertex_requirements != components::mesh_vertex::flags::none and
|
||||
(vertex_comps & requirement.vertex_requirements) == requirement.vertex_requirements
|
||||
)
|
||||
and
|
||||
(
|
||||
requirement.material_requirements != material_component::flags::none and
|
||||
(material_comps & requirement.material_requirements) == requirement.material_requirements
|
||||
)
|
||||
) {
|
||||
base_requirements |= requirements::mesh::flags{ 1 << i };
|
||||
}
|
||||
}
|
||||
|
||||
ztu::logger::debug("base reqs: %", std::bitset<32>{ static_cast<unsigned long long>(static_cast<int>(base_requirements)) });
|
||||
|
||||
|
||||
const auto base_shader = shader_program_lookup.find(base_requirements);
|
||||
if (not base_shader)
|
||||
{
|
||||
ztu::logger::warn("Could not find base shader!");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const auto point_shader = shader_program_lookup.find(base_requirements | requirements::mesh::flags::point);
|
||||
if (not point_shader)
|
||||
{
|
||||
ztu::logger::warn("Could not find point shader!");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const auto lit_shader = shader_program_lookup.find(base_requirements | requirements::mesh::flags::lit);
|
||||
if (not lit_shader)
|
||||
{
|
||||
ztu::logger::warn("Could not find lit shader!");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto shader_programs = std::array{
|
||||
*base_shader,
|
||||
*point_shader,
|
||||
*base_shader,
|
||||
*lit_shader
|
||||
};
|
||||
|
||||
ztu::logger::debug(
|
||||
"shaders: % % %",
|
||||
base_shader->program_id,
|
||||
point_shader->program_id,
|
||||
lit_shader->program_id
|
||||
);
|
||||
|
||||
batch_index = m_batches.size();
|
||||
batch_id = m_next_batch_id++;
|
||||
m_batches.emplace_back(batch_type{}, batch_component);
|
||||
m_id_lookup.push_back(batch_id);
|
||||
m_component_lookup.emplace(m_component_lookup.begin() + lookup_index, batch_component, batch_index);
|
||||
m_shader_programs.insert(
|
||||
m_shader_programs.begin() + lookup_index,
|
||||
shader_programs.begin(), shader_programs.end()
|
||||
);
|
||||
}
|
||||
|
||||
auto& batch = m_batches[batch_index].first;
|
||||
|
||||
const auto mesh_id = batch.add(mesh, bounding_box, transform, material);
|
||||
|
||||
return id_type{ batch_id, mesh_id };
|
||||
}
|
||||
|
||||
std::optional<aabb> mesh_batch_renderer::bounding_box(id_type id)
|
||||
{
|
||||
const auto lookup_it = std::ranges::find(m_id_lookup, id.first);
|
||||
|
||||
if (lookup_it == m_id_lookup.end())
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const auto batch_index = lookup_it - m_id_lookup.begin();
|
||||
auto& batch = m_batches[batch_index].first;
|
||||
|
||||
return batch.bounding_box(id.second);
|
||||
}
|
||||
|
||||
bool mesh_batch_renderer::remove(const id_type id)
|
||||
{
|
||||
const auto lookup_it = std::ranges::find(m_id_lookup, id.first);
|
||||
|
||||
if (lookup_it == m_id_lookup.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto batch_index = lookup_it - m_id_lookup.begin();
|
||||
auto& batch = m_batches[batch_index].first;
|
||||
|
||||
// If batches can be removed the indices in m_component_lookup need to be changed.
|
||||
return batch.remove(id.second);
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<bool Textured, bool Lit, bool Alpha>
|
||||
void render_mesh_batch(
|
||||
const zgl::shader_program_handle& shader_program,
|
||||
const mesh_batch& batch,
|
||||
const glm::mat4& vp_matrix,
|
||||
const glm::mat4& view_matrix,
|
||||
const GLenum draw_mode
|
||||
) {
|
||||
const auto meshes = batch.meshes();
|
||||
const auto transforms = batch.transforms();
|
||||
const auto textures = batch.textures();
|
||||
const auto surface_properties = batch.surface_properties();
|
||||
const auto alphas = batch.alphas();
|
||||
|
||||
/*ztu::logger::debug("meshes: %", meshes.size());
|
||||
ztu::logger::debug("transforms: %", transforms.size());
|
||||
ztu::logger::debug("textures: %", textures.size());
|
||||
ztu::logger::debug("surface_properties: %", surface_properties.size());
|
||||
ztu::logger::debug("alphas: %", alphas.size());
|
||||
|
||||
ztu::logger::debug("textured: % alpha: % lit: %", Textured, Alpha, Lit);*/
|
||||
|
||||
namespace uniforms = shader_program::uniforms::mesh;
|
||||
|
||||
for (std::size_t i{}; i != meshes.size(); ++i)
|
||||
{
|
||||
//ztu::logger::debug("Mesh: %", i);
|
||||
|
||||
const auto& mesh = meshes[i];
|
||||
const auto& model_matrix = transforms[i];
|
||||
|
||||
const auto mvp_matrix = vp_matrix * model_matrix;
|
||||
|
||||
shader_program.set_uniform<uniforms::mvp.info>(mvp_matrix);
|
||||
|
||||
if constexpr (Textured)
|
||||
{
|
||||
textures[i].bind();
|
||||
}
|
||||
|
||||
if constexpr (Lit)
|
||||
{
|
||||
shader_program.set_uniform<uniforms::model_matrix.info>(model_matrix);
|
||||
|
||||
// TODO more efficient set
|
||||
const auto& properties = surface_properties[i];
|
||||
shader_program.set_uniform<uniforms::ambient_filter.info>(properties.ambient_filter);
|
||||
shader_program.set_uniform<uniforms::diffuse_filter.info>(properties.diffuse_filter);
|
||||
shader_program.set_uniform<uniforms::specular_filter.info>(properties.specular_filter);
|
||||
shader_program.set_uniform<uniforms::shininess.info>(properties.shininess);
|
||||
}
|
||||
|
||||
if constexpr (Alpha)
|
||||
{
|
||||
|
||||
shader_program.set_uniform<uniforms::alpha.info>(alphas[i]);
|
||||
}
|
||||
|
||||
//ztu::logger::debug("vao: % valid: %%", mesh.vao_id, std::boolalpha, (bool)glIsVertexArray(mesh.vao_id));
|
||||
|
||||
mesh.bind();
|
||||
|
||||
//ztu::logger::debug("glDrawElements(%, %)", draw_mode, mesh.index_count);
|
||||
|
||||
glDrawElements(draw_mode, mesh.index_count, GL_UNSIGNED_INT, nullptr);
|
||||
|
||||
//ztu::logger::debug("done");
|
||||
}
|
||||
}
|
||||
|
||||
void mesh_batch_renderer::render(
|
||||
const modes::mesh render_mode,
|
||||
const glm::mat4& vp_matrix,
|
||||
const glm::mat4& view_matrix,
|
||||
const glm::vec3& view_pos,
|
||||
const lighting_setup& lights
|
||||
) {
|
||||
|
||||
namespace uniforms = shader_program::uniforms::mesh;
|
||||
|
||||
const auto render_mode_index = static_cast<int>(render_mode);
|
||||
|
||||
const auto lines = render_mode == modes::mesh::wire_frame;
|
||||
const auto points = render_mode == modes::mesh::points;
|
||||
const auto lit = render_mode == modes::mesh::lit_faces;
|
||||
|
||||
for (std::size_t i{}; i != m_batches.size(); ++i) {
|
||||
|
||||
//ztu::logger::debug("batch: %", i);
|
||||
|
||||
const auto& [ batch, batch_components ] = m_batches[i];
|
||||
|
||||
const auto [ vertex_components, material_components ] = batch_components;
|
||||
|
||||
const auto textured = (
|
||||
(vertex_components & components::mesh_vertex::flags::tex_coord) != components::mesh_vertex::flags::none
|
||||
and (material_components & material_component::flags::texture) != material_component::flags::none
|
||||
);
|
||||
|
||||
const auto alpha = (
|
||||
(material_components & material_component::flags::transparency) != material_component::flags::none
|
||||
);
|
||||
|
||||
const auto draw_mode = points ? GLenum{ GL_POINTS } : GLenum{ GL_TRIANGLES };
|
||||
|
||||
const auto& shader_program = m_shader_programs[i * m_render_mode_count + render_mode_index];
|
||||
|
||||
//ztu::logger::debug("shader_program: % valid: %%", shader_program.program_id, std::boolalpha, (bool)glIsProgram(shader_program.program_id));
|
||||
|
||||
shader_program.bind();
|
||||
|
||||
if (lit)
|
||||
{
|
||||
// TODO set more efficiently
|
||||
shader_program.set_uniform<uniforms::view_pos.info>(view_pos);
|
||||
shader_program.set_uniform<uniforms::point_light_direction.info>(lights.point_light_direction);
|
||||
shader_program.set_uniform<uniforms::point_light_color.info>(lights.point_light_color);
|
||||
shader_program.set_uniform<uniforms::ambient_light_color.info>(lights.ambient_light_color);
|
||||
}
|
||||
|
||||
if (textured)
|
||||
{
|
||||
constexpr auto texture_unit = 0;
|
||||
glActiveTexture(GL_TEXTURE0 + texture_unit);
|
||||
shader_program.set_uniform<uniforms::tex.info>(texture_unit);
|
||||
}
|
||||
else
|
||||
{
|
||||
shader_program.set_uniform<uniforms::color.info>(glm::vec4(lights.ambient_light_color, 1.0f));
|
||||
}
|
||||
|
||||
if (lines)
|
||||
{
|
||||
glPolygonMode(GL_FRONT, GL_LINE);
|
||||
glPolygonMode(GL_BACK, GL_LINE);
|
||||
}
|
||||
|
||||
unroll_bool_function_template(
|
||||
[&]<bool Textured, bool Lit, bool Alpha>() {
|
||||
render_mesh_batch<Textured, Lit, Alpha>(
|
||||
shader_program,
|
||||
batch,
|
||||
vp_matrix,
|
||||
view_matrix,
|
||||
draw_mode
|
||||
);
|
||||
},
|
||||
textured, lit, alpha
|
||||
);
|
||||
|
||||
if (lines)
|
||||
{
|
||||
glPolygonMode(GL_FRONT, GL_FILL);
|
||||
glPolygonMode(GL_BACK, GL_FILL);
|
||||
}
|
||||
}
|
||||
|
||||
zgl::texture_handle::unbind();
|
||||
zgl::mesh_handle::unbind();
|
||||
zgl::shader_program_handle::unbind();
|
||||
}
|
||||
|
||||
}
|
||||
243
source/rendering/batch_renderers/point_cloud_batch_renderer.cpp
Normal file
243
source/rendering/batch_renderers/point_cloud_batch_renderer.cpp
Normal file
@@ -0,0 +1,243 @@
|
||||
#include "rendering/batch_renderers/point_cloud_batch_renderer.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "rendering/requirements/point_cloud_requirements.hpp"
|
||||
#include "shader_program/uniforms/point_cloud_uniforms.hpp"
|
||||
#include "util/unroll_bool_template.hpp"
|
||||
|
||||
namespace rendering
|
||||
{
|
||||
|
||||
point_cloud_batch_renderer::point_cloud_batch_renderer(int render_mode_count)
|
||||
: m_render_mode_count{ render_mode_count } {};
|
||||
|
||||
std::pair<std::size_t, bool> point_cloud_batch_renderer::lookup_batch(
|
||||
const batch_components_type& batch_component
|
||||
) const {
|
||||
const auto component_it = std::upper_bound(
|
||||
m_component_lookup.begin(), m_component_lookup.end(),
|
||||
batch_component,
|
||||
[](const auto& batch_component, const auto& entry)
|
||||
{
|
||||
return batch_component < entry.first;
|
||||
}
|
||||
);
|
||||
|
||||
const auto index = component_it - m_component_lookup.begin();
|
||||
|
||||
const auto match = (
|
||||
index == 0 or m_component_lookup[index - 1].first == batch_component
|
||||
);
|
||||
|
||||
return { index - static_cast<std::size_t>(match), match };
|
||||
}
|
||||
|
||||
std::optional<point_cloud_batch_renderer::id_type> point_cloud_batch_renderer::add(
|
||||
batch_components_type batch_components,
|
||||
const zgl::point_cloud_handle& point_cloud,
|
||||
const aabb& bounding_box,
|
||||
const zgl::model_matrix_handle& transform,
|
||||
const shader_program_lookups::point_cloud_lookup& shader_program_lookup
|
||||
) {
|
||||
const auto [ lookup_index, lookup_match ] = lookup_batch(batch_components);
|
||||
|
||||
std::size_t batch_index;
|
||||
batch_id_type batch_id;
|
||||
|
||||
if (lookup_match) {
|
||||
batch_index = m_component_lookup[lookup_index].second;
|
||||
batch_id = m_id_lookup[batch_index];
|
||||
}
|
||||
else
|
||||
{
|
||||
auto base_requirements = requirements::point_cloud::flags{};
|
||||
|
||||
const auto vertex_comps = batch_components;
|
||||
|
||||
for (std::size_t i{}; i != requirements::point_cloud::all.size(); ++i)
|
||||
{
|
||||
const auto& requirement = requirements::point_cloud::all[i];
|
||||
|
||||
if (
|
||||
(vertex_comps & requirement.vertex_requirements) != components::point_cloud_vertex::flags::none
|
||||
) {
|
||||
base_requirements |= requirements::point_cloud::flags{ 1 << i };
|
||||
}
|
||||
}
|
||||
|
||||
const auto uniform_color_shader = shader_program_lookup.find(
|
||||
base_requirements | requirements::point_cloud::flags::uniform_color
|
||||
);
|
||||
|
||||
if (not uniform_color_shader)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const auto rainbow_shader = shader_program_lookup.find(
|
||||
base_requirements | requirements::point_cloud::flags::rainbow
|
||||
);
|
||||
|
||||
if (not rainbow_shader)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto shader_programs = std::array{
|
||||
*uniform_color_shader,
|
||||
*rainbow_shader
|
||||
};
|
||||
|
||||
batch_index = m_batches.size();
|
||||
batch_id = m_next_batch_id++;
|
||||
m_batches.emplace_back(batch_type{}, batch_components);
|
||||
m_id_lookup.push_back(batch_id);
|
||||
m_component_lookup.emplace(m_component_lookup.begin() + lookup_index, batch_components, batch_index);
|
||||
m_shader_programs.insert(
|
||||
m_shader_programs.begin() + lookup_index,
|
||||
shader_programs.begin(), shader_programs.end()
|
||||
);
|
||||
}
|
||||
|
||||
auto& batch = m_batches[batch_index].first;
|
||||
|
||||
const auto mesh_id = batch.add(point_cloud, bounding_box, transform);
|
||||
|
||||
return id_type{ batch_id, mesh_id };
|
||||
}
|
||||
|
||||
std::optional<aabb> point_cloud_batch_renderer::bounding_box(id_type id)
|
||||
{
|
||||
const auto lookup_it = std::ranges::find(m_id_lookup, id.first);
|
||||
|
||||
if (lookup_it == m_id_lookup.end())
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const auto batch_index = lookup_it - m_id_lookup.begin();
|
||||
auto& batch = m_batches[batch_index].first;
|
||||
|
||||
return batch.bounding_box(id.second);
|
||||
}
|
||||
|
||||
bool point_cloud_batch_renderer::remove(const id_type id)
|
||||
{
|
||||
const auto lookup_it = std::ranges::find(m_id_lookup, id.first);
|
||||
|
||||
if (lookup_it == m_id_lookup.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto batch_index = lookup_it - m_id_lookup.begin();
|
||||
auto& batch = m_batches[batch_index].first;
|
||||
|
||||
// If batches can be removed the indices in m_component_lookup need to be changed.
|
||||
return batch.remove(id.second);
|
||||
}
|
||||
|
||||
|
||||
template<bool Normals>
|
||||
void render_point_cloud_batch(
|
||||
const zgl::shader_program_handle& shader_program,
|
||||
const point_cloud_batch& batch,
|
||||
const glm::mat4& vp_matrix,
|
||||
const glm::vec3& camera_position
|
||||
) {
|
||||
const auto point_clouds = batch.point_clouds();
|
||||
const auto transforms = batch.transforms();
|
||||
|
||||
namespace uniforms = shader_program::uniforms::point_cloud;
|
||||
|
||||
for (std::size_t i{}; i != point_clouds.size(); ++i)
|
||||
{
|
||||
const auto& point_cloud = point_clouds[i];
|
||||
const auto& model_matrix = transforms[i];
|
||||
|
||||
// TODO check order
|
||||
const auto mvp_matrix = vp_matrix * model_matrix;
|
||||
|
||||
shader_program.set_uniform<uniforms::mvp.info>(mvp_matrix);
|
||||
|
||||
if constexpr (Normals)
|
||||
{
|
||||
shader_program.set_uniform<uniforms::model.info>(model_matrix);
|
||||
shader_program.set_uniform<uniforms::camera_position.info>(camera_position);
|
||||
}
|
||||
|
||||
point_cloud.bind();
|
||||
|
||||
using block_index_type = ztu::u16;
|
||||
static constexpr auto block_size = static_cast<std::size_t>(
|
||||
std::numeric_limits<block_index_type>::max()
|
||||
);
|
||||
|
||||
for (GLsizei j{}; j < point_cloud.point_count; j += block_size)
|
||||
{
|
||||
const auto points_left = static_cast<std::size_t>(point_cloud.point_count) - j;
|
||||
const auto points_in_block = std::min(points_left, block_size);
|
||||
glDrawArrays(
|
||||
GL_POINTS,
|
||||
j,
|
||||
static_cast<block_index_type>(points_in_block)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void point_cloud_batch_renderer::render(
|
||||
const modes::point_cloud render_mode,
|
||||
const glm::mat4& vp_matrix,
|
||||
const glm::vec3& camera_position,
|
||||
const lighting_setup&
|
||||
) {
|
||||
|
||||
namespace uniforms = shader_program::uniforms::point_cloud;
|
||||
|
||||
const auto render_mode_index = static_cast<std::size_t>(render_mode);
|
||||
|
||||
glEnable(GL_PROGRAM_POINT_SIZE);
|
||||
glEnable(GL_POINT_SMOOTH);
|
||||
|
||||
const auto rainbow = render_mode == modes::point_cloud::rainbow;
|
||||
|
||||
|
||||
for (std::size_t i{}; i != m_batches.size(); ++i) {
|
||||
|
||||
const auto& [ batch, vertex_components ] = m_batches[i];
|
||||
|
||||
const auto normals = static_cast<bool>(vertex_components & components::point_cloud_vertex::flags::normal);
|
||||
|
||||
const auto& shader_program = m_shader_programs[i * m_render_mode_count + render_mode_index];
|
||||
|
||||
shader_program.bind();
|
||||
|
||||
if (rainbow)
|
||||
{
|
||||
shader_program.set_uniform<uniforms::rainbow_offset_y.info>(0.0f); // TODO fix
|
||||
shader_program.set_uniform<uniforms::rainbow_scale_y.info>(0.0f); // TODO fix
|
||||
}
|
||||
|
||||
unroll_bool_function_template(
|
||||
[&]<bool Normals>() {
|
||||
render_point_cloud_batch<Normals>(
|
||||
shader_program,
|
||||
batch,
|
||||
vp_matrix,
|
||||
camera_position
|
||||
);
|
||||
},
|
||||
normals
|
||||
);
|
||||
}
|
||||
|
||||
glDisable(GL_PROGRAM_POINT_SIZE);
|
||||
glDisable(GL_POINT_SMOOTH);
|
||||
|
||||
zgl::point_cloud_handle::unbind();
|
||||
zgl::shader_program_handle::unbind();
|
||||
}
|
||||
|
||||
}
|
||||
147
source/rendering/batches/mesh_batch.ipp
Normal file
147
source/rendering/batches/mesh_batch.ipp
Normal file
@@ -0,0 +1,147 @@
|
||||
#ifndef INCLUDE_MESH_BATCH_IMPLEMENTATION
|
||||
# error Never include this file directly include 'mesh_batch.hpp'
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
inline mesh_batch::id_type mesh_batch::add(
|
||||
const zgl::mesh_handle& mesh,
|
||||
const aabb& bounding_box,
|
||||
const zgl::model_matrix_handle& transform,
|
||||
const zgl::material_handle& material
|
||||
) {
|
||||
std::size_t index;
|
||||
if (material.texture)
|
||||
{
|
||||
// Sort by texture id if possible, so meshes the same texture are rendered consecutively.
|
||||
const auto texture_it = std::ranges::upper_bound(
|
||||
m_textures,
|
||||
*material.texture,
|
||||
[](const auto& lhs, const auto& rhs)
|
||||
{
|
||||
return lhs.texture_id < rhs.texture_id;
|
||||
}
|
||||
);
|
||||
index = texture_it - m_textures.begin();
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO inserting by vao might split up texture sequence, this needs more attention
|
||||
// Otherwise, sort by vao, so meshes with the same vertices are rendered consecutively.
|
||||
const auto mesh_it = std::ranges::upper_bound(
|
||||
m_meshes,
|
||||
mesh,
|
||||
[](const auto& lhs, const auto& rhs)
|
||||
{
|
||||
return lhs.vao_id < rhs.vao_id;
|
||||
}
|
||||
);
|
||||
index = mesh_it - m_meshes.begin();
|
||||
}
|
||||
|
||||
m_meshes.insert(m_meshes.begin() + index, mesh);
|
||||
m_bounding_boxes.insert(m_bounding_boxes.begin() + index, bounding_box);
|
||||
m_transforms.insert(m_transforms.begin() + index, transform);
|
||||
|
||||
if (material.texture)
|
||||
{
|
||||
m_textures.insert(m_textures.begin() + index, *material.texture);
|
||||
}
|
||||
|
||||
if (material.surface_properties)
|
||||
{
|
||||
m_surface_properties.insert(m_surface_properties.begin() + index, *material.surface_properties);
|
||||
}
|
||||
|
||||
if (material.alpha)
|
||||
{
|
||||
m_alphas.insert(m_alphas.begin() + index, *material.alpha);
|
||||
}
|
||||
|
||||
const auto mesh_id = m_next_mesh_id++;
|
||||
|
||||
m_id_lookup.insert(m_id_lookup.begin() + index, mesh_id);
|
||||
|
||||
return mesh_id;
|
||||
}
|
||||
|
||||
std::optional<aabb> mesh_batch::bounding_box(id_type id)
|
||||
{
|
||||
const auto lookup_it = std::ranges::find(m_id_lookup, id);
|
||||
|
||||
if (lookup_it == m_id_lookup.end())
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const auto index = lookup_it - m_id_lookup.begin();
|
||||
|
||||
const auto& base_bounding_box = m_bounding_boxes[index];
|
||||
const auto& transform = m_transforms[index];
|
||||
|
||||
return base_bounding_box.transformed(transform);
|
||||
}
|
||||
|
||||
inline bool mesh_batch::remove(const id_type id)
|
||||
{
|
||||
const auto lookup_it = std::ranges::find(m_id_lookup, id);
|
||||
|
||||
if (lookup_it == m_id_lookup.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto index = lookup_it - m_id_lookup.begin();
|
||||
|
||||
m_id_lookup.erase(m_id_lookup.begin() + index);
|
||||
m_meshes.erase(m_meshes.begin() + index);
|
||||
m_bounding_boxes.erase(m_bounding_boxes.begin() + index);
|
||||
m_transforms.erase(m_transforms.begin() + index);
|
||||
|
||||
if (not m_textures.empty())
|
||||
{
|
||||
m_textures.erase(m_textures.begin() + index);
|
||||
}
|
||||
|
||||
if (not m_surface_properties.empty())
|
||||
{
|
||||
m_surface_properties.erase(m_surface_properties.begin() + index);
|
||||
}
|
||||
|
||||
if (not m_alphas.empty())
|
||||
{
|
||||
m_alphas.erase(m_alphas.begin() + index);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline std::span<const zgl::mesh_handle> mesh_batch::meshes() const
|
||||
{
|
||||
return m_meshes;
|
||||
}
|
||||
|
||||
inline std::span<const aabb> mesh_batch::bounding_boxes() const
|
||||
{
|
||||
return m_bounding_boxes;
|
||||
}
|
||||
|
||||
inline std::span<const zgl::model_matrix_handle> mesh_batch::transforms() const
|
||||
{
|
||||
return m_transforms;
|
||||
}
|
||||
|
||||
inline std::span<const zgl::texture_handle> mesh_batch::textures() const
|
||||
{
|
||||
return m_textures;
|
||||
}
|
||||
|
||||
inline std::span<const zgl::surface_properties_handle> mesh_batch::surface_properties() const
|
||||
{
|
||||
return m_surface_properties;
|
||||
}
|
||||
|
||||
inline std::span<const zgl::alpha_handle> mesh_batch::alphas() const
|
||||
{
|
||||
return m_alphas;
|
||||
}
|
||||
69
source/rendering/batches/point_cloud_batch.ipp
Normal file
69
source/rendering/batches/point_cloud_batch.ipp
Normal file
@@ -0,0 +1,69 @@
|
||||
#ifndef INCLUDE_POINT_CLOUD_BATCH_IMPLEMENTATION
|
||||
# error Never include this file directly include 'point_cloud_batch.hpp'
|
||||
#endif
|
||||
|
||||
|
||||
inline point_cloud_batch::id_type point_cloud_batch::add(
|
||||
const zgl::point_cloud_handle& point_cloud,
|
||||
const aabb& bounding_box,
|
||||
const zgl::model_matrix_handle& transform
|
||||
) {
|
||||
const auto new_id = m_next_id++;
|
||||
|
||||
m_point_clouds.push_back(point_cloud);
|
||||
m_transforms.push_back(transform);
|
||||
m_bounding_boxes.push_back(bounding_box);
|
||||
m_id_lookup.push_back(new_id);
|
||||
|
||||
return new_id;
|
||||
}
|
||||
|
||||
std::optional<aabb> point_cloud_batch::bounding_box(id_type id)
|
||||
{
|
||||
const auto lookup_it = std::ranges::find(m_id_lookup, id);
|
||||
|
||||
if (lookup_it == m_id_lookup.end())
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const auto index = lookup_it - m_id_lookup.begin();
|
||||
|
||||
const auto& base_bounding_box = m_bounding_boxes[index];
|
||||
const auto& transform = m_transforms[index];
|
||||
|
||||
return base_bounding_box.transformed(transform);
|
||||
}
|
||||
|
||||
inline bool point_cloud_batch::remove(id_type id)
|
||||
{
|
||||
const auto lookup_it = std::ranges::find(m_id_lookup, id);
|
||||
|
||||
if (lookup_it == m_id_lookup.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto index = lookup_it - m_id_lookup.begin();
|
||||
|
||||
m_id_lookup.erase(m_id_lookup.begin() + index);
|
||||
m_point_clouds.erase(m_point_clouds.begin() + index);
|
||||
m_transforms.erase(m_transforms.begin() + index);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline std::span<const zgl::point_cloud_handle> point_cloud_batch::point_clouds() const
|
||||
{
|
||||
return m_point_clouds;
|
||||
}
|
||||
|
||||
inline std::span<const zgl::model_matrix_handle> point_cloud_batch::transforms() const
|
||||
{
|
||||
return m_transforms;
|
||||
}
|
||||
|
||||
inline std::span<const aabb> point_cloud_batch::bounding_boxes() const
|
||||
{
|
||||
return m_bounding_boxes;
|
||||
}
|
||||
58
source/rendering/shader_program_lookups/mesh_lookup.cpp
Normal file
58
source/rendering/shader_program_lookups/mesh_lookup.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
#include "rendering/shader_program_lookups/mesh_lookup.hpp"
|
||||
|
||||
#include "util/logger.hpp" // TODO remove
|
||||
#include <bitset> // TODO remove
|
||||
|
||||
namespace rendering::shader_program_lookups
|
||||
{
|
||||
|
||||
void mesh_lookup::add(
|
||||
const zgl::shader_program_handle& shader_program_handle
|
||||
) {
|
||||
m_shader_program_lookup.add(
|
||||
shader_program_handle,
|
||||
shader_program::attributes::mesh::all,
|
||||
shader_program::uniforms::mesh::all
|
||||
);
|
||||
}
|
||||
|
||||
std::optional<zgl::shader_program_handle> mesh_lookup::find(
|
||||
requirements::mesh::flags requirements
|
||||
) const {
|
||||
auto capability = shader_program::capabilities::mesh::type{};
|
||||
|
||||
auto index = std::size_t{};
|
||||
|
||||
auto requirement_flags = static_cast<ztu::u32>(requirements);
|
||||
|
||||
while (requirement_flags)
|
||||
{
|
||||
if (requirement_flags & 1)
|
||||
{
|
||||
const auto shader_requirements_index = requirements::mesh::all[index].shader_program_requirement_index;
|
||||
const auto& [ attributes, uniforms ] = shader_program::capabilities::mesh::all[shader_requirements_index];
|
||||
capability.attributes |= attributes;
|
||||
capability.uniforms |= uniforms;
|
||||
}
|
||||
|
||||
requirement_flags >>= 1;
|
||||
++index;
|
||||
}
|
||||
|
||||
// TODO if not textured and not colored add ucolor "for free"
|
||||
|
||||
ztu::logger::debug("attributes reqs: %", std::bitset<32>{ static_cast<unsigned long long>(static_cast<int>(capability.attributes)) });
|
||||
ztu::logger::debug("uniforms reqs: %", std::bitset<32>{ static_cast<unsigned long long>(static_cast<int>(capability.uniforms)) });
|
||||
|
||||
return m_shader_program_lookup.find(
|
||||
static_cast<ztu::u32>(capability.attributes),
|
||||
static_cast<ztu::u32>(capability.uniforms),
|
||||
shader_program::attributes::mesh::all
|
||||
);
|
||||
}
|
||||
|
||||
void mesh_lookup::print() {
|
||||
m_shader_program_lookup.print();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
#include "rendering/shader_program_lookups/point_cloud_lookup.hpp"
|
||||
|
||||
namespace rendering::shader_program_lookups
|
||||
{
|
||||
|
||||
void point_cloud_lookup::add(
|
||||
const zgl::shader_program_handle& shader_program_handle
|
||||
) {
|
||||
m_program_lookup.add(
|
||||
shader_program_handle,
|
||||
shader_program::attributes::point_cloud::all,
|
||||
shader_program::uniforms::point_cloud::all
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
std::optional<zgl::shader_program_handle> point_cloud_lookup::find(
|
||||
requirements::point_cloud::flags requirements
|
||||
) const {
|
||||
auto capability = shader_program::capabilities::point_cloud::type{};
|
||||
|
||||
auto index = std::size_t{};
|
||||
|
||||
auto requirement_flags = static_cast<ztu::u32>(requirements);
|
||||
|
||||
while (requirement_flags)
|
||||
{
|
||||
if (requirement_flags & 1)
|
||||
{
|
||||
const auto shader_requirements_index = requirements::point_cloud::all[index].shader_program_requirement_index;
|
||||
const auto& [ attributes, uniforms ] = shader_program::capabilities::point_cloud::all[shader_requirements_index];
|
||||
capability.attributes |= attributes;
|
||||
capability.uniforms |= uniforms;
|
||||
}
|
||||
|
||||
requirement_flags >>= 1;
|
||||
++index;
|
||||
}
|
||||
|
||||
return m_program_lookup.find(
|
||||
static_cast<ztu::u32>(capability.attributes),
|
||||
static_cast<ztu::u32>(capability.uniforms),
|
||||
shader_program::attributes::point_cloud::all
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
117
source/scene/flying_camera.cpp
Executable file
117
source/scene/flying_camera.cpp
Executable file
@@ -0,0 +1,117 @@
|
||||
#include "scene/flying_camera.hpp"
|
||||
|
||||
#include <SFML/Window/Keyboard.hpp>
|
||||
#include <cmath>
|
||||
#include <numbers>
|
||||
#include "glm/gtx/string_cast.hpp"
|
||||
#include "glm/gtx/euler_angles.hpp"
|
||||
|
||||
#include "util/logger.hpp" // TODO remove
|
||||
#include "glm/gtx/vector_angle.hpp"
|
||||
|
||||
flying_camera::flying_camera(const float yaw, const float pitch, const float roll) {
|
||||
m_world_rotation = glm::mat3(glm::eulerAngleYXZ<float>(yaw, pitch, roll));
|
||||
m_world_up = m_world_rotation * glm::vec3(0, 1, 0);
|
||||
|
||||
m_velocity = glm::vec3(0.f, 0.f, 0.f);
|
||||
m_pitch = 0.f;
|
||||
m_yaw = glm::radians(-90.f);
|
||||
m_roll = 0.f;
|
||||
}
|
||||
|
||||
|
||||
void flying_camera::update(
|
||||
const float time_delta,
|
||||
const glm::vec2 mouse_pos_delta,
|
||||
const float mouse_wheel_delta,
|
||||
camera_view& view
|
||||
) {
|
||||
static constexpr auto pi = std::numbers::pi_v<float>;
|
||||
static constexpr auto epsilon = std::numeric_limits<float>::epsilon();
|
||||
static constexpr auto world_up = glm::vec3(0.0f, 1.0f, 0.0f);
|
||||
|
||||
static constexpr auto friction_coefficient = 25.0f;
|
||||
static constexpr auto walk_acceleration = 3000.0f;
|
||||
static constexpr auto max_velocity = 4000.0f;
|
||||
|
||||
static constexpr float max_pitch = (pi / 2.0f) - epsilon;
|
||||
static constexpr float min_fov = 0.01f * pi;
|
||||
static constexpr float max_fov = 0.8f * pi;
|
||||
|
||||
m_yaw += mouse_pos_delta.x;
|
||||
m_pitch -= mouse_pos_delta.y;
|
||||
|
||||
m_yaw = std::fmod(m_yaw, 2.0f * pi);
|
||||
m_pitch = std::clamp(m_pitch, -max_pitch, max_pitch);
|
||||
|
||||
view.front.x = std::cos(m_yaw) * std::cos(m_pitch);
|
||||
view.front.y = std::sin(m_pitch);
|
||||
view.front.z = std::sin(m_yaw) * std::cos(m_pitch);
|
||||
view.front = glm::normalize(view.front);
|
||||
|
||||
view.right = glm::normalize(glm::cross(view.front, world_up));
|
||||
view.up = glm::normalize(glm::cross(view.right, view.front));
|
||||
|
||||
view.front = m_world_rotation * view.front;
|
||||
view.up = m_world_rotation * view.up;
|
||||
view.right = m_world_rotation * view.right;
|
||||
|
||||
view.fov *= 1.0f + mouse_wheel_delta;
|
||||
view.fov = std::clamp(view.fov, min_fov, max_fov);
|
||||
|
||||
using kb = sf::Keyboard;
|
||||
auto acceleration = glm::vec3{ 0.0f };
|
||||
|
||||
if (kb::isKeyPressed(kb::W)) acceleration += view.front;
|
||||
if (kb::isKeyPressed(kb::S)) acceleration -= view.front;
|
||||
|
||||
if (kb::isKeyPressed(kb::A)) acceleration -= view.right;
|
||||
if (kb::isKeyPressed(kb::D)) acceleration += view.right;
|
||||
|
||||
if (kb::isKeyPressed(kb::Space)) acceleration += m_world_up; // TODO fix
|
||||
if (kb::isKeyPressed(kb::LControl)) acceleration -= m_world_up;
|
||||
|
||||
acceleration *= walk_acceleration;
|
||||
|
||||
const auto acc = glm::length(acceleration);
|
||||
if (acc > epsilon)
|
||||
{
|
||||
acceleration *= walk_acceleration / acc;
|
||||
}
|
||||
|
||||
if (kb::isKeyPressed(kb::LShift)) acceleration *= 2.0f;
|
||||
|
||||
m_velocity += acceleration * time_delta;
|
||||
|
||||
const float drag = std::exp(-friction_coefficient * time_delta);
|
||||
m_velocity *= drag;
|
||||
|
||||
|
||||
const auto speed = time_delta * glm::length(m_velocity);
|
||||
if (speed > max_velocity) {
|
||||
m_velocity *= (max_velocity / speed) * max_velocity;
|
||||
}
|
||||
|
||||
view.position += m_velocity * time_delta;
|
||||
}
|
||||
|
||||
void flying_camera::look_at(
|
||||
const glm::vec3& origin,
|
||||
const glm::vec3& target,
|
||||
camera_view& view
|
||||
) {
|
||||
static constexpr auto world_up = glm::vec3(0.0f, 1.0f, 0.0f);
|
||||
// TODO inverted matrix
|
||||
|
||||
view.position = origin;
|
||||
view.front = glm::normalize(target - origin);
|
||||
view.right = glm::normalize(glm::cross(view.front, world_up));
|
||||
view.up = glm::normalize(glm::cross(view.right, view.front));
|
||||
view.fov = std::numbers::pi_v<float> / 2.0f;
|
||||
|
||||
m_velocity = { 0.0f, 0.0f, 0.0f };
|
||||
|
||||
m_pitch = std::asin(glm::dot(view.front, world_up));
|
||||
m_yaw = std::atan2(view.front.z, view.front.x);
|
||||
m_roll = std::atan2(view.up.x, view.up.y);
|
||||
}
|
||||
636
source/viewer/asset_loader.cpp
Normal file
636
source/viewer/asset_loader.cpp
Normal file
@@ -0,0 +1,636 @@
|
||||
#include "viewer/asset_loader.hpp"
|
||||
|
||||
#include <geometry/normal_estimation.hpp>
|
||||
|
||||
#include "geometry/aabb.hpp"
|
||||
#include "util/logger.hpp"
|
||||
#include "glm/gtx/string_cast.hpp" // TODO remove
|
||||
|
||||
namespace viewer
|
||||
{
|
||||
std::error_code asset_loader::init(
|
||||
components::mesh_vertex::flags enabled_mesh_components,
|
||||
material_component::flags enabled_material_components,
|
||||
components::point_cloud_vertex::flags enabled_point_cloud_components,
|
||||
const dynamic_material_data& default_material
|
||||
) {
|
||||
|
||||
//m_ctx.setActive(true);
|
||||
|
||||
m_enabled_mesh_components = enabled_mesh_components;
|
||||
m_enabled_material_components = enabled_material_components;
|
||||
m_enabled_point_cloud_components = enabled_point_cloud_components;
|
||||
|
||||
m_dynamic_material_data_buffer.push_back(default_material);
|
||||
|
||||
return create_gl_materials();
|
||||
}
|
||||
|
||||
std::error_code asset_loader::load_shader(
|
||||
const GLenum type,
|
||||
const std::filesystem::path& filename,
|
||||
zgl::shader_handle& shader_handle
|
||||
) {
|
||||
|
||||
auto& [ buffer_source, buffer_type ] = m_dynamic_shader_data_buffer;
|
||||
|
||||
std::stringstream source_stream;
|
||||
static constexpr auto glsl_version = 460;
|
||||
|
||||
source_stream << "#version " << glsl_version << '\n';
|
||||
source_stream << "#define " << "hello" << '\n';
|
||||
source_stream << buffer_source;
|
||||
|
||||
buffer_type = type;
|
||||
|
||||
if (filename.empty())
|
||||
{
|
||||
buffer_source = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (const auto e = m_shader_loader.load(filename, buffer_source)) {
|
||||
ztu::logger::warn(
|
||||
"Could not load shader_program_data source file %: [%] %",
|
||||
filename,
|
||||
e.category().name(),
|
||||
e.message()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (const auto e = create_gl_shader()) {
|
||||
return e;
|
||||
}
|
||||
|
||||
shader_handle = m_gl_shader_data.back().handle();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::error_code asset_loader::build_shader_program(
|
||||
const zgl::shader_handle& vertex_shader,
|
||||
const zgl::shader_handle& geometry_shader,
|
||||
const zgl::shader_handle& fragment_shader,
|
||||
zgl::shader_program_handle& shader_program_handle
|
||||
) {
|
||||
|
||||
auto program_data = zgl::shader_program_data{};
|
||||
|
||||
if (const auto e = zgl::shader_program_data::build_from(
|
||||
vertex_shader,
|
||||
geometry_shader,
|
||||
fragment_shader,
|
||||
program_data
|
||||
)) {
|
||||
return e;
|
||||
}
|
||||
|
||||
shader_program_handle = program_data.handle();
|
||||
|
||||
m_gl_shader_program_data.emplace_back(std::move(program_data));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::error_code asset_loader::load_asset(
|
||||
const std::string& format,
|
||||
const std::filesystem::path& filename,
|
||||
std::vector<std::pair<dynamic_mesh_handle_type, dynamic_material_handle_type>>& dynamic_mesh_handles,
|
||||
std::vector<dynamic_point_cloud_handle_type>& dynamic_point_cloud_handles
|
||||
) {
|
||||
std::error_code error;
|
||||
|
||||
if ((error = load_mesh(
|
||||
format, filename, dynamic_mesh_handles
|
||||
))) {
|
||||
if (
|
||||
error.category() == std::generic_category() and
|
||||
static_cast<std::errc>(error.value()) == std::errc::invalid_argument
|
||||
) {
|
||||
error = load_point_cloud(format, filename, dynamic_point_cloud_handles);
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
std::error_code asset_loader::load_mesh(
|
||||
const std::string& format,
|
||||
const std::filesystem::path& filename,
|
||||
std::vector<std::pair<dynamic_mesh_handle_type, dynamic_material_handle_type>>& dynamic_mesh_handles
|
||||
) {
|
||||
const auto mesh_loader_id = m_mesh_loader.find_loader(format);
|
||||
|
||||
if (not mesh_loader_id)
|
||||
{
|
||||
return std::make_error_code(std::errc::invalid_argument);
|
||||
}
|
||||
|
||||
if (const auto e = m_mesh_loader.read(
|
||||
*mesh_loader_id,
|
||||
filename,
|
||||
m_dynamic_mesh_data_buffer,
|
||||
m_enabled_mesh_components,
|
||||
m_dynamic_material_data_buffer,
|
||||
m_enabled_material_components,
|
||||
next_materials_id,
|
||||
true
|
||||
)) {
|
||||
return e;
|
||||
}
|
||||
|
||||
return process_materials_and_meshes(dynamic_mesh_handles);
|
||||
}
|
||||
|
||||
std::error_code asset_loader::load_point_cloud(
|
||||
const std::string& format,
|
||||
const std::filesystem::path& filename,
|
||||
std::vector<dynamic_point_cloud_handle_type>& dynamic_point_cloud_handles
|
||||
) {
|
||||
const auto point_cloud_loader_id = m_point_cloud_loader.find_loader(format);
|
||||
|
||||
if (not point_cloud_loader_id)
|
||||
{
|
||||
return std::make_error_code(std::errc::invalid_argument);
|
||||
}
|
||||
|
||||
if (const auto e = m_point_cloud_loader.read(
|
||||
*point_cloud_loader_id,
|
||||
filename,
|
||||
m_dynamic_point_cloud_buffer,
|
||||
true
|
||||
)) {
|
||||
return e;
|
||||
}
|
||||
|
||||
return process_point_clouds(dynamic_point_cloud_handles);
|
||||
}
|
||||
|
||||
std::error_code asset_loader::load_asset_directory(
|
||||
const std::string& format,
|
||||
const std::filesystem::path& path,
|
||||
std::vector<std::pair<dynamic_mesh_handle_type, dynamic_material_handle_type>>& dynamic_mesh_handles,
|
||||
std::vector<dynamic_point_cloud_handle_type>& dynamic_point_cloud_handles
|
||||
) {
|
||||
std::error_code error;
|
||||
|
||||
if ((error = load_mesh_directory(
|
||||
format, path, dynamic_mesh_handles
|
||||
))) {
|
||||
if (
|
||||
error.category() == std::generic_category() and
|
||||
static_cast<std::errc>(error.value()) == std::errc::invalid_argument
|
||||
) {
|
||||
error = load_point_cloud_directory(format, path, dynamic_point_cloud_handles);
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
std::error_code asset_loader::load_mesh_directory(
|
||||
const std::string& format,
|
||||
const std::filesystem::path& path,
|
||||
std::vector<std::pair<dynamic_mesh_handle_type, dynamic_material_handle_type>>& dynamic_mesh_handles
|
||||
) {
|
||||
const auto mesh_loader_id = m_mesh_loader.find_loader(format);
|
||||
|
||||
if (not mesh_loader_id)
|
||||
{
|
||||
return std::make_error_code(std::errc::invalid_argument);
|
||||
}
|
||||
|
||||
if (const auto e = m_mesh_loader.read_directory(
|
||||
*mesh_loader_id,
|
||||
path,
|
||||
m_dynamic_mesh_data_buffer,
|
||||
m_enabled_mesh_components,
|
||||
m_dynamic_material_data_buffer,
|
||||
m_enabled_material_components,
|
||||
next_materials_id,
|
||||
true
|
||||
)) {
|
||||
return e;
|
||||
}
|
||||
|
||||
return process_materials_and_meshes(dynamic_mesh_handles);
|
||||
}
|
||||
|
||||
|
||||
std::error_code asset_loader::load_point_cloud_directory(
|
||||
const std::string& format,
|
||||
const std::filesystem::path& path,
|
||||
std::vector<dynamic_point_cloud_handle_type>& dynamic_point_cloud_handles
|
||||
) {
|
||||
const auto point_cloud_loader_id = m_point_cloud_loader.find_loader(format);
|
||||
|
||||
if (not point_cloud_loader_id)
|
||||
{
|
||||
return std::make_error_code(std::errc::invalid_argument);
|
||||
}
|
||||
|
||||
if (const auto e = m_point_cloud_loader.read_directory(
|
||||
*point_cloud_loader_id,
|
||||
path,
|
||||
m_dynamic_point_cloud_buffer,
|
||||
true
|
||||
)) {
|
||||
return e;
|
||||
}
|
||||
|
||||
return process_point_clouds(dynamic_point_cloud_handles);
|
||||
}
|
||||
|
||||
std::error_code asset_loader::process_materials_and_meshes(
|
||||
std::vector<std::pair<dynamic_mesh_handle_type, dynamic_material_handle_type>>& dynamic_mesh_handles
|
||||
) {
|
||||
|
||||
const auto material_count_before = m_gl_material_data_references.size();
|
||||
|
||||
if (const auto e = create_gl_materials())
|
||||
{
|
||||
m_dynamic_mesh_data_buffer.clear();
|
||||
return e;
|
||||
}
|
||||
|
||||
const auto new_materials = std::span(
|
||||
m_gl_material_data_references.begin() + material_count_before,
|
||||
m_gl_material_data_references.end()
|
||||
);
|
||||
|
||||
const auto mesh_count_before = m_gl_mesh_data.size();
|
||||
|
||||
create_gl_meshes(new_materials);
|
||||
|
||||
const auto new_meshes = std::span(
|
||||
m_gl_mesh_data.begin() + mesh_count_before,
|
||||
m_gl_mesh_data.end()
|
||||
);
|
||||
|
||||
const auto dynamic_mesh_count_before = dynamic_mesh_handles.size();
|
||||
dynamic_mesh_handles.resize(dynamic_mesh_handles.size() + new_meshes.size());
|
||||
|
||||
std::ranges::transform(
|
||||
new_meshes,
|
||||
dynamic_mesh_handles.begin() + dynamic_mesh_count_before,
|
||||
[&](const auto& entry)
|
||||
{
|
||||
const auto& [ gl_mesh_data, bounding_box ] = entry;
|
||||
const auto material_id = gl_mesh_data.material_id();
|
||||
|
||||
auto material_index = std::size_t{ 0 };
|
||||
|
||||
if (material_id != 0)
|
||||
{
|
||||
const auto material_reference_it = std::ranges::find_if(
|
||||
new_materials,
|
||||
[&material_id](const auto& entry) {
|
||||
return entry.first == material_id;
|
||||
}
|
||||
);
|
||||
|
||||
if (material_reference_it == new_materials.end())
|
||||
{
|
||||
ztu::logger::error(
|
||||
"Something went horribly wrong while searching for material. Falling back to default material"
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
material_index = material_reference_it.base() - m_gl_material_data_references.begin().base();
|
||||
}
|
||||
}
|
||||
|
||||
const auto& gl_material = m_gl_material_data[material_index];
|
||||
|
||||
//ztu::logger::debug("mesh components: %", static_cast<unsigned int>(gl_mesh_data.components()));
|
||||
//ztu::logger::debug("material components: %", static_cast<unsigned int>(gl_material.components()));
|
||||
|
||||
return std::make_pair(
|
||||
dynamic_mesh_handle_type{
|
||||
.handle = gl_mesh_data.handle(),
|
||||
.bounding_box = bounding_box,
|
||||
.components = gl_mesh_data.components()
|
||||
},
|
||||
dynamic_material_handle_type{
|
||||
.handle = gl_material.handle(),
|
||||
.components = gl_material.components()
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::error_code asset_loader::process_point_clouds(
|
||||
std::vector<dynamic_point_cloud_handle_type>& dynamic_point_cloud_handles
|
||||
) {
|
||||
const auto point_cloud_count_before = m_gl_point_cloud_data.size();
|
||||
|
||||
create_gl_point_clouds();
|
||||
|
||||
const auto new_point_clouds = std::span(
|
||||
m_gl_point_cloud_data.begin() + point_cloud_count_before,
|
||||
m_gl_point_cloud_data.end()
|
||||
);
|
||||
|
||||
const auto dynamic_point_cloud_count_before = dynamic_point_cloud_handles.size();
|
||||
dynamic_point_cloud_handles.resize(dynamic_point_cloud_handles.size() + new_point_clouds.size());
|
||||
|
||||
std::ranges::transform(
|
||||
new_point_clouds,
|
||||
dynamic_point_cloud_handles.begin() + dynamic_point_cloud_count_before,
|
||||
[&](const auto& gl_point_cloud_data)
|
||||
{
|
||||
const auto& [ data, box ] = gl_point_cloud_data;
|
||||
return dynamic_point_cloud_handle_type{
|
||||
.handle = data.handle(),
|
||||
.bounding_box = box,
|
||||
.components = data.components()
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::error_code asset_loader::create_gl_materials()
|
||||
{
|
||||
auto error = std::error_code{};
|
||||
|
||||
for (const auto& material_data : m_dynamic_material_data_buffer)
|
||||
{
|
||||
auto gl_material_data = zgl::material_data{};
|
||||
|
||||
if ((error = zgl::material_data::build_from(
|
||||
material_data.texture(),
|
||||
material_data.surface_properties(),
|
||||
material_data.transparency(),
|
||||
material_data.components(),
|
||||
gl_material_data
|
||||
))) {
|
||||
ztu::logger::error(
|
||||
"Error while creating material gpu handle: [%] %",
|
||||
error.category().name(),
|
||||
error.message()
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_gl_material_data.emplace_back(std::move(gl_material_data));
|
||||
m_gl_material_data_references.emplace_back(next_materials_id, 0);
|
||||
}
|
||||
|
||||
++next_materials_id;
|
||||
}
|
||||
|
||||
m_dynamic_material_data_buffer.clear();
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
void asset_loader::create_gl_meshes(std::span<const material_reference_entry_type> material_references)
|
||||
{
|
||||
auto component_type_buffer = std::array<GLenum, static_cast<std::size_t>(components::mesh_vertex::count)>{};
|
||||
auto component_length_buffer = std::array<GLint, static_cast<std::size_t>(components::mesh_vertex::count)>{};
|
||||
|
||||
auto component_stride = GLsizei{};
|
||||
auto component_count = ztu::u32{};
|
||||
|
||||
for (auto& mesh_data : m_dynamic_mesh_data_buffer)
|
||||
{
|
||||
if (mesh_data.triangles().empty())
|
||||
{
|
||||
ztu::logger::warn("Skipping mesh with empty index buffer.");
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto material_id = mesh_data.material_id();
|
||||
|
||||
auto material_index = std::size_t{ 0 };
|
||||
|
||||
if (material_id != 0) // Default material is always there
|
||||
{
|
||||
const auto material_reference_it = std::ranges::find_if(
|
||||
material_references,
|
||||
[&material_id](const material_reference_entry_type& entry) {
|
||||
return entry.first == material_id;
|
||||
}
|
||||
);
|
||||
|
||||
if (material_reference_it == material_references.end())
|
||||
{
|
||||
ztu::logger::error("Skipping mesh because referenced material cannot be found");
|
||||
continue;
|
||||
}
|
||||
|
||||
material_index = material_reference_it - material_references.begin();
|
||||
}
|
||||
|
||||
// Add normals if missing
|
||||
if ((mesh_data.components() & components::mesh_vertex::flags::normal) == components::mesh_vertex::flags::none)
|
||||
{
|
||||
ztu::logger::warn("Model is missing normal vectors, so they are estimated!");
|
||||
estimate_normals(
|
||||
mesh_data.positions(),
|
||||
mesh_data.triangles(),
|
||||
mesh_data.normals()
|
||||
);
|
||||
mesh_data.components() |= components::mesh_vertex::flags::normal;
|
||||
}
|
||||
|
||||
auto mesh_box = aabb{};
|
||||
mesh_box.add_points<components::mesh_vertex::normal::value_type>(mesh_data.positions());
|
||||
|
||||
mesh_data.build_vertex_buffer(
|
||||
m_vertex_buffer,
|
||||
component_count,
|
||||
component_type_buffer,
|
||||
component_length_buffer,
|
||||
component_stride
|
||||
);
|
||||
|
||||
auto gl_mesh_data = zgl::mesh_data{};
|
||||
|
||||
const auto& first_triangle = mesh_data.triangles().front();
|
||||
|
||||
// TODO make span of size component_count
|
||||
|
||||
if (const auto e = zgl::mesh_data::build_from(
|
||||
m_vertex_buffer,
|
||||
std::span(component_type_buffer).subspan(0, component_count),
|
||||
std::span(component_length_buffer).subspan(0, component_count),
|
||||
component_stride,
|
||||
std::span(
|
||||
first_triangle.data(),
|
||||
mesh_data.triangles().size() * first_triangle.size()
|
||||
),
|
||||
mesh_data.material_id(),
|
||||
mesh_data.components(),
|
||||
gl_mesh_data
|
||||
)) {
|
||||
ztu::logger::error(
|
||||
"Error while creating opengl mesh data: [%] %\nMesh will be skipped.",
|
||||
e.category().name(),
|
||||
e.message()
|
||||
);
|
||||
}
|
||||
|
||||
++m_gl_material_data_references[material_index].second;
|
||||
|
||||
m_gl_mesh_data.emplace_back(std::move(gl_mesh_data), mesh_box);
|
||||
}
|
||||
|
||||
m_dynamic_mesh_data_buffer.clear();
|
||||
}
|
||||
|
||||
void asset_loader::create_gl_point_clouds()
|
||||
{
|
||||
auto component_type_buffer = std::array<GLenum, static_cast<std::size_t>(components::point_cloud_vertex::count)>{};
|
||||
auto component_length_buffer = std::array<GLint, static_cast<std::size_t>(components::point_cloud_vertex::count)>{};
|
||||
auto component_stride = GLsizei{};
|
||||
auto component_count = ztu::u32{};
|
||||
|
||||
for (const auto& point_cloud_data : m_dynamic_point_cloud_buffer)
|
||||
{
|
||||
if (point_cloud_data.positions().empty())
|
||||
{
|
||||
ztu::logger::warn("Skipping point cloud without points.");
|
||||
continue;
|
||||
}
|
||||
|
||||
auto point_cloud_box = aabb{};
|
||||
point_cloud_box.add_points<components::mesh_vertex::normal::value_type>(point_cloud_data.positions());
|
||||
|
||||
point_cloud_data.build_vertex_buffer(
|
||||
m_vertex_buffer,
|
||||
component_count,
|
||||
component_type_buffer,
|
||||
component_length_buffer,
|
||||
component_stride
|
||||
);
|
||||
|
||||
auto gl_point_cloud_data = zgl::point_cloud_data{};
|
||||
|
||||
if (const auto e = zgl::point_cloud_data::build_from(
|
||||
m_vertex_buffer,
|
||||
component_type_buffer,
|
||||
component_length_buffer,
|
||||
component_stride,
|
||||
gl_point_cloud_data
|
||||
)) {
|
||||
ztu::logger::error(
|
||||
"Error while creating opengl point cloud data: [%] %\nPoint cloud will be skipped.",
|
||||
e.category().name(),
|
||||
e.message()
|
||||
);
|
||||
}
|
||||
|
||||
m_gl_point_cloud_data.emplace_back(std::move(gl_point_cloud_data), point_cloud_box);
|
||||
}
|
||||
|
||||
m_dynamic_point_cloud_buffer.clear();
|
||||
}
|
||||
|
||||
std::error_code asset_loader::create_gl_shader()
|
||||
{
|
||||
auto shader_data = zgl::shader_data{};
|
||||
|
||||
const auto& [source, type] = m_dynamic_shader_data_buffer;
|
||||
|
||||
if (const auto e = zgl::shader_data::build_from(type, source, shader_data)) {
|
||||
return e;
|
||||
}
|
||||
|
||||
m_gl_shader_data.emplace_back(std::move(shader_data));
|
||||
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
bool asset_loader::unload(const zgl::shader_program_handle& shader_handle)
|
||||
{
|
||||
const auto it = std::find_if(
|
||||
m_gl_shader_program_data.begin(), m_gl_shader_program_data.end(),
|
||||
[&shader_handle](const auto& gl_shader_data) {
|
||||
return gl_shader_data.handle().program_id == shader_handle.program_id;
|
||||
}
|
||||
);
|
||||
|
||||
if (it == m_gl_shader_program_data.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_gl_shader_program_data.erase(it);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool asset_loader::unload(const zgl::mesh_handle& mesh_handle)
|
||||
{
|
||||
const auto it = std::ranges::find_if(
|
||||
m_gl_mesh_data,
|
||||
[&mesh_handle](const auto& gl_mesh_data) {
|
||||
return gl_mesh_data.first.handle().vao_id == mesh_handle.vao_id;
|
||||
}
|
||||
);
|
||||
|
||||
if (it == m_gl_mesh_data.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto material_id = it->first.material_id();
|
||||
|
||||
const auto reference_it = std::ranges::find_if(
|
||||
m_gl_material_data_references,
|
||||
[&material_id](const auto& entry) {
|
||||
return entry.first == material_id;
|
||||
}
|
||||
);
|
||||
|
||||
if (reference_it != m_gl_material_data_references.end())
|
||||
{
|
||||
// Do not delete default material at index 0
|
||||
if (--reference_it->second == 0 and reference_it != m_gl_material_data_references.begin())
|
||||
{
|
||||
const auto index = reference_it - m_gl_material_data_references.begin();
|
||||
m_gl_material_data.erase(m_gl_material_data.begin() + index);
|
||||
m_gl_material_data_references.erase(reference_it);
|
||||
}
|
||||
}
|
||||
|
||||
m_gl_mesh_data.erase(it);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void asset_loader::unload_shader_data()
|
||||
{
|
||||
m_gl_shader_data.clear();
|
||||
}
|
||||
|
||||
|
||||
bool asset_loader::unload(const zgl::point_cloud_handle& point_cloud_handle)
|
||||
{
|
||||
const auto it = std::ranges::find_if(
|
||||
m_gl_point_cloud_data,
|
||||
[&point_cloud_handle](const auto& entry) {
|
||||
return entry.first.handle().vao_id == point_cloud_handle.vao_id;
|
||||
}
|
||||
);
|
||||
|
||||
if (it == m_gl_point_cloud_data.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_gl_point_cloud_data.erase(it);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
275
source/viewer/dynamic_shader_program_loading.cpp
Normal file
275
source/viewer/dynamic_shader_program_loading.cpp
Normal file
@@ -0,0 +1,275 @@
|
||||
#include "../../include/viewer/dynamic_shader_program_loading.hpp"
|
||||
#include "../../include/util/string_lookup.hpp"
|
||||
#include "../../include/util/logger.hpp"
|
||||
#include <sstream>
|
||||
|
||||
std::size_t viewer::dynamic_shader_program_loading::count_shader_files(
|
||||
const std::filesystem::path& path
|
||||
) {
|
||||
using namespace std::string_view_literals;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
auto shader_file_count = std::size_t{ 0 };
|
||||
|
||||
for (const auto& [ asset_type, folder ] : {
|
||||
std::make_pair(asset_types::mesh, "mesh"sv),
|
||||
std::make_pair(asset_types::point_cloud, "point_cloud"sv)
|
||||
}) {
|
||||
const auto folder_begin = fs::directory_iterator{ path / folder };
|
||||
const auto folder_end = fs::directory_iterator{};
|
||||
|
||||
shader_file_count += std::count_if(
|
||||
folder_begin, folder_end,
|
||||
[](auto& entry) { return entry.is_regular_file(); }
|
||||
);
|
||||
}
|
||||
|
||||
return shader_file_count;
|
||||
}
|
||||
|
||||
void viewer::dynamic_shader_program_loading::load_directory(
|
||||
asset_loader& loader,
|
||||
instance& z3d,
|
||||
std::mutex& gl_resource_lock,
|
||||
std::mutex& progress_lock,
|
||||
std::string& progress_title,
|
||||
float& progress_ratio,
|
||||
const std::filesystem::path& path
|
||||
) {
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
auto progress_builder = std::stringstream{};
|
||||
|
||||
const auto shader_file_count = static_cast<float>(count_shader_files(path));
|
||||
constexpr auto shader_loading_progress = 0.8f;
|
||||
|
||||
|
||||
auto shader_indices = ztu::string_lookup<ztu::u32>({
|
||||
{ "vertex", 0 },
|
||||
{ "geometry", 1 },
|
||||
{ "fragment", 2 }
|
||||
});
|
||||
|
||||
constexpr auto shader_types = std::array<GLenum, 3>{
|
||||
GL_VERTEX_SHADER,
|
||||
GL_GEOMETRY_SHADER,
|
||||
GL_FRAGMENT_SHADER
|
||||
};
|
||||
|
||||
auto program_capabilities = std::vector<ztu::u32>{};
|
||||
auto programs = std::vector<std::array<zgl::shader_handle, 3>>{};
|
||||
|
||||
auto capability_indices = ztu::string_lookup<ztu::u32>();
|
||||
auto capabilities = std::vector<ztu::u32>{};
|
||||
capabilities.reserve(8);
|
||||
|
||||
constexpr auto dot_char = '.';
|
||||
constexpr auto separator_char = '_';
|
||||
constexpr auto optional_char = '?';
|
||||
|
||||
auto curr_shader_count = std::size_t{ 0 };
|
||||
|
||||
for (const auto& [ asset_type, folder ] : {
|
||||
std::make_pair(asset_types::mesh, "mesh"sv),
|
||||
std::make_pair(asset_types::point_cloud, "point_cloud"sv),
|
||||
}) {
|
||||
program_capabilities.clear();
|
||||
programs.clear();
|
||||
capability_indices.clear();
|
||||
|
||||
for (const auto& file : fs::directory_iterator{ path / folder })
|
||||
{
|
||||
if (not file.is_regular_file())
|
||||
continue;
|
||||
|
||||
const auto& file_path = file.path();
|
||||
|
||||
if (file_path.extension() != ".glsl")
|
||||
continue;
|
||||
|
||||
const auto filename = file_path.filename().string();
|
||||
|
||||
progress_lock.lock();
|
||||
|
||||
progress_builder.str(std::string{});
|
||||
progress_builder << "Loading shader '" << filename << "\'...";
|
||||
progress_title = progress_builder.str();
|
||||
progress_ratio = shader_loading_progress * static_cast<float>(curr_shader_count) / shader_file_count;
|
||||
|
||||
progress_lock.unlock();
|
||||
|
||||
const auto name = std::string_view(filename.begin(), std::ranges::find(filename, dot_char));
|
||||
|
||||
const auto type_str = std::string_view(name.begin(), std::ranges::find(name, separator_char));
|
||||
|
||||
const auto shader_type_index_it = shader_indices.find(type_str);
|
||||
if (shader_type_index_it == shader_indices.end()) {
|
||||
ztu::logger::warn("Unknown shader type '%'. Skipping shader.", type_str);
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto shader_type_index = shader_type_index_it->second;
|
||||
const auto shader_type = shader_types[shader_type_index];
|
||||
|
||||
auto shader_handle = zgl::shader_handle{};
|
||||
if (const auto e = loader.load_shader(shader_type, file_path, shader_handle)) {
|
||||
ztu::logger::error(
|
||||
"Error while loading shader %: [%] %",
|
||||
file_path,
|
||||
e.category().name(),
|
||||
e.message()
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
ztu::logger::debug("% -> %", filename, shader_handle.shader_id);
|
||||
|
||||
capabilities.clear();
|
||||
capabilities.push_back(0);
|
||||
|
||||
auto specifiers_str = std::string_view(type_str.end() + 1, name.end()); // skip separator_char
|
||||
|
||||
while (not specifiers_str.empty())
|
||||
{
|
||||
const auto pos = specifiers_str.find(separator_char);
|
||||
auto specifier_str = specifiers_str.substr(0, pos);
|
||||
|
||||
if (pos == std::string_view::npos)
|
||||
{
|
||||
specifiers_str = std::string_view{};
|
||||
}
|
||||
else
|
||||
{
|
||||
specifiers_str = specifiers_str.substr(pos + 1); // skip separator_char
|
||||
}
|
||||
|
||||
if (specifier_str.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto optional = specifier_str.back() == optional_char;
|
||||
if (optional)
|
||||
{
|
||||
specifier_str = specifier_str.substr(0, specifier_str.size() - 1);
|
||||
}
|
||||
|
||||
const auto index_it = capability_indices.find(specifier_str);
|
||||
|
||||
auto capability_index = capability_indices.size();
|
||||
if (index_it == capability_indices.end())
|
||||
{
|
||||
capability_indices.emplace(specifier_str, capability_index);
|
||||
}
|
||||
else
|
||||
{
|
||||
capability_index = index_it->second;
|
||||
}
|
||||
|
||||
const auto capability_flag = ztu::u32{ 1 } << capability_index;
|
||||
|
||||
const auto capability_count = capabilities.size();
|
||||
|
||||
if (optional)
|
||||
{
|
||||
capabilities.resize(2 * capability_count);
|
||||
std::copy_n(capabilities.begin(), capability_count, capabilities.begin() + capability_count);
|
||||
}
|
||||
|
||||
for (std::size_t i{}; i != capability_count; ++i)
|
||||
{
|
||||
capabilities[i] |= capability_flag;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& capability : capabilities)
|
||||
{
|
||||
const auto program_capability_it = std::ranges::upper_bound(program_capabilities, capability);
|
||||
|
||||
auto program_index = program_capability_it - program_capabilities.begin();
|
||||
|
||||
if (
|
||||
program_capability_it == program_capabilities.begin() or
|
||||
*std::prev(program_capability_it) != capability
|
||||
) {
|
||||
program_capabilities.insert(program_capability_it, capability);
|
||||
programs.emplace(programs.begin() + program_index);
|
||||
}
|
||||
else
|
||||
{
|
||||
--program_index; // The element before the iterator matches.
|
||||
}
|
||||
|
||||
programs[program_index][shader_type_index] = shader_handle;
|
||||
}
|
||||
|
||||
++curr_shader_count;
|
||||
}
|
||||
|
||||
progress_lock.lock();
|
||||
|
||||
progress_title = "Linking programs...";
|
||||
|
||||
progress_lock.unlock();
|
||||
|
||||
// Remove any duplicates shader combinations.
|
||||
std::ranges::sort(
|
||||
programs,
|
||||
[](const auto& lhs, const auto& rhs) {
|
||||
return std::lexicographical_compare(
|
||||
lhs.begin(), lhs.end(),
|
||||
rhs.begin(), rhs.end(),
|
||||
[](const auto& a, const auto& b) {
|
||||
return a.shader_id < b.shader_id;
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
programs.erase(std::ranges::unique(programs).begin(), programs.end());
|
||||
|
||||
ztu::logger::debug("Linking % programs.", programs.size());
|
||||
|
||||
// create shader_program
|
||||
for (const auto& [vertex, geometry, fragment] : programs)
|
||||
{
|
||||
if (vertex.shader_id == 0 or fragment.shader_id == 0)
|
||||
{
|
||||
ztu::logger::warn(
|
||||
"Skipping program as the combination is unlikely to be used (vertex: % geometry: % fragment: %).",
|
||||
vertex.shader_id, geometry.shader_id, fragment.shader_id
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto program_handle = zgl::shader_program_handle{};
|
||||
if (const auto e = loader.build_shader_program(vertex, geometry, fragment, program_handle))
|
||||
{
|
||||
ztu::logger::error(
|
||||
"Error occurred while linking shader program: [%] %",
|
||||
e.category().name(),
|
||||
e.message()
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
ztu::logger::debug(
|
||||
"Linked (vertex: % geometry: % fragment: %) -> %",
|
||||
vertex.shader_id, geometry.shader_id, fragment.shader_id,
|
||||
program_handle.program_id
|
||||
);
|
||||
|
||||
|
||||
gl_resource_lock.lock();
|
||||
z3d.add_shader_program(asset_type, program_handle);
|
||||
gl_resource_lock.unlock();
|
||||
}
|
||||
|
||||
gl_resource_lock.lock();
|
||||
z3d.m_mesh_shader_program_lookup.print();
|
||||
gl_resource_lock.unlock();
|
||||
|
||||
loader.unload_shader_data();
|
||||
}
|
||||
}
|
||||
504
source/viewer/instance.cpp
Normal file
504
source/viewer/instance.cpp
Normal file
@@ -0,0 +1,504 @@
|
||||
#include "viewer/instance.hpp"
|
||||
#include "viewer/asset_loader.hpp"
|
||||
|
||||
#include "SFML/Window.hpp"
|
||||
#include "SFML/Graphics/Text.hpp"
|
||||
#include "SFML/Graphics/Shape.hpp"
|
||||
#include "SFML/Graphics/RectangleShape.hpp"
|
||||
#include "SFML/Graphics/Sprite.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <glm/gtx/string_cast.hpp>
|
||||
#include "glm/gtx/euler_angles.hpp"
|
||||
|
||||
namespace viewer
|
||||
{
|
||||
|
||||
instance::instance() :
|
||||
m_context_settings{ 24, 8, 2, 4, 6 },
|
||||
m_mesh_renderer(static_cast<int>(rendering::modes::mesh::count)),
|
||||
m_point_cloud_renderer(static_cast<int>(rendering::modes::point_cloud::count)),
|
||||
//m_camera(0.0f, 0.0f, 0.0f)
|
||||
m_camera(0.0f, -std::numbers::pi_v<float> / 2.0f, std::numbers::pi_v<float>)
|
||||
{
|
||||
}
|
||||
|
||||
std::error_code instance::init(std::string title)
|
||||
{
|
||||
m_title = std::move(title);
|
||||
|
||||
windowed(512, 344, true);
|
||||
|
||||
if (glewInit() != GLEW_OK)
|
||||
{
|
||||
return std::make_error_code(std::errc::not_supported);
|
||||
}
|
||||
|
||||
m_settings.lighting.point_light_direction = glm::normalize(m_settings.lighting.point_light_direction);
|
||||
|
||||
m_mesh_render_mode = rendering::modes::mesh::lit_faces;
|
||||
m_point_cloud_render_mode = rendering::modes::point_cloud::rainbow;
|
||||
|
||||
glEnable(GL_CULL_FACE);
|
||||
glCullFace(GL_BACK);
|
||||
glFrontFace(GL_CCW);
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthFunc(GL_LESS);
|
||||
glClearDepth(1.f);
|
||||
|
||||
set_background_color({ 0.0f, 0.0f, 0.0f, 0.0f });
|
||||
|
||||
const auto current_path = std::filesystem::current_path();
|
||||
const auto data_path = current_path / ".." / "data";
|
||||
|
||||
const auto font_filename = data_path / "fonts" / "JetBrainsMono_Medium.ttf";
|
||||
if (not m_font.loadFromFile(font_filename)) {
|
||||
ztu::logger::error("Could not open font file: %", font_filename);
|
||||
}
|
||||
|
||||
const auto image_dir = data_path / "images";
|
||||
|
||||
const auto logo_filename = image_dir/ "logo.png";
|
||||
if (not m_logo.loadFromFile(logo_filename)) {
|
||||
ztu::logger::error("Could not open image file: %", logo_filename);
|
||||
}
|
||||
|
||||
const auto spinner_filename = image_dir/ "spinner.png";
|
||||
if (not m_spinner.loadFromFile(spinner_filename)) {
|
||||
ztu::logger::error("Could not open image file: %", spinner_filename);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void instance::set_background_color(const glm::vec4& color)
|
||||
{
|
||||
glClearColor(color.r, color.g, color.b, color.a);
|
||||
}
|
||||
|
||||
std::optional<instance::asset_id> instance::add_mesh(
|
||||
const zgl::mesh_handle& mesh,
|
||||
const aabb& bounding_box,
|
||||
const components::mesh_vertex::flags mesh_components,
|
||||
const zgl::material_handle& material,
|
||||
const material_component::flags material_components
|
||||
) {
|
||||
const auto mesh_id = m_mesh_renderer.add(
|
||||
std::make_pair(mesh_components, material_components),
|
||||
mesh,
|
||||
bounding_box,
|
||||
glm::identity<zgl::model_matrix_handle>(),
|
||||
material,
|
||||
m_mesh_shader_program_lookup
|
||||
);
|
||||
|
||||
if (not mesh_id)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return asset_id(std::in_place_index_t<id_mesh_index>{}, *mesh_id);
|
||||
}
|
||||
|
||||
std::optional<instance::asset_id> instance::add_point_cloud(
|
||||
const zgl::point_cloud_handle& point_cloud,
|
||||
const aabb& bounding_box,
|
||||
const components::point_cloud_vertex::flags point_cloud_components
|
||||
) {
|
||||
const auto point_cloud_id = m_point_cloud_renderer.add(
|
||||
point_cloud_components,
|
||||
point_cloud,
|
||||
bounding_box,
|
||||
glm::identity<zgl::model_matrix_handle>(),
|
||||
m_point_cloud_shader_program_lookup
|
||||
);
|
||||
|
||||
if (not point_cloud_id)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return asset_id(std::in_place_index_t<id_point_cloud_index>{}, *point_cloud_id);
|
||||
}
|
||||
|
||||
void instance::add_shader_program(
|
||||
asset_types type, zgl::shader_program_handle shader_program_handle
|
||||
) {
|
||||
switch (type)
|
||||
{
|
||||
case asset_types::mesh:
|
||||
m_mesh_shader_program_lookup.add(shader_program_handle);
|
||||
break;
|
||||
case asset_types::point_cloud:
|
||||
m_point_cloud_shader_program_lookup.add(shader_program_handle);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool instance::remove(asset_id id)
|
||||
{
|
||||
switch (id.index()) {
|
||||
case id_mesh_index:
|
||||
return m_mesh_renderer.remove(std::get<id_mesh_index>(id));
|
||||
case id_point_cloud_index:
|
||||
return m_point_cloud_renderer.remove(std::get<id_point_cloud_index>(id));
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool instance::look_at(asset_id id)
|
||||
{
|
||||
auto bounding_box_opt = std::optional<aabb>{ std::nullopt };
|
||||
|
||||
switch (id.index()) {
|
||||
case id_mesh_index:
|
||||
bounding_box_opt = m_mesh_renderer.bounding_box(std::get<id_mesh_index>(id));
|
||||
break;
|
||||
case id_point_cloud_index:
|
||||
bounding_box_opt = m_point_cloud_renderer.bounding_box(std::get<id_point_cloud_index>(id));
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bounding_box_opt)
|
||||
{
|
||||
const auto& bounding_box = *bounding_box_opt;
|
||||
ztu::logger::debug("aabb: % %", glm::to_string(bounding_box.min), glm::to_string(bounding_box.max));
|
||||
//m_camera.look_at(bounding_box.max, bounding_box.min, m_view);
|
||||
|
||||
m_camera.look_at(bounding_box.center(), bounding_box.min, m_view);
|
||||
|
||||
ztu::logger::debug("pos: % front: % right: % up: % as: % fov: %",
|
||||
glm::to_string(m_view.position),
|
||||
glm::to_string(m_view.front),
|
||||
glm::to_string(m_view.right),
|
||||
glm::to_string(m_view.up),
|
||||
m_view.aspect_ratio,
|
||||
m_view.fov
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void instance::run_progress(
|
||||
std::mutex& lock,
|
||||
std::string& title,
|
||||
float& progress,
|
||||
const double fps
|
||||
) {
|
||||
namespace chr = std::chrono;
|
||||
using namespace std::chrono_literals;
|
||||
using clock = chr::high_resolution_clock;
|
||||
using duration_type = clock::duration;
|
||||
using floating_second = chr::duration<float>;
|
||||
|
||||
sf::RectangleShape bar_background, bar_foreground;
|
||||
sf::Text title_text, percent_text;
|
||||
sf::Texture logo_texture, spinner_texture;
|
||||
sf::Sprite logo_sprite, spinner_sprite;
|
||||
|
||||
sf::Color background_color = { 0, 0, 0, 255 };
|
||||
|
||||
constexpr auto font_size = 14;
|
||||
constexpr auto padding = 15;
|
||||
constexpr auto spinner_size = 10.0f;
|
||||
constexpr auto spinner_degrees_per_second = 270.0f;
|
||||
|
||||
title_text.setFont(m_font);
|
||||
title_text.setCharacterSize(font_size);
|
||||
percent_text.setFont(m_font);
|
||||
percent_text.setCharacterSize(font_size);
|
||||
|
||||
bar_foreground.setOutlineColor(sf::Color::Transparent);
|
||||
bar_foreground.setFillColor(sf::Color{ 107, 203, 119 });
|
||||
|
||||
bar_background.setOutlineColor(sf::Color::Transparent);
|
||||
bar_background.setFillColor(sf::Color{ 64, 170, 87 });
|
||||
|
||||
|
||||
const auto target_frame_time = std::chrono::duration_cast<duration_type>(1s) / fps;
|
||||
|
||||
std::stringstream percent_builder;
|
||||
percent_builder << std::setw(3) << std::setfill(' ');
|
||||
|
||||
logo_texture.loadFromImage(m_logo);
|
||||
logo_sprite.setTexture(logo_texture);
|
||||
const auto logo_scale = static_cast<float>(m_screen_size.x) / static_cast<float>(logo_texture.getSize().x);
|
||||
logo_sprite.scale({ logo_scale, logo_scale });
|
||||
logo_sprite.setOrigin(0, 0);
|
||||
logo_sprite.setPosition(0, 0);
|
||||
|
||||
const auto dim = static_cast<glm::vec2>(m_screen_size);
|
||||
const auto bar_dim = sf::Vector2f(dim.x - 2 * padding, 10.0f);
|
||||
const auto bar_pos = sf::Vector2f((dim.x - bar_dim.x) / 2.0f, dim.y - bar_dim.y - padding);
|
||||
bar_background.setPosition(bar_pos + sf::Vector2f{ 3, 3 });
|
||||
bar_background.setSize(bar_dim);
|
||||
bar_foreground.setPosition(bar_pos);
|
||||
|
||||
spinner_texture.loadFromImage(m_spinner);
|
||||
spinner_sprite.setTexture(spinner_texture);
|
||||
const auto spinner_dim = static_cast<float>(spinner_texture.getSize().y);
|
||||
const auto spinner_scale = spinner_size / spinner_dim;
|
||||
spinner_sprite.scale({ spinner_scale, spinner_scale });
|
||||
spinner_sprite.setOrigin(spinner_dim / 2.0f, spinner_dim / 2.0f);
|
||||
spinner_sprite.setPosition(padding + spinner_size / 2.0f, bar_pos.y - 0.5f * padding - spinner_size / 2.0f);
|
||||
|
||||
const auto title_text_pos = sf::Vector2f(1.5f * padding + spinner_size, bar_pos.y - 0.5f * padding - font_size);
|
||||
const auto percent_text_pos = sf::Vector2f(dim.x - padding, bar_pos.y - 0.5f * padding - font_size);
|
||||
title_text.setPosition(title_text_pos);
|
||||
percent_text.setPosition(percent_text_pos);
|
||||
|
||||
percent_text.setFillColor(sf::Color::White);
|
||||
title_text.setFillColor(sf::Color::White);
|
||||
|
||||
const auto start_time = clock::now();
|
||||
|
||||
while (true)
|
||||
{
|
||||
const auto frame_begin = clock::now();
|
||||
|
||||
const auto t = chr::duration_cast<floating_second>(frame_begin - start_time).count();
|
||||
|
||||
lock.lock();
|
||||
|
||||
auto curr_progress = progress;
|
||||
title_text.setString(title);
|
||||
|
||||
lock.unlock();
|
||||
|
||||
if (curr_progress == std::numeric_limits<float>::max())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
m_window.clear(background_color);
|
||||
|
||||
m_window.draw(logo_sprite);
|
||||
m_window.draw(bar_background);
|
||||
|
||||
bar_foreground.setSize(sf::Vector2f(curr_progress * bar_dim.x, bar_dim.y));
|
||||
m_window.draw(bar_foreground);
|
||||
|
||||
const auto spinner_angle = t * spinner_degrees_per_second;
|
||||
spinner_sprite.setRotation(spinner_angle);
|
||||
m_window.draw(spinner_sprite);
|
||||
|
||||
const auto percent = static_cast<int>(std::round(100.0f * curr_progress));
|
||||
|
||||
percent_builder.str("");
|
||||
percent_builder << percent << '%';
|
||||
|
||||
percent_text.setString(percent_builder.str());
|
||||
const auto percent_text_bounds = percent_text.getLocalBounds();
|
||||
percent_text.setOrigin(
|
||||
percent_text_bounds.left + percent_text_bounds.width,
|
||||
0
|
||||
);
|
||||
|
||||
m_window.draw(title_text);
|
||||
m_window.draw(percent_text);
|
||||
m_window.display();
|
||||
|
||||
const auto time_taken = clock::now() - frame_begin;
|
||||
if (time_taken < target_frame_time)
|
||||
{
|
||||
std::this_thread::sleep_for(target_frame_time - time_taken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void instance::run(std::mutex& gl_resource_lock, const double fps)
|
||||
{
|
||||
namespace chr = std::chrono;
|
||||
using namespace std::chrono_literals;
|
||||
using clock = chr::high_resolution_clock;
|
||||
using duration_type = clock::duration;
|
||||
using floating_second = chr::duration<double>;
|
||||
|
||||
const auto target_frame_time = std::chrono::duration_cast<duration_type>(1s) / fps;
|
||||
|
||||
auto frame_begin = clock::now();
|
||||
|
||||
m_mouse_locked = true;
|
||||
m_window.setMouseCursorVisible(false);
|
||||
|
||||
while (true)
|
||||
{
|
||||
auto prev_frame_begin = frame_begin;
|
||||
frame_begin = clock::now();
|
||||
|
||||
const auto dt = chr::duration_cast<floating_second>(frame_begin - prev_frame_begin);
|
||||
|
||||
if (not update(dt.count())) break;
|
||||
|
||||
gl_resource_lock.lock();
|
||||
render();
|
||||
gl_resource_lock.unlock();
|
||||
|
||||
/*ztu::logger::debug("pos: % front: % right: % up: % as: % fov: %",
|
||||
glm::to_string(m_view.position),
|
||||
glm::to_string(m_view.front),
|
||||
glm::to_string(m_view.right),
|
||||
glm::to_string(m_view.up),
|
||||
m_view.aspect_ratio,
|
||||
m_view.fov
|
||||
);*/
|
||||
|
||||
const auto time_taken = clock::now() - frame_begin;
|
||||
if (time_taken < target_frame_time)
|
||||
{
|
||||
std::this_thread::sleep_for(target_frame_time - time_taken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void instance::windowed(const unsigned int width, const unsigned int height, const bool decorations) {
|
||||
|
||||
m_window.create(
|
||||
sf::VideoMode(width, height),
|
||||
m_title,
|
||||
decorations ? sf::Style::Default : sf::Style::None,
|
||||
m_context_settings
|
||||
);
|
||||
|
||||
m_screen_size = { width, height };
|
||||
|
||||
m_view.aspect_ratio = static_cast<float>(width) / static_cast<float>(height);
|
||||
}
|
||||
|
||||
void instance::fullscreen()
|
||||
{
|
||||
m_window.create(
|
||||
sf::VideoMode(),
|
||||
m_title,
|
||||
sf::Style::Fullscreen,
|
||||
m_context_settings
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
void instance::size(const unsigned int width, const unsigned int height) {
|
||||
|
||||
m_window.setSize({ width, height });
|
||||
|
||||
m_screen_size = { width, height };
|
||||
|
||||
m_view.aspect_ratio = static_cast<float>(width) / static_cast<float>(height);
|
||||
}
|
||||
|
||||
bool instance::update(const double dt) {
|
||||
|
||||
//ztu::logger::log("pos: % dir: %", glm::to_string(m_view.position), glm::to_string(m_view.front));
|
||||
|
||||
auto mouse_pos_delta = glm::vec2{ 0, 0 };
|
||||
auto mouse_wheel_delta = 0.0f;
|
||||
|
||||
sf::Event event;
|
||||
while (m_window.pollEvent(event))
|
||||
{
|
||||
switch (event.type) {
|
||||
case sf::Event::Closed: [[unlikely]]
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case sf::Event::Resized:
|
||||
{
|
||||
const auto& [ width, height ] = event.size;
|
||||
glViewport(0, 0, width, height);
|
||||
m_screen_size = { width, height };
|
||||
break;
|
||||
}
|
||||
case sf::Event::MouseWheelMoved:
|
||||
{
|
||||
mouse_wheel_delta = m_settings.scroll_speed * event.mouseWheel.delta;
|
||||
break;
|
||||
}
|
||||
case sf::Event::KeyPressed:
|
||||
switch (event.key.code) {
|
||||
case sf::Keyboard::Escape:
|
||||
return false;
|
||||
case sf::Keyboard::Tab:
|
||||
m_window.setMouseCursorVisible(m_mouse_locked);
|
||||
m_mouse_locked = not m_mouse_locked;
|
||||
break;
|
||||
case sf::Keyboard::T:
|
||||
//renderIndex = (renderIndex + 1) % renderers.size();
|
||||
break;
|
||||
case sf::Keyboard::C:
|
||||
//renderIndex = (renderIndex + 1) % renderers.size();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_view.aspect_ratio = static_cast<float>(m_screen_size.x) / static_cast<float>(m_screen_size.y);
|
||||
|
||||
if (m_mouse_locked) [[likely]]
|
||||
{
|
||||
const auto screen_center = m_screen_size / 2;
|
||||
|
||||
const auto [ mouse_x, mouse_y ] = sf::Mouse::getPosition(m_window);
|
||||
sf::Mouse::setPosition({ screen_center.x, screen_center.y }, m_window);
|
||||
|
||||
const auto mouse_pixel_delta = glm::ivec2{ mouse_x, mouse_y } - screen_center;
|
||||
|
||||
mouse_pos_delta = m_settings.mouse_sensitivity * static_cast<glm::vec2>(mouse_pixel_delta);
|
||||
|
||||
m_camera.update(
|
||||
static_cast<float>(dt),
|
||||
mouse_pos_delta,
|
||||
mouse_wheel_delta,
|
||||
m_view
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void instance::render()
|
||||
{
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
const auto view_matrix = m_view.create_view_matrix();
|
||||
const auto projection_matrix = m_view.create_projection_matrix();
|
||||
|
||||
const auto vp_matrix = projection_matrix * view_matrix;
|
||||
|
||||
m_mesh_renderer.render(
|
||||
m_mesh_render_mode,
|
||||
vp_matrix,
|
||||
view_matrix,
|
||||
m_view.position,
|
||||
m_settings.lighting
|
||||
);
|
||||
|
||||
m_point_cloud_renderer.render(
|
||||
m_point_cloud_render_mode,
|
||||
vp_matrix,
|
||||
m_view.position,
|
||||
m_settings.lighting
|
||||
);
|
||||
|
||||
m_window.display();
|
||||
|
||||
//std::cout << "f\n";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user