Ported the obj parser.
This commit is contained in:
@@ -3,159 +3,90 @@
|
||||
#include "util/binary_ifstream.hpp"
|
||||
#include "util/unroll_bool_template.hpp"
|
||||
#include "util/logger.hpp"
|
||||
#include <execution>
|
||||
|
||||
template<bool Normals>
|
||||
std::error_code read_body(
|
||||
binary_ifstream& in,
|
||||
const std::uint32_t expected_triangle_count,
|
||||
std::vector<mesh_vertex_components::position>& positions,
|
||||
std::vector<mesh_vertex_components::normal>& normals,
|
||||
std::vector<std::array<ztu::u32, 3>>& triangles
|
||||
|
||||
std::error_code assets::stl_loader::prefetch(
|
||||
path_id_lookups& lookups
|
||||
) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto read_vector = [&in](auto& vector) -> std::error_code
|
||||
std::error_code assets::stl_loader::load(
|
||||
path_id_lookups& lookups,
|
||||
data_stores& stores,
|
||||
bool pedantic
|
||||
) {
|
||||
m_path_buffer.clear();
|
||||
lookups.meshes.by_extension(".stl", m_path_buffer);
|
||||
|
||||
std::for_each(
|
||||
std::execution::parallel_unsequenced_policy{},
|
||||
m_path_buffer.begin(),
|
||||
m_path_buffer.end(),
|
||||
parser_context{
|
||||
lookups,
|
||||
stores,
|
||||
}
|
||||
);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
assets::stl_loader::parser_context::parser_context(
|
||||
path_id_lookups& m_id_lookups,
|
||||
data_stores& m_stores
|
||||
) :
|
||||
m_id_lookups{ &m_id_lookups },
|
||||
m_stores{ &m_stores }
|
||||
{
|
||||
constexpr auto expected_vertex_count = 8192;
|
||||
m_mesh.positions().reserve(expected_vertex_count);
|
||||
m_mesh.normals().reserve(expected_vertex_count);
|
||||
m_mesh.colors().reserve(expected_vertex_count);
|
||||
m_mesh.reflectances().reserve(expected_vertex_count);
|
||||
m_mesh.tex_coords().reserve(expected_vertex_count);
|
||||
m_mesh.triangles().reserve(2 * expected_vertex_count);
|
||||
}
|
||||
|
||||
void assets::stl_loader::parser_context::reset()
|
||||
{
|
||||
m_mesh.clear();
|
||||
m_vertex_index_lookup.clear();
|
||||
}
|
||||
|
||||
template<int L>
|
||||
std::error_code read_vector(binary_ifstream& in, z3d::vec<L, float>& vector)
|
||||
{
|
||||
for (int i{}; i != L; ++i)
|
||||
{
|
||||
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 = mesh_vertex_components::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 = mesh_vertex_components::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>())
|
||||
if (const auto e = in.read_ieee754<std::endian::little>(vector[i))
|
||||
{
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
std::error_code stl_loader::read_directory(
|
||||
const std::filesystem::path& path,
|
||||
std::vector<dynamic_mesh_data>& meshes,
|
||||
mesh_vertex_components::flags enabled_mesh_vertex_componentss,
|
||||
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);
|
||||
}
|
||||
void assets::stl_loader::parser_context::operator()(lookup_type::const_pointer entry) noexcept
|
||||
{
|
||||
const auto& [ filename, id ] = *entry;
|
||||
|
||||
for (const auto& file : fs::directory_iterator{ path / "frames" })
|
||||
auto in = binary_ifstream{};
|
||||
|
||||
if (const auto e = in.open(filename, true))
|
||||
{
|
||||
const auto& file_path = file.path();
|
||||
|
||||
if (file_path.extension() != ".stl")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (const auto e = read(
|
||||
file_path,
|
||||
meshes,
|
||||
enabled_mesh_vertex_componentss,
|
||||
materials,
|
||||
enabled_material_components,
|
||||
base_material_id,
|
||||
pedantic
|
||||
)) {
|
||||
ztu::logger::error(
|
||||
"Error while loading stl file '%': [%] %",
|
||||
file_path,
|
||||
e.category().name(),
|
||||
e.message()
|
||||
);
|
||||
}
|
||||
ztu::logger::error("Cannot open stl file %.", filename);
|
||||
return;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
std::error_code stl_loader::read(
|
||||
const std::filesystem::path& filename,
|
||||
std::vector<dynamic_mesh_data>& meshes,
|
||||
mesh_vertex_components::flags enabled_mesh_vertex_componentss,
|
||||
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;
|
||||
}
|
||||
reset();
|
||||
|
||||
auto header_bytes_left = static_cast<binary_ifstream::size_type>(80);
|
||||
|
||||
if (pedantic)
|
||||
if (true) // TODO pedanatic
|
||||
{
|
||||
// Check if ASCII file was provided, these start with a specific character sequence.
|
||||
|
||||
@@ -163,9 +94,10 @@ std::error_code stl_loader::read(
|
||||
|
||||
auto magic_bytes = std::array<binary_ifstream::char_type, ascii_magic_string.size()>{};
|
||||
|
||||
if ((error = in.read(magic_bytes)))
|
||||
if (const auto e = in.read(magic_bytes))
|
||||
{
|
||||
return error;
|
||||
ztu::logger::error("Error while parsing stl magic bytes of %: %", filename, e.message());
|
||||
return;
|
||||
}
|
||||
|
||||
const auto magic_string = std::string_view(
|
||||
@@ -175,79 +107,81 @@ std::error_code stl_loader::read(
|
||||
|
||||
if (magic_string == ascii_magic_string)
|
||||
{
|
||||
return std::make_error_code(std::errc::illegal_byte_sequence);
|
||||
ztu::logger::error("Error while parsing stl magic bytes %: ASCII stl files are not supported.", filename);
|
||||
return;
|
||||
}
|
||||
|
||||
header_bytes_left -= ascii_magic_string.size();
|
||||
}
|
||||
|
||||
// Ignore (rest of) header.
|
||||
if ((error = in.skip(header_bytes_left)))
|
||||
if (const auto e = in.skip(header_bytes_left))
|
||||
{
|
||||
return error;
|
||||
ztu::logger::error("Error while parsing stl header of %: %", filename, e.message());
|
||||
return;
|
||||
}
|
||||
|
||||
// Read number of bytes
|
||||
auto expected_triangle_count = std::uint32_t{};
|
||||
|
||||
if ((error = in.read<std::endian::little>(expected_triangle_count)))
|
||||
if (const auto e = in.read<std::endian::little>(expected_triangle_count))
|
||||
{
|
||||
return error;
|
||||
ztu::logger::error("Error while parsing stl triangle count %: %", filename, e.message());
|
||||
return;
|
||||
}
|
||||
|
||||
// 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();
|
||||
auto vertex_parsing_error = std::error_code{};
|
||||
|
||||
material_id = 0; // Set to default material
|
||||
for (std::uint32_t i{}; i != expected_triangle_count; ++i) {
|
||||
|
||||
positions.reserve(expected_triangle_count * 3);
|
||||
normals.reserve(expected_triangle_count);
|
||||
triangles.reserve(expected_triangle_count);
|
||||
mesh_vertex_components::normal normal;
|
||||
if (((vertex_parsing_error = read_vector(in, normal))))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
const auto normals_enabled = (
|
||||
(enabled_mesh_vertex_componentss & mesh_vertex_components::flags::normal) != mesh_vertex_components::flags::none
|
||||
);
|
||||
auto triangle = z3d::index_triangle{};
|
||||
|
||||
error = unroll_bool_function_template([&]<bool Normals>() {
|
||||
return read_body<Normals>(
|
||||
in,expected_triangle_count,
|
||||
positions,
|
||||
normals,
|
||||
triangles
|
||||
);
|
||||
}, normals_enabled);
|
||||
for (auto& index : triangle)
|
||||
{
|
||||
mesh_vertex_components::position position;
|
||||
if (((vertex_parsing_error = read_vector(in, normal))))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Free any unused reserved memory
|
||||
positions.shrink_to_fit();
|
||||
normals.shrink_to_fit();
|
||||
triangles.shrink_to_fit();
|
||||
if (const auto it = m_vertex_index_lookup.find(position); it != m_vertex_index_lookup.end())
|
||||
{
|
||||
index = it->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
index = m_mesh.positions().size();
|
||||
m_vertex_index_lookup.emplace_hint(it, position, index);
|
||||
m_mesh.positions().emplace_back(position);
|
||||
}
|
||||
}
|
||||
|
||||
if (error)
|
||||
{
|
||||
return error;
|
||||
m_mesh.triangles().emplace_back(triangle);
|
||||
|
||||
// Skip attribute bytes
|
||||
if (((vertex_parsing_error = in.skip<std::uint16_t>())))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ztu::logger::debug("Normal count: %", normals.size());
|
||||
|
||||
if (not positions.empty())
|
||||
if (vertex_parsing_error)
|
||||
{
|
||||
mesh.components() |= mesh_vertex_components::flags::position;
|
||||
ztu::logger::error("Error while parsing stl vertices in %: %", filename, vertex_parsing_error.message());
|
||||
return;
|
||||
}
|
||||
|
||||
if (not normals.empty())
|
||||
{
|
||||
ztu::logger::debug("Enabling normals!!!");
|
||||
mesh.components() |= mesh_vertex_components::flags::normal;
|
||||
}
|
||||
m_mesh.component_flags |= mesh_vertex_components::flags::position;
|
||||
m_mesh.component_flags |= mesh_vertex_components::flags::normal;
|
||||
}
|
||||
|
||||
meshes.emplace_back(std::move(mesh));
|
||||
|
||||
return {};
|
||||
}
|
||||
Reference in New Issue
Block a user