From 20e4afd203301c65053e0185619b990066f72b2c Mon Sep 17 00:00:00 2001 From: sean Date: Fri, 27 Oct 2023 17:32:31 +0200 Subject: [PATCH] Add: GenerateMeshIndices option This also adds a default material into the gl_viewer example for meshes without a material. --- examples/gl_viewer/gl_viewer.cpp | 15 ++++++++-- include/fastgltf/parser.hpp | 9 ++++++ src/fastgltf.cpp | 49 ++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 3 deletions(-) diff --git a/examples/gl_viewer/gl_viewer.cpp b/examples/gl_viewer/gl_viewer.cpp index 66caadd10..5e70bc8ff 100644 --- a/examples/gl_viewer/gl_viewer.cpp +++ b/examples/gl_viewer/gl_viewer.cpp @@ -295,7 +295,8 @@ bool loadGltf(Viewer* viewer, std::string_view cPath) { fastgltf::Options::AllowDouble | fastgltf::Options::LoadGLBBuffers | fastgltf::Options::LoadExternalBuffers | - fastgltf::Options::LoadExternalImages; + fastgltf::Options::LoadExternalImages | + fastgltf::Options::GenerateMeshIndices; fastgltf::GltfDataBuffer data; data.loadFromFile(path); @@ -372,7 +373,7 @@ bool loadMesh(Viewer* viewer, fastgltf::Mesh& mesh) { primitive.primitiveType = fastgltf::to_underlying(it->type); primitive.vertexArray = vao; if (it->materialIndex.has_value()) { - primitive.materialUniformsIndex = it->materialIndex.value(); + primitive.materialUniformsIndex = it->materialIndex.value() + 1; // Adjust for default material auto& material = viewer->asset.materials[it->materialIndex.value()]; if (material.pbrData.baseColorTexture.has_value()) { auto& texture = viewer->asset.textures[material.pbrData.baseColorTexture->textureIndex]; @@ -380,7 +381,9 @@ bool loadMesh(Viewer* viewer, fastgltf::Mesh& mesh) { return false; primitive.albedoTexture = viewer->textures[texture.imageIndex.value()].texture; } - } + } else { + primitive.materialUniformsIndex = 0; + } { // Position @@ -648,6 +651,12 @@ int main(int argc, char* argv[]) { return -1; } + // Add a default material + auto& defaultMaterial = viewer.materials.emplace_back(); + defaultMaterial.baseColorFactor = glm::vec4(1.0f); + defaultMaterial.alphaCutoff = 0.0f; + defaultMaterial.flags = 0; + // We load images first. auto& asset = viewer.asset; for (auto& image : asset.images) { diff --git a/include/fastgltf/parser.hpp b/include/fastgltf/parser.hpp index d537c4057..2fe6c091c 100644 --- a/include/fastgltf/parser.hpp +++ b/include/fastgltf/parser.hpp @@ -250,6 +250,13 @@ namespace fastgltf { * to LoadExternalBuffers. */ LoadExternalImages = 1 << 7, + + /** + * Lets fastgltf generate indices for all mesh primitives without indices. This currently + * does not de-duplicate the vertices. This is entirely for compatibility and simplifying the + * loading process. + */ + GenerateMeshIndices = 1 << 8, }; // clang-format on @@ -652,6 +659,8 @@ namespace fastgltf { [[nodiscard]] auto decodeDataUri(URIView& uri) const noexcept -> Expected; [[nodiscard]] auto loadFileFromUri(URIView& uri) const noexcept -> Expected; + Error generateMeshIndices(Asset& asset) const; + Error parseAccessors(simdjson::dom::array& array, Asset& asset); Error parseAnimations(simdjson::dom::array& array, Asset& asset); Error parseBuffers(simdjson::dom::array& array, Asset& asset); diff --git a/src/fastgltf.cpp b/src/fastgltf.cpp index 051b0573c..82683201e 100644 --- a/src/fastgltf.cpp +++ b/src/fastgltf.cpp @@ -722,6 +722,51 @@ fg::MimeType fg::Parser::getMimeTypeFromString(std::string_view mime) { } } +fg::Error fg::Parser::generateMeshIndices(fastgltf::Asset& asset) const { + for (auto& mesh : asset.meshes) { + for (auto& primitive : mesh.primitives) { + if (primitive.indicesAccessor.has_value()) + continue; + + auto* positionAttribute = primitive.findAttribute("POSITION"); + if (positionAttribute == primitive.attributes.end()) { + return Error::InvalidGltf; + } + auto& positionAccessor = asset.accessors[positionAttribute->second]; + + sources::Vector generatedIndices; + generatedIndices.bytes.resize(positionAccessor.count * getElementByteSize(positionAccessor.type, positionAccessor.componentType)); + fastgltf::span indices { reinterpret_cast(generatedIndices.bytes.data()), + generatedIndices.bytes.size() / sizeof(std::uint32_t) }; + for (std::size_t i = 0; i < positionAccessor.count; ++i) { + indices[i] = i; + } + + auto bufferIdx = asset.buffers.size(); + + auto bufferViewIdx = asset.bufferViews.size(); + auto& bufferView = asset.bufferViews.emplace_back(); + bufferView.byteLength = generatedIndices.bytes.size(); + bufferView.bufferIndex = bufferIdx; + bufferView.byteOffset = 0; + + auto accessorIdx = asset.accessors.size(); + auto& accessor = asset.accessors.emplace_back(); + accessor.byteOffset = 0; + accessor.count = positionAccessor.count; + accessor.type = AccessorType::Scalar; + accessor.componentType = ComponentType::UnsignedInt; + accessor.normalized = false; + accessor.bufferViewIndex = bufferViewIdx; + + auto& buffer = asset.buffers.emplace_back(); + buffer.byteLength = generatedIndices.bytes.size(); + buffer.data = std::move(generatedIndices); + primitive.indicesAccessor = accessorIdx; + } + } +} + fg::Error fg::validate(const fastgltf::Asset& asset) { auto isExtensionUsed = [&used = asset.extensionsUsed](std::string_view extension) { for (const auto& extensionUsed : used) { @@ -1155,6 +1200,10 @@ fg::Expected fg::Parser::parse(simdjson::dom::object root, Category c asset.availableCategories = readCategories; + if (hasBit(options, Options::GenerateMeshIndices)) { + generateMeshIndices(asset); + } + return Expected(std::move(asset)); }