124 lines
3.9 KiB
C++
124 lines
3.9 KiB
C++
#include "model.hpp"
|
|
|
|
#include <fastgltf/core.hpp>
|
|
#include <fastgltf/tools.hpp>
|
|
#include <fastgltf/util.hpp>
|
|
#include <ranges>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
namespace {
|
|
[[nodiscard]] auto load_mesh(fastgltf::Asset &asset, fastgltf::Mesh &mesh) {
|
|
auto meshes = std::vector<trb::game::mesh_node>();
|
|
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<glm::vec3>();
|
|
{
|
|
auto &position_accessor = asset.accessors[position_it->accessorIndex];
|
|
if (!position_accessor.bufferViewIndex.has_value())
|
|
continue;
|
|
|
|
fastgltf::iterateAccessorWithIndex<fastgltf::math::fvec3>(
|
|
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<std::uint32_t>(asset, index_accessor,
|
|
primitive.indices.data());
|
|
}
|
|
|
|
auto normals = std::vector<glm::vec3>();
|
|
{
|
|
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<fastgltf::math::fvec3>(
|
|
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<node>();
|
|
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
|