Files
tanks-reborn/source/game/model.cpp

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