#include "model.hpp" #include #include #include #include #include #include namespace { [[nodiscard]] auto load_mesh(fastgltf::Asset &asset, fastgltf::Mesh &mesh) { auto meshes = std::vector(); meshes.reserve(mesh.primitives.size()); for (auto it = mesh.primitives.begin(); it != mesh.primitives.end(); ++it) { auto *position_it = it->findAttribute("POSITION"); assert(position_it != it->attributes.end()); assert(it->indicesAccessor.has_value()); auto *normal_it = it->findAttribute("NORMAL"); assert(normal_it != it->attributes.end()); const auto index = std::distance(mesh.primitives.begin(), it); auto &primitive = meshes.emplace_back(); if (it->type != fastgltf::PrimitiveType::Triangles) { throw std::runtime_error("only triangular meshes are supported"); } auto positions = std::vector(); { auto &position_accessor = asset.accessors[position_it->accessorIndex]; if (!position_accessor.bufferViewIndex.has_value()) continue; fastgltf::iterateAccessorWithIndex( asset, position_accessor, [&](fastgltf::math::fvec3 pos, std::size_t idx) { positions.emplace_back(pos.x(), pos.y(), pos.z()); }); } { auto &index_accessor = asset.accessors[it->indicesAccessor.value()]; if (!index_accessor.bufferViewIndex.has_value()) { throw std::runtime_error( "no index buffer was specified for mesh primitive"); } primitive.indices.resize(index_accessor.count); fastgltf::copyFromAccessor(asset, index_accessor, primitive.indices.data()); } auto normals = std::vector(); { auto &normal_accessor = asset.accessors[normal_it->accessorIndex]; if (!normal_accessor.bufferViewIndex.has_value()) { throw std::runtime_error( "no index buffer was specified for mesh primitive"); } fastgltf::iterateAccessorWithIndex( asset, normal_accessor, [&](fastgltf::math::fvec3 pos, std::size_t idx) { normals.emplace_back(pos.x(), pos.y(), pos.z()); }); } for (const auto [position, normal] : std::views::zip(positions, normals)) { primitive.vertices.push_back({ .position = position, .normal = normal, .uv = glm::vec2(), }); } } return meshes; } } // namespace namespace trb::game { scene load_glb(std::filesystem::path path) { constexpr auto gltfOptions = fastgltf::Options::DontRequireValidAssetMember | fastgltf::Options::AllowDouble | fastgltf::Options::LoadExternalBuffers | fastgltf::Options::LoadExternalImages | fastgltf::Options::GenerateMeshIndices; if (!std::filesystem::exists(path)) { throw std::runtime_error( std::format("path does not exist for asset {}", path.generic_string())); } auto parser = fastgltf::Parser(); auto file = fastgltf::GltfFileStream(path); auto maybe_asset = parser.loadGltfBinary(file, path.parent_path(), gltfOptions); if (!maybe_asset) { const auto error = maybe_asset.error(); throw std::runtime_error(std::format("failed to load asset {}", fastgltf::getErrorMessage(error))); } auto &asset = maybe_asset.get(); auto nodes = std::vector(); for (auto &in_mesh : asset.meshes) { for (auto &parsed_mesh : ::load_mesh(asset, in_mesh)) { nodes.push_back(std::move(parsed_mesh)); } } return { .nodes = nodes, .name = path.stem().generic_string(), }; } } // namespace trb::game