diff --git a/games/devtest/mods/gltf/models/invalid_bufferview_bounds.gltf b/games/devtest/mods/gltf/models/invalid_bufferview_bounds.gltf new file mode 100644 index 000000000..2182861c6 --- /dev/null +++ b/games/devtest/mods/gltf/models/invalid_bufferview_bounds.gltf @@ -0,0 +1 @@ +{"scene":0,"scenes":[{"nodes":[0]}],"nodes":[{"mesh":0}],"meshes":[{"primitives":[{"attributes":{"POSITION":0}}]}],"buffers":[{"uri":"data:application/octet-stream;base64,AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAA","byteLength":36}],"bufferViews":[{"buffer":0,"byteOffset":1,"byteLength":36,"target":34962}],"accessors":[{"bufferView":0,"byteOffset":0,"componentType":5126,"count":3,"type":"VEC3","max":[1,1,0],"min":[0,0,0]}],"asset":{"version":"2.0"}} diff --git a/games/devtest/mods/gltf/models/simple_sparse_accessor.gltf b/games/devtest/mods/gltf/models/simple_sparse_accessor.gltf new file mode 100644 index 000000000..979896825 --- /dev/null +++ b/games/devtest/mods/gltf/models/simple_sparse_accessor.gltf @@ -0,0 +1 @@ +{"scene":0,"scenes":[{"nodes":[0]}],"nodes":[{"mesh":0}],"meshes":[{"primitives":[{"attributes":{"POSITION":1},"indices":0}]}],"buffers":[{"uri":"data:application/gltf-buffer;base64,AAAIAAcAAAABAAgAAQAJAAgAAQACAAkAAgAKAAkAAgADAAoAAwALAAoAAwAEAAsABAAMAAsABAAFAAwABQANAAwABQAGAA0AAAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAQAAAAAAAAAAAAABAQAAAAAAAAAAAAACAQAAAAAAAAAAAAACgQAAAAAAAAAAAAADAQAAAAAAAAAAAAAAAAAAAgD8AAAAAAACAPwAAgD8AAAAAAAAAQAAAgD8AAAAAAABAQAAAgD8AAAAAAACAQAAAgD8AAAAAAACgQAAAgD8AAAAAAADAQAAAgD8AAAAACAAKAAwAAAAAAIA/AAAAQAAAAAAAAEBAAABAQAAAAAAAAKBAAACAQAAAAAA=","byteLength":284}],"bufferViews":[{"buffer":0,"byteOffset":0,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":72,"byteLength":168},{"buffer":0,"byteOffset":240,"byteLength":6},{"buffer":0,"byteOffset":248,"byteLength":36}],"accessors":[{"bufferView":0,"byteOffset":0,"componentType":5123,"count":36,"type":"SCALAR","max":[13],"min":[0]},{"bufferView":1,"byteOffset":0,"componentType":5126,"count":14,"type":"VEC3","max":[6,4,0],"min":[0,0,0],"sparse":{"count":3,"indices":{"bufferView":2,"byteOffset":0,"componentType":5123},"values":{"bufferView":3,"byteOffset":0}}}],"asset":{"version":"2.0"}} diff --git a/src/unittest/test_irr_gltf_mesh_loader.cpp b/src/unittest/test_irr_gltf_mesh_loader.cpp index 193f0905c..155e1d5cf 100644 --- a/src/unittest/test_irr_gltf_mesh_loader.cpp +++ b/src/unittest/test_irr_gltf_mesh_loader.cpp @@ -44,6 +44,12 @@ SECTION("error cases") { SECTION("invalid JSON") { CHECK(loadMesh(invalid_model_path + "json_missing_brace.gltf") == nullptr); } + + // This is an example of something that should be validated by tiniergltf. + SECTION("invalid bufferview bounds") + { + CHECK(loadMesh(invalid_model_path + "invalid_bufferview_bounds.gltf") == nullptr); + } } SECTION("minimal triangle") { @@ -329,4 +335,33 @@ SECTION("snow man") { } } +// https://github.com/KhronosGroup/glTF-Sample-Models/tree/main/2.0/SimpleSparseAccessor +SECTION("simple sparse accessor") +{ + const auto mesh = loadMesh(model_path + "simple_sparse_accessor.gltf"); + REQUIRE(mesh != nullptr); + const auto *vertices = reinterpret_cast( + mesh->getMeshBuffer(0)->getVertices()); + const std::array expectedPositions = { + // Lower + v3f(0, 0, 0), + v3f(1, 0, 0), + v3f(2, 0, 0), + v3f(3, 0, 0), + v3f(4, 0, 0), + v3f(5, 0, 0), + v3f(6, 0, 0), + // Upper + v3f(0, 1, 0), + v3f(1, 2, 0), // overridden + v3f(2, 1, 0), + v3f(3, 3, 0), // overridden + v3f(4, 1, 0), + v3f(5, 4, 0), // overridden + v3f(6, 1, 0), + }; + for (std::size_t i = 0; i < expectedPositions.size(); ++i) + CHECK(vertices[i].Pos == expectedPositions[i]); +} + }