Started obj port and gave up.

This commit is contained in:
zy4n
2025-03-31 21:41:24 +02:00
parent 0acfe36118
commit bc065bc657
20 changed files with 422 additions and 593 deletions

View File

@@ -5,16 +5,12 @@
#include <array>
#include "assets/components/mesh_vertex_components.hpp"
#include "assets/data_loaders/"
#include "util/logger.hpp"
#include "util/for_each.hpp"
#include "util/uix.hpp"
#include <set>
#include "util/line_parser.hpp"
namespace obj_loader_error
namespace assets::obj_loader_error
{
struct category : std::error_category
{
@@ -54,48 +50,328 @@ struct category : std::error_category
} // namespace mesh_loader_error
inline std::error_category& connector_error_category()
inline std::error_category& obj_loader_error_category()
{
static obj_loader_error::category category;
static assets::obj_loader_error::category category;
return category;
}
namespace obj_loader_error
namespace assets::obj_loader_error
{
inline std::error_code make_error_code(codes e)
{
return { static_cast<int>(e), connector_error_category() };
return { static_cast<int>(e), obj_loader_error_category() };
}
} // namespace mesh_loader_error
template<typename T, std::size_t Count>
std::errc parse_numeric_vector(std::string_view param, std::array<T, Count>& values)
assets::obj_loader::parser_context::parser_context(
const texture_id_lookup& texture_id_lookup,
material_id_lookup& material_id_lookup,
material_store& material_store,
store_type& store
) :
m_texture_id_lookup{ &texture_id_lookup },
m_material_id_lookup{ &material_id_lookup },
m_material_store{ &material_store },
m_store{ &store }
{
auto it = param.begin();
const auto end = param.end();
constexpr auto expected_vertex_count = 8192;
m_buffer.positions().reserve(expected_vertex_count);
m_buffer.normals().reserve(expected_vertex_count);
m_buffer.colors().reserve(expected_vertex_count);
m_buffer.reflectances().reserve(expected_vertex_count);
m_buffer.tex_coords().reserve(expected_vertex_count);
m_buffer.triangles().reserve(2 * expected_vertex_count);
for (auto& value : values)
m_read_buffer.positions().reserve(expected_vertex_count);
m_read_buffer.normals().reserve(expected_vertex_count);
m_read_buffer.colors().reserve(expected_vertex_count);
m_read_buffer.reflectances().reserve(expected_vertex_count);
m_read_buffer.tex_coords().reserve(expected_vertex_count);
m_read_buffer.triangles().reserve(2 * expected_vertex_count);
}
void assets::obj_loader::parser_context::reset()
{
m_buffer.clear();
m_read_buffer.clear();
vertex_ids.clear();
}
void assets::obj_loader::parser_context::operator()(lookup_type::const_pointer entry) noexcept
{
using obj_loader_error::codes;
using obj_loader_error::make_error_code;
namespace fs = std::filesystem;
reset();
auto path_buffer = fs::path{};
const auto base_dir = fs::canonical(fs::path(filename).parent_path());
// Buffers for storing the vertex component definitions.
auto& position_buffer = m_read_buffer.positions();
auto& normal_buffer = m_read_buffer.normals();
auto& tex_coord_buffer = m_read_buffer.tex_coords();
auto& positions = m_buffer.positions();
auto& normals = m_buffer.normals();
auto& tex_coords = m_buffer.tex_coords();
auto& triangles = m_buffer.triangles();
const auto& [ filename, id ] = *entry;
auto in = std::ifstream{ filename };
if (not in.is_open())
{
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.
ztu::logger::warn("Cannot open obj file %.", filename);
return;
}
return {};
};
const auto push_mesh = [&](const bool clear_read_buffer = false)
{
if (not triangles.empty())
{
ztu::logger::debug("Parsed % positions.", positions.size());
ztu::logger::debug("Parsed % normals.", normals.size());
ztu::logger::debug("Parsed % tex_coords.", tex_coords.size());
ztu::logger::debug("Parsed % triangles.", triangles.size());
void obj_loader::find_materials(
// Copy buffer into store and keep capacity.
m_store->insert(id, m_buffer);
}
if (clear_read_buffer)
{
m_read_buffer.clear();
}
m_buffer.clear();
vertex_ids.clear();
};
const auto find_or_push_vertex = [&](const z3d::index_triangle& vertex) -> z3d::vertex_index
{
auto indexed_vid = indexed_vertex_type{
.vertex = vertex,
.buffer_index = static_cast<z3d::vertex_index>(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 index is out of range, push default constructed value.
// Not ideal, but better than out of range indices.
auto& position = positions.emplace_back();
if (position_index < position_buffer.size())
{
position = position_buffer[position_index];
}
auto& normal = normals.emplace_back();
if (normal_index < normal_buffer.size())
{
normal = normal_buffer[normal_index];
}
auto& tex_coord = tex_coords.emplace_back();
if (tex_coord_index < tex_coord_buffer.size())
{
tex_coord = tex_coord_buffer[tex_coord_index];
}
}
return id_it->buffer_index;
};
const material_library_data* curr_material_library{};
const auto ec = ztu::parse_lines<codes>(
in,
pedantic,
make_line_parser("v ", ztu::is_repeating, [&](const auto& param)
{
mesh_vertex_components::position position;
if (parse_numeric_vector(param, position) != std::errc{}) [[unlikely]]
{
return codes::malformed_vertex;
}
position_buffer.push_back(position);
return codes::ok;
}),
make_line_parser("vt ", ztu::is_repeating, [&](const auto& param)
{
mesh_vertex_components::tex_coord coord;
if (parse_numeric_vector(param, coord) != std::errc{}) [[unlikely]]
{
return codes::malformed_texture_coordinate;
}
tex_coord_buffer.push_back(coord);
return codes::ok;
}),
make_line_parser("vn ", ztu::is_repeating, [&](const auto& param)
{
mesh_vertex_components::normal normal;
if (parse_numeric_vector(param, normal) != std::errc{}) [[unlikely]]
{
return codes::malformed_normal;
}
normal_buffer.push_back(normal);
return codes::ok;
}),
make_line_parser("o ", ztu::is_not_repeating, [&](const auto&)
{
push_mesh(); // Name is currently ignored
return codes::ok;
}),
make_line_parser("f ", ztu::is_repeating, [&](const auto& param)
{
const auto begin = param.begin().base();
const auto end = param.end().base();
auto vertex = z3d::index_triangle{};
z3d::vertex_index 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::malformed_face;
}
--component_index; // Convert to zero based index.
it = ptr;
if (it == end or *it != '/')
{
break;
}
++it;
}
++vertex_count;
if (it != end and *it != ' ') [[unlikely]]
{
return codes::malformed_face;
}
const auto curr_index = find_or_push_vertex(vertex);
if (vertex_count >= 3)
{
triangles.emplace_back() = {
first_index,
prev_index,
curr_index
};
}
else if (vertex_count == 1)
{
first_index = curr_index;
}
prev_index = curr_index;
}
return codes::ok;
}),
make_line_parser("usemtl ", ztu::is_not_repeating, [&](const auto& param)
{
push_mesh(false);
if (not curr_material_library) [[unlikely]]
{
return codes::use_material_without_material_library;
}
const auto material_id_it = curr_material_library->find(param);
if (material_id_it == curr_material_library->end()) [[unlikely]]
{
return codes::unknown_material_name;
}
m_buffer.material() = material_id_it->second;
return codes::ok;
}),
make_line_parser("mtllib ", ztu::is_not_repeating, [&](const auto& param)
{
path_buffer.assign(param);
if (path_buffer.is_relative())
{
path_buffer = base_dir;
path_buffer /= param;
}
const auto material_library_id_it = m_id_lookups->material_libraries.find(path_buffer);
if (material_library_id_it != m_id_lookups->material_libraries.end()) [[likely]]
{
const auto material_library_id = material_library_id_it->second;
const auto [ it, found ] = m_stores->material_libraries.find(material_library_id);
if (found)
{
curr_material_library = &(it->second);
}
else
{
// TODO ALARM!!!
}
}
else [[unlikely]]
{
ztu::logger::warn(
"Could not find a matching material library with path '%'. Proceeding with default material.",
param
);
curr_material_library = nullptr;
}
})
);
if (ec != codes::ok)
{
const auto e = make_error_code(ec);
ztu::logger::error("Error while parsing obj file %: %", filename, e.message());
}
push_mesh();
}
void assets::obj_loader::find_materials(
std::span<char> buffer,
std::filesystem::path& path_buffer,
const std::filesystem::path& base_directory,
@@ -162,13 +438,13 @@ void obj_loader::find_materials(
}
std::error_code obj_loader::prefetch(
std::error_code assets::obj_loader::prefetch(
const file_dir_list& paths,
prefetch_queue& queue
) {
namespace fs = std::filesystem;
using obj_loader_error::codes;
using obj_loader_error::make_error_code;
using assets::obj_loader_error::codes;
using assets::obj_loader_error::make_error_code;
auto buffer = std::vector<char>(8 * 1024, '\0');
@@ -218,7 +494,7 @@ std::error_code obj_loader::prefetch(
return {};
}
std::error_code obj_loader::load(
std::error_code assets::obj_loader::load(
dynamic_mesh_buffer& buffer,
const file_dir_list& paths,
prefetch_lookup& id_lookup,
@@ -299,263 +575,28 @@ std::error_code obj_loader::load(
return {};
}
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();
const auto end = param.end();
std::error_code obj_loader::parse_file(
dynamic_mesh_buffer& read_buffer,
dynamic_mesh_buffer& mesh_buffer,
std::filesystem::path& path_buffer,
const std::filesystem::path& base_directory,
std::set<indexed_vertex_type>& vertex_ids,
std::ifstream& in,
prefetch_lookup& id_lookup,
dynamic_shader_source_store& store,
bool pedantic
) {
using obj_loader_error::codes;
using obj_loader_error::make_error_code;
read_buffer.clear();
mesh_buffer.clear();
vertex_ids.clear();
// Buffers for storing the vertex component definitions.
auto& position_buffer = read_buffer.positions();
auto& normal_buffer = read_buffer.normals();
auto& tex_coord_buffer = read_buffer.tex_coords();
auto& positions = mesh_buffer.positions();
auto& normals = mesh_buffer.normals();
auto& tex_coords = mesh_buffer.tex_coords();
auto& triangles = mesh_buffer.triangles();
const auto push_mesh = [&](const bool clear_read_buffer = false)
for (auto& value : values)
{
if (not triangles.empty())
if (it >= end)
{
ztu::logger::debug("Parsed % positions.", positions.size());
ztu::logger::debug("Parsed % normals.", normals.size());
ztu::logger::debug("Parsed % tex_coords.", tex_coords.size());
ztu::logger::debug("Parsed % triangles.", triangles.size());
// Copy buffer into store and keep capacity.
store.meshes.add(mesh_buffer);
return std::errc::invalid_argument;
}
if (clear_read_buffer)
const auto [ptr, ec] = std::from_chars(it, end, value);
if (ec != std::errc{})
{
read_buffer.clear();
return ec;
}
mesh_buffer.clear();
vertex_ids.clear();
};
const auto find_or_push_vertex = [&](const vertex_type& vertex) -> index_type
{
auto indexed_vid = indexed_vertex_type{
.vertex = vertex,
.buffer_index = static_cast<index_type>(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 index is out of range, push default constructed value.
// Not ideal, but better than out of range indices.
auto& position = positions.emplace_back();
if (position_index < position_buffer.size())
{
position = position_buffer[position_index];
}
auto& normal = normals.emplace_back();
if (normal_index < normal_buffer.size())
{
normal = normal_buffer[normal_index];
}
auto& tex_coord = tex_coords.emplace_back();
if (tex_coord_index < tex_coord_buffer.size())
{
tex_coord = tex_coord_buffer[tex_coord_index];
}
}
return id_it->buffer_index;
};
auto curr_material_library_it = dynamic_material_library_store::iterator_type{};
auto has_material_library = false;
const auto ec = ztu::parse_lines<codes>(
in,
pedantic,
make_line_parser("v ", ztu::is_repeating, [&](const auto& param)
{
mesh_vertex_components::position position;
if (parse_numeric_vector(param, position) != std::errc{}) [[unlikely]]
{
return codes::malformed_vertex;
}
position_buffer.push_back(position);
return codes::ok;
}),
make_line_parser("vt ", ztu::is_repeating, [&](const auto& param)
{
mesh_vertex_components::tex_coord coord;
if (parse_numeric_vector(param, coord) != std::errc{}) [[unlikely]]
{
return codes::malformed_texture_coordinate;
}
tex_coord_buffer.push_back(coord);
return codes::ok;
}),
make_line_parser("vn ", ztu::is_repeating, [&](const auto& param)
{
mesh_vertex_components::normal normal;
if (parse_numeric_vector(param, normal) != std::errc{}) [[unlikely]]
{
return codes::malformed_normal;
}
normal_buffer.push_back(normal);
return codes::ok;
}),
make_line_parser("o ", ztu::is_not_repeating, [&](const auto&)
{
push_mesh(); // Name is currently ignored
return codes::ok;
}),
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{};
index_type 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::malformed_face;
}
--component_index; // Convert to zero based index.
it = ptr;
if (it == end or *it != '/')
{
break;
}
++it;
}
++vertex_count;
if (it != end and *it != ' ') [[unlikely]]
{
return codes::malformed_face;
}
const auto curr_index = find_or_push_vertex(vertex);
if (vertex_count >= 3)
{
auto& triangle = 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;
}),
make_line_parser("usemtl ", ztu::is_not_repeating, [&](const auto& param)
{
push_mesh(false);
if (not has_material_library) [[unlikely]]
{
return codes::use_material_without_material_library;
}
const auto material_id_it = curr_material_library_it->find(param);
if (material_id_it == curr_material_library_it->end()) [[unlikely]]
{
return codes::unknown_material_name;
}
mesh_buffer.material_id() = material_id_it->second;
return codes::ok;
}),
make_line_parser("mtllib ", ztu::is_not_repeating, [&](const auto& param)
{
path_buffer.assign(param);
if (path_buffer.is_relative())
{
path_buffer = base_directory;
path_buffer /= param; // TODO Doesn't thi allocate an extra path?!?
}
const auto material_library_id_it = id_lookup.material_libraries.find(path_buffer);
if (material_library_id_it != id_lookup.material_libraries.end()) [[likely]]
{
const auto material_library_id = material_library_id_it->second;
std::tie(curr_material_library_it, has_material_library) = store.material_libraries.find(material_library_id);
}
else [[unlikely]]
{
ztu::logger::warn(
"Could not find a matching material library with path '%'. Proceeding with default material.",
param
);
has_material_library = false;
}
})
);
if (ec != codes::ok)
{
return make_error_code(ec);
it = ptr + 1; // Skip space in between components.
}
push_mesh();
return {};
}
};