Skip to content

Commit

Permalink
Added support for EXT_mesh_gpu_instancing (#30)
Browse files Browse the repository at this point in the history
  • Loading branch information
DragonJoker authored Sep 6, 2023
1 parent ee5dd89 commit 51b7c61
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 7 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
7 changes: 6 additions & 1 deletion include/fastgltf/parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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";
Expand All @@ -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<std::pair<std::string_view, Extensions>, 17> extensionStrings = {{
static constexpr std::array<std::pair<std::string_view, Extensions>, 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 },
Expand Down
31 changes: 26 additions & 5 deletions include/fastgltf/types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1309,8 +1309,29 @@ namespace fastgltf {
*/
std::variant<TRS, TransformMatrix> transform;

/**
* Only ever non-empty when EXT_mesh_gpu_instancing is enabled and used by the asset.
*/
std::pmr::vector<std::pair<std::pmr::string, std::size_t>> 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<std::pmr::string, std::size_t>;
Expand All @@ -1325,23 +1346,23 @@ namespace fastgltf {
Optional<std::size_t> indicesAccessor;
Optional<std::size_t> 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;
}
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;
}
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<decltype(targetAttributes)>::iterator it = targetAttributes.begin(); it != targetAttributes.end(); ++it) {
if (it->first == name)
Expand All @@ -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<decltype(targetAttributes)>::const_iterator it = targetAttributes.cbegin(); it != targetAttributes.cend(); ++it) {
if (it->first == name)
Expand Down
25 changes: 25 additions & 0 deletions src/fastgltf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2737,6 +2737,31 @@ fg::Error fg::Parser::parseNodes(simdjson::dom::array& nodes, Asset& asset) {
node.lightIndex = static_cast<std::size_t>(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<std::size_t>(attributeIndex)));
}
return Error::None;
};
parseAttributes(attributesObject, node.instancingAttributes);
}
}
}

std::string_view name;
Expand Down
20 changes: 20 additions & 0 deletions tests/extension_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}

0 comments on commit 51b7c61

Please sign in to comment.