From 51b7c61c50b945dcc6c30c60d9bb9c4da637db17 Mon Sep 17 00:00:00 2001 From: Sylvain Doremus Date: Wed, 6 Sep 2023 22:31:47 +0200 Subject: [PATCH] Added support for EXT_mesh_gpu_instancing (#30) --- CMakeLists.txt | 2 +- include/fastgltf/parser.hpp | 7 ++++++- include/fastgltf/types.hpp | 31 ++++++++++++++++++++++++++----- src/fastgltf.cpp | 25 +++++++++++++++++++++++++ tests/extension_tests.cpp | 20 ++++++++++++++++++++ 5 files changed, 78 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b03f7f52..291edaeef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -132,7 +132,7 @@ install( if (FASTGLTF_ENABLE_TESTS OR FASTGLTF_ENABLE_EXAMPLES) # This is required so that Catch2 compiles with C++17, enabling various features we use in tests. if (NOT DEFINED CMAKE_CXX_STANDARD OR CMAKE_CXX_STANDARD STREQUAL "" OR CMAKE_CXX_STANDARD LESS 17) - set(CMAKE_CXX_STANDARD 17 CACHE BOOL "C++ standard" FORCE) + set(CMAKE_CXX_STANDARD "17" CACHE STRING "C++ standard" FORCE) endif() add_subdirectory(deps) diff --git a/include/fastgltf/parser.hpp b/include/fastgltf/parser.hpp index 49be5acae..b090e55a1 100644 --- a/include/fastgltf/parser.hpp +++ b/include/fastgltf/parser.hpp @@ -176,6 +176,9 @@ namespace fastgltf { // See https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_anisotropy/README.md KHR_materials_anisotropy = 1 << 18, + + // See https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Vendor/EXT_mesh_gpu_instancing/README.md + EXT_mesh_gpu_instancing = 1 << 19, }; // clang-format on @@ -251,6 +254,7 @@ namespace fastgltf { // String representations of glTF 2.0 extension identifiers. namespace extensions { + constexpr std::string_view EXT_mesh_gpu_instancing = "EXT_mesh_gpu_instancing"; constexpr std::string_view EXT_meshopt_compression = "EXT_meshopt_compression"; constexpr std::string_view EXT_texture_webp = "EXT_texture_webp"; constexpr std::string_view KHR_lights_punctual = "KHR_lights_punctual"; @@ -274,7 +278,8 @@ namespace fastgltf { // An array of pairs of string representations of extension identifiers and their respective enum // value used for enabling/disabling the loading of it. This also represents all extensions that // fastgltf supports and understands. - static constexpr std::array, 17> extensionStrings = {{ + static constexpr std::array, 18> extensionStrings = {{ + { extensions::EXT_mesh_gpu_instancing, Extensions::EXT_mesh_gpu_instancing }, { extensions::EXT_meshopt_compression, Extensions::EXT_meshopt_compression }, { extensions::EXT_texture_webp, Extensions::EXT_texture_webp }, { extensions::KHR_lights_punctual, Extensions::KHR_lights_punctual }, diff --git a/include/fastgltf/types.hpp b/include/fastgltf/types.hpp index df48aceec..1314ac220 100644 --- a/include/fastgltf/types.hpp +++ b/include/fastgltf/types.hpp @@ -1309,8 +1309,29 @@ namespace fastgltf { */ std::variant transform; + /** + * Only ever non-empty when EXT_mesh_gpu_instancing is enabled and used by the asset. + */ + std::pmr::vector> instancingAttributes; + std::pmr::string name; - }; + + [[nodiscard]] auto findInstancingAttribute(std::string_view name) noexcept { + for (decltype(instancingAttributes)::iterator it = instancingAttributes.begin(); it != instancingAttributes.end(); ++it) { + if (it->first == name) + return it; + } + return instancingAttributes.end(); + } + + [[nodiscard]] auto findInstancingAttribute(std::string_view name) const noexcept { + for (decltype(instancingAttributes)::const_iterator it = instancingAttributes.cbegin(); it != instancingAttributes.cend(); ++it) { + if (it->first == name) + return it; + } + return instancingAttributes.cend(); + } + }; struct Primitive { using attribute_type = std::pair; @@ -1325,7 +1346,7 @@ namespace fastgltf { Optional indicesAccessor; Optional materialIndex; - [[nodiscard]] auto findAttribute(std::string_view name) { + [[nodiscard]] auto findAttribute(std::string_view name) noexcept { for (decltype(attributes)::iterator it = attributes.begin(); it != attributes.end(); ++it) { if (it->first == name) return it; @@ -1333,7 +1354,7 @@ namespace fastgltf { return attributes.end(); } - [[nodiscard]] auto findAttribute(std::string_view name) const { + [[nodiscard]] auto findAttribute(std::string_view name) const noexcept { for (decltype(attributes)::const_iterator it = attributes.cbegin(); it != attributes.cend(); ++it) { if (it->first == name) return it; @@ -1341,7 +1362,7 @@ namespace fastgltf { return attributes.cend(); } - [[nodiscard]] auto findTargetAttribute(std::size_t targetIndex, std::string_view name) { + [[nodiscard]] auto findTargetAttribute(std::size_t targetIndex, std::string_view name) noexcept { auto& targetAttributes = targets[targetIndex]; for (std::remove_reference_t::iterator it = targetAttributes.begin(); it != targetAttributes.end(); ++it) { if (it->first == name) @@ -1350,7 +1371,7 @@ namespace fastgltf { return targetAttributes.end(); } - [[nodiscard]] auto findTargetAttribute(std::size_t targetIndex, std::string_view name) const { + [[nodiscard]] auto findTargetAttribute(std::size_t targetIndex, std::string_view name) const noexcept { const auto& targetAttributes = targets[targetIndex]; for (std::remove_reference_t::const_iterator it = targetAttributes.cbegin(); it != targetAttributes.cend(); ++it) { if (it->first == name) diff --git a/src/fastgltf.cpp b/src/fastgltf.cpp index 8668eb27d..53ba79eb3 100644 --- a/src/fastgltf.cpp +++ b/src/fastgltf.cpp @@ -2737,6 +2737,31 @@ fg::Error fg::Parser::parseNodes(simdjson::dom::array& nodes, Asset& asset) { node.lightIndex = static_cast(light); } } + + dom::object gpuInstancingObject; + if (extensionsObject[extensions::EXT_mesh_gpu_instancing].get_object().get(gpuInstancingObject) == SUCCESS) { + dom::object attributesObject; + if (gpuInstancingObject["attributes"].get_object().get(attributesObject) == SUCCESS) { + auto parseAttributes = [this](dom::object& object, decltype(node.instancingAttributes)& attributes) -> auto { + // We iterate through the JSON object and write each key/pair value into the + // attribute map. The keys are only validated in the validate() method. + attributes = decltype(node.instancingAttributes)(0, resourceAllocator.get()); + attributes.reserve(object.size()); + for (const auto& field : object) { + const auto key = field.key; + + std::uint64_t attributeIndex; + if (field.value.get_uint64().get(attributeIndex) != SUCCESS) { + return Error::InvalidGltf; + } + attributes.emplace_back( + std::make_pair(std::pmr::string(key, resourceAllocator.get()), static_cast(attributeIndex))); + } + return Error::None; + }; + parseAttributes(attributesObject, node.instancingAttributes); + } + } } std::string_view name; diff --git a/tests/extension_tests.cpp b/tests/extension_tests.cpp index 3d960d7b2..97f528acd 100644 --- a/tests/extension_tests.cpp +++ b/tests/extension_tests.cpp @@ -197,3 +197,23 @@ TEST_CASE("Test KHR_materials_clearcoat", "[gltf-loader]") { REQUIRE(materials[7].clearcoat->clearcoatRoughnessTexture->textureIndex == 2); REQUIRE(materials[7].clearcoat->clearcoatRoughnessTexture->texCoordIndex == 0); } + +TEST_CASE("Test EXT_mesh_gpu_instancing", "[gltf-loader]") { + auto simpleInstancingTest = sampleModels / "2.0" / "SimpleInstancing" / "glTF"; + fastgltf::GltfDataBuffer jsonData; + REQUIRE(jsonData.loadFromFile(simpleInstancingTest / "SimpleInstancing.gltf")); + + fastgltf::Parser parser(fastgltf::Extensions::EXT_mesh_gpu_instancing); + auto asset = parser.loadGLTF(&jsonData, simpleInstancingTest, fastgltf::Options::None, fastgltf::Category::Accessors | fastgltf::Category::Nodes); + REQUIRE(asset.error() == fastgltf::Error::None); + REQUIRE(fastgltf::validate(asset.get()) == fastgltf::Error::None); + + REQUIRE(asset->accessors.size() >= 6); + REQUIRE(asset->nodes.size() >= 1); + + auto& nodes = asset->nodes; + REQUIRE(nodes[0].instancingAttributes.size() == 3u); + REQUIRE(nodes[0].findInstancingAttribute("TRANSLATION") != nodes[0].instancingAttributes.cend()); + REQUIRE(nodes[0].findInstancingAttribute("SCALE") != nodes[0].instancingAttributes.cend()); + REQUIRE(nodes[0].findInstancingAttribute("ROTATION") != nodes[0].instancingAttributes.cend()); +}