188 lines
4.4 KiB
C++
188 lines
4.4 KiB
C++
#include "assets/file_parsers/stl_loader.hpp"
|
|
|
|
#include "util/binary_ifstream.hpp"
|
|
#include "util/unroll_bool_template.hpp"
|
|
#include "util/logger.hpp"
|
|
#include <execution>
|
|
|
|
|
|
std::error_code assets::stl_loader::prefetch(
|
|
path_id_lookups& lookups
|
|
) {
|
|
return {};
|
|
}
|
|
|
|
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)
|
|
{
|
|
if (const auto e = in.read_ieee754<std::endian::little>(vector[i))
|
|
{
|
|
return e;
|
|
}
|
|
}
|
|
return {};
|
|
};
|
|
|
|
|
|
void assets::stl_loader::parser_context::operator()(lookup_type::const_pointer entry) noexcept
|
|
{
|
|
const auto& [ filename, id ] = *entry;
|
|
|
|
auto in = binary_ifstream{};
|
|
|
|
if (const auto e = in.open(filename, true))
|
|
{
|
|
ztu::logger::error("Cannot open stl file %.", filename);
|
|
return;
|
|
}
|
|
|
|
reset();
|
|
|
|
auto header_bytes_left = static_cast<binary_ifstream::size_type>(80);
|
|
|
|
if (true) // TODO pedanatic
|
|
{
|
|
// 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 (const auto e = in.read(magic_bytes))
|
|
{
|
|
ztu::logger::error("Error while parsing stl magic bytes of %: %", filename, e.message());
|
|
return;
|
|
}
|
|
|
|
const auto magic_string = std::string_view(
|
|
reinterpret_cast<const char*>(magic_bytes.data()),
|
|
magic_bytes.size()
|
|
);
|
|
|
|
if (magic_string == ascii_magic_string)
|
|
{
|
|
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 (const auto e = in.skip(header_bytes_left))
|
|
{
|
|
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 (const auto e = in.read<std::endian::little>(expected_triangle_count))
|
|
{
|
|
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 vertex_parsing_error = std::error_code{};
|
|
|
|
for (std::uint32_t i{}; i != expected_triangle_count; ++i) {
|
|
|
|
mesh_vertex_components::normal normal;
|
|
if (((vertex_parsing_error = read_vector(in, normal))))
|
|
{
|
|
break;
|
|
}
|
|
|
|
auto triangle = z3d::index_triangle{};
|
|
|
|
for (auto& index : triangle)
|
|
{
|
|
mesh_vertex_components::position position;
|
|
if (((vertex_parsing_error = read_vector(in, normal))))
|
|
{
|
|
break;
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
m_mesh.triangles().emplace_back(triangle);
|
|
|
|
// Skip attribute bytes
|
|
if (((vertex_parsing_error = in.skip<std::uint16_t>())))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (vertex_parsing_error)
|
|
{
|
|
ztu::logger::error("Error while parsing stl vertices in %: %", filename, vertex_parsing_error.message());
|
|
return;
|
|
}
|
|
|
|
m_mesh.component_flags |= mesh_vertex_components::flags::position;
|
|
m_mesh.component_flags |= mesh_vertex_components::flags::normal;
|
|
}
|
|
|