#include "gltf_loader.h" #include "spdlog/spdlog.h" #include enum class SceneFileType { GLTF, GLB, UNKNOWN, }; bool load_gltf(const std::string_view path, iris::Scene &scene) { tinygltf::Model model; tinygltf::TinyGLTF loader; std::string error; std::string warning; SceneFileType file_type = [&path] { if (path.find_last_of(".") != std::string::npos) { std::string_view extension = path.substr(path.find_last_of(".") + 1); if (extension == "glb") { return SceneFileType::GLB; } else if (extension == "gltf") { return SceneFileType::GLTF; } } return SceneFileType::UNKNOWN; }(); switch (file_type) { case SceneFileType::GLTF: if (!loader.LoadASCIIFromFile(&model, &error, &warning, path.data())) { spdlog::error("Failed to load glTF file: {}", error); return false; } break; case SceneFileType::GLB: if (!loader.LoadBinaryFromFile(&model, &error, &warning, path.data())) { spdlog::error("Failed to load glTF file: {}", error); return false; } break; case SceneFileType::UNKNOWN: spdlog::error("Unknown file type: {}", path); return false; } spdlog::info("loaded glTF file {} has:\n" "{} accessors\n" "{} animations\n" "{} buffers\n" "{} bufferViews\n" "{} materials\n" "{} meshes\n" "{} nodes\n" "{} textures\n" "{} images\n" "{} skins\n" "{} samplers\n" "{} cameras\n" "{} scenes\n" "{} lights", path, model.accessors.size(), model.animations.size(), model.buffers.size(), model.bufferViews.size(), model.materials.size(), model.meshes.size(), model.nodes.size(), model.textures.size(), model.images.size(), model.skins.size(), model.samplers.size(), model.cameras.size(), model.scenes.size(), model.lights.size()); for (const auto &mesh : model.meshes) { iris::Mesh iris_mesh { .name = mesh.name, .vertices = {}, .indices = {}, }; for (const auto &primitive : mesh.primitives) { const auto &index_accessor = model.accessors[primitive.indices]; const auto &index_buffer_view = model.bufferViews[index_accessor.bufferView]; const auto &index_buffer = model.buffers[index_buffer_view.buffer]; const uint8_t *index_data_ptr = index_buffer.data.data() + index_buffer_view.byteOffset + index_accessor.byteOffset; auto extract_index = [&index_data_ptr]() -> T requires std::integral { const T *index_data = reinterpret_cast(index_data_ptr); index_data_ptr += sizeof(T); return *index_data; }; switch (index_accessor.componentType) { case TINYGLTF_COMPONENT_TYPE_BYTE: for (size_t i = 0; i < index_accessor.count; i++) { iris_mesh.indices.push_back(extract_index.operator()()); } break; case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE: for (size_t i = 0; i < index_accessor.count; i++) { iris_mesh.indices.push_back(extract_index.operator()()); } break; case TINYGLTF_COMPONENT_TYPE_SHORT: for (size_t i = 0; i < index_accessor.count; i++) { iris_mesh.indices.push_back(extract_index.operator()()); } break; case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT: for (size_t i = 0; i < index_accessor.count; i++) { iris_mesh.indices.push_back(extract_index.operator()()); } break; case TINYGLTF_COMPONENT_TYPE_INT: for (size_t i = 0; i < index_accessor.count; i++) { iris_mesh.indices.push_back(extract_index.operator()()); } break; case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT: for (size_t i = 0; i < index_accessor.count; i++) { iris_mesh.indices.push_back(extract_index.operator()()); } break; default: spdlog::error("Unsupported index component type: {}", index_accessor.componentType); return false; } switch (primitive.mode) { case TINYGLTF_MODE_TRIANGLES: { // All float ? for (const auto &[attrib_name, accessor_index] : primitive.attributes) { const auto &accessor = model.accessors[accessor_index]; const auto &buffer_view = model.bufferViews[accessor.bufferView]; const auto &buffer = model.buffers[buffer_view.buffer]; const float *data = reinterpret_cast(buffer.data.data() + buffer_view.byteOffset + accessor.byteOffset); // TODO: support other types. Currently only float3 is supported if (accessor.type != TINYGLTF_TYPE_VEC3 || accessor.componentType != TINYGLTF_COMPONENT_TYPE_FLOAT) { spdlog::error("Unsupported POSITION type: {}, {}", accessor.type, accessor.componentType); return false; } // spdlog::info("attribute: {}, count: {}", attrib_name, accessor.count); if (attrib_name == "POSITION") { spdlog::info("loading POSITION, count {}", accessor.count); for (size_t i = 0; i < accessor.count; i++) { iris_mesh.vertices.push_back(data[i * 3 + 0]); iris_mesh.vertices.push_back(data[i * 3 + 1]); iris_mesh.vertices.push_back(data[i * 3 + 2]); } } else if (attrib_name == "NORMAL") { spdlog::info("loading NORMAL, count {}", accessor.count); for (size_t i = 0; i < accessor.count; i++) { iris_mesh.vertices.push_back(data[i * 3 + 0]); iris_mesh.vertices.push_back(data[i * 3 + 1]); iris_mesh.vertices.push_back(data[i * 3 + 2]); } // TODO reorder normals correctly } else if (attrib_name == "TEXCOORD_0") { spdlog::info("loading TEXCOORD_0, count {}", accessor.count); for (size_t i = 0; i < accessor.count; i++) { iris_mesh.vertices.push_back(data[i * 2 + 0]); iris_mesh.vertices.push_back(data[i * 2 + 1]); } } else { spdlog::warn("Unsupported attribute: {}", attrib_name); } } } // TODO add support for other modes default: spdlog::error("Unsupported primitive mode: {}", primitive.mode); return false; } } scene.meshes.push_back(iris_mesh); } return true; }