From 338b3e4f0ae0bb6d640cb8f87627b6b623199caa Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Tue, 9 Jan 2024 12:26:18 -0500 Subject: [PATCH] Add support in face index visitor for different primitive modes --- CHANGES.md | 5 + .../include/CesiumGltf/AccessorUtility.h | 83 ++++-- CesiumGltf/test/TestAccessorUtility.cpp | 248 +++++++++++++++--- 3 files changed, 282 insertions(+), 54 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 35250fe29..439bf2ae6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,11 @@ ### ? - ? + +##### Breaking Changes :mega: + +- `IndicesForFaceFromAccessor` now propertly supports `TRIANGLE_STRIP` and `TRIANGLE_FAN` modes. This requires the struct to be initialized with the correct primitive mode. + ##### Additions :tada: - Added conversions from `std::string` to other metadata types in `MetadataConversions`. This enables the same conversions as `std::string_view`, while allowing runtime engines to use `std::string` for convenience. diff --git a/CesiumGltf/include/CesiumGltf/AccessorUtility.h b/CesiumGltf/include/CesiumGltf/AccessorUtility.h index 5dfd88d83..24ea12ccc 100644 --- a/CesiumGltf/include/CesiumGltf/AccessorUtility.h +++ b/CesiumGltf/include/CesiumGltf/AccessorUtility.h @@ -2,6 +2,8 @@ #include "AccessorView.h" +#include + #include #include @@ -113,16 +115,41 @@ getIndexAccessorView(const Model& model, const MeshPrimitive& primitive); */ struct IndicesForFaceFromAccessor { std::array operator()(std::monostate) { - if (faceIndex < 0 || faceIndex >= vertexCount / 3) { + int64_t firstVertex = faceIndex; + int64_t numFaces = 0; + + switch (primitiveMode) { + case MeshPrimitive::Mode::TRIANGLE_STRIP: + numFaces = vertexCount - 2; + break; + case MeshPrimitive::Mode::TRIANGLE_FAN: + numFaces = vertexCount - 2; + firstVertex++; + break; + case MeshPrimitive::Mode::TRIANGLES: + numFaces = vertexCount / 3; + firstVertex *= 3; + break; + default: + // Unsupported primitive mode. + return {-1, -1, -1}; + } + + if (faceIndex < 0 || faceIndex >= numFaces) { return {-1, -1, -1}; } - const int64_t firstVertex = faceIndex * 3; std::array result; - for (int64_t i = 0; i < 3; i++) { - int64_t vertexIndex = firstVertex + i; - result[i] = vertexIndex < vertexCount ? vertexIndex : -1; + if (primitiveMode == MeshPrimitive::Mode::TRIANGLE_FAN) { + result[0] = 0; + result[1] = firstVertex < vertexCount ? firstVertex : -1; + result[2] = firstVertex + 1 < vertexCount ? firstVertex + 1 : -1; + } else { + for (int64_t i = 0; i < 3; i++) { + int64_t vertexIndex = firstVertex + i; + result[i] = vertexIndex < vertexCount ? vertexIndex : -1; + } } return result; @@ -130,18 +157,41 @@ struct IndicesForFaceFromAccessor { template std::array operator()(const AccessorView& value) { - if (faceIndex < 0 || faceIndex >= value.size() / 3) { + int64_t firstIndex = faceIndex; + int64_t numFaces = 0; + + switch (primitiveMode) { + case MeshPrimitive::Mode::TRIANGLE_STRIP: + numFaces = value.size() - 2; + break; + case MeshPrimitive::Mode::TRIANGLE_FAN: + numFaces = value.size() - 2; + firstIndex++; + break; + case MeshPrimitive::Mode::TRIANGLES: + numFaces = value.size() / 3; + firstIndex *= 3; + break; + default: + // Unsupported primitive mode. + return {-1, -1, -1}; + } + + if (faceIndex < 0 || faceIndex >= numFaces) { return {-1, -1, -1}; } - const int64_t firstVertex = faceIndex * 3; std::array result; - for (int64_t i = 0; i < 3; i++) { - int64_t vertexIndex = firstVertex + i; - result[i] = vertexIndex < value.size() - ? static_cast(value[vertexIndex]) - : -1; + if (primitiveMode == MeshPrimitive::Mode::TRIANGLE_FAN) { + result[0] = value[0]; + result[1] = firstIndex < value.size() ? value[firstIndex] : -1; + result[2] = firstIndex + 1 < value.size() ? value[firstIndex + 1] : -1; + } else { + for (int64_t i = 0; i < 3; i++) { + int64_t index = firstIndex + i; + result[i] = index < value.size() ? value[index] : -1; + } } return result; @@ -149,7 +199,8 @@ struct IndicesForFaceFromAccessor { int64_t faceIndex; int64_t vertexCount; -}; + int32_t primitiveMode; +}; // namespace CesiumGltf /** * Type definition for all kinds of texture coordinate (TEXCOORD_n) accessors. @@ -161,9 +212,9 @@ typedef std::variant< TexCoordAccessorType; /** - * Retrieves an accessor view for the specified texture coordinate set from the - * given glTF primitive and model. This verifies that the accessor is of a valid - * type. If not, the returned accessor view will be invalid., + * Retrieves an accessor view for the specified texture coordinate set from + * the given glTF primitive and model. This verifies that the accessor is of a + * valid type. If not, the returned accessor view will be invalid., */ TexCoordAccessorType getTexCoordAccessorView( const Model& model, diff --git a/CesiumGltf/test/TestAccessorUtility.cpp b/CesiumGltf/test/TestAccessorUtility.cpp index 80cd5367d..e94b62281 100644 --- a/CesiumGltf/test/TestAccessorUtility.cpp +++ b/CesiumGltf/test/TestAccessorUtility.cpp @@ -290,92 +290,225 @@ TEST_CASE("Test getIndexAccessorView") { } } -TEST_CASE("Test FaceVertexIndicesFromAccessor") { +TEST_CASE("Test IndicesForFaceFromAccessor") { Model model; - std::vector indices{0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 6, 7, 8}; int64_t vertexCount = 9; - Buffer& buffer = model.buffers.emplace_back(); - buffer.cesium.data.resize(indices.size() * sizeof(uint32_t)); - std::memcpy( - buffer.cesium.data.data(), - indices.data(), - buffer.cesium.data.size()); - buffer.byteLength = static_cast(buffer.cesium.data.size()); + // Triangle mode indices + std::vector + triangleIndices{0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 6, 7, 8}; + { + Buffer& buffer = model.buffers.emplace_back(); + buffer.cesium.data.resize(triangleIndices.size() * sizeof(uint32_t)); + std::memcpy( + buffer.cesium.data.data(), + triangleIndices.data(), + buffer.cesium.data.size()); + buffer.byteLength = static_cast(buffer.cesium.data.size()); - BufferView& bufferView = model.bufferViews.emplace_back(); - bufferView.buffer = 0; - bufferView.byteLength = buffer.byteLength; + BufferView& bufferView = model.bufferViews.emplace_back(); + bufferView.buffer = static_cast(model.buffers.size() - 1); + bufferView.byteLength = buffer.byteLength; - Accessor& accessor = model.accessors.emplace_back(); - accessor.bufferView = 0; - accessor.componentType = Accessor::ComponentType::UNSIGNED_INT; - accessor.type = Accessor::Type::SCALAR; - accessor.count = - bufferView.byteLength / static_cast(sizeof(uint32_t)); + Accessor& accessor = model.accessors.emplace_back(); + accessor.bufferView = static_cast(model.bufferViews.size() - 1); + accessor.componentType = Accessor::ComponentType::UNSIGNED_INT; + accessor.type = Accessor::Type::SCALAR; + accessor.count = + bufferView.byteLength / static_cast(sizeof(uint32_t)); + } + + // Triangle strip and fan indices + std::vector specialIndices{1, 2, 3, 4, 5, 6, 7, 8, 0}; + { + Buffer& buffer = model.buffers.emplace_back(); + buffer.cesium.data.resize(specialIndices.size() * sizeof(uint32_t)); + std::memcpy( + buffer.cesium.data.data(), + specialIndices.data(), + buffer.cesium.data.size()); + buffer.byteLength = static_cast(buffer.cesium.data.size()); + + BufferView& bufferView = model.bufferViews.emplace_back(); + bufferView.buffer = static_cast(model.buffers.size() - 1); + bufferView.byteLength = buffer.byteLength; + + Accessor& accessor = model.accessors.emplace_back(); + accessor.bufferView = static_cast(model.bufferViews.size() - 1); + accessor.componentType = Accessor::ComponentType::UNSIGNED_INT; + accessor.type = Accessor::Type::SCALAR; + accessor.count = + bufferView.byteLength / static_cast(sizeof(uint32_t)); + } SECTION("Handles invalid accessor") { + REQUIRE(model.accessors.size() > 0); // Wrong component type - IndexAccessorType indexAccessor = AccessorView(model, accessor); - auto indicesForFace = - std::visit(IndicesForFaceFromAccessor{0, vertexCount}, indexAccessor); + IndexAccessorType indexAccessor = + AccessorView(model, model.accessors[0]); + auto indicesForFace = std::visit( + IndicesForFaceFromAccessor{ + 0, + vertexCount, + MeshPrimitive::Mode::TRIANGLES}, + indexAccessor); for (int64_t index : indicesForFace) { REQUIRE(index == -1); } } SECTION("Handles invalid face index") { - IndexAccessorType indexAccessor = AccessorView(model, accessor); - auto indicesForFace = - std::visit(IndicesForFaceFromAccessor{-1, vertexCount}, indexAccessor); + REQUIRE(model.accessors.size() > 0); + IndexAccessorType indexAccessor = + AccessorView(model, model.accessors[0]); + auto indicesForFace = std::visit( + IndicesForFaceFromAccessor{ + -1, + vertexCount, + MeshPrimitive::Mode::TRIANGLES}, + indexAccessor); + for (int64_t index : indicesForFace) { + REQUIRE(index == -1); + } + + indicesForFace = std::visit( + IndicesForFaceFromAccessor{ + 10, + vertexCount, + MeshPrimitive::Mode::TRIANGLES}, + indexAccessor); + for (int64_t index : indicesForFace) { + REQUIRE(index == -1); + } + } + + SECTION("Handles invalid primitive modes") { + REQUIRE(model.accessors.size() > 0); + IndexAccessorType indexAccessor = + AccessorView(model, model.accessors[0]); + auto indicesForFace = std::visit( + IndicesForFaceFromAccessor{ + -1, + vertexCount, + MeshPrimitive::Mode::POINTS}, + indexAccessor); + for (int64_t index : indicesForFace) { + REQUIRE(index == -1); + } + + indicesForFace = std::visit( + IndicesForFaceFromAccessor{10, vertexCount, MeshPrimitive::Mode::LINES}, + indexAccessor); for (int64_t index : indicesForFace) { REQUIRE(index == -1); } - indicesForFace = - std::visit(IndicesForFaceFromAccessor{10, vertexCount}, indexAccessor); + indicesForFace = std::visit( + IndicesForFaceFromAccessor{ + 10, + vertexCount, + MeshPrimitive::Mode::LINE_LOOP}, + indexAccessor); for (int64_t index : indicesForFace) { REQUIRE(index == -1); } } - SECTION("Retrieves from valid accessor and face index") { - IndexAccessorType indexAccessor = AccessorView(model, accessor); - const size_t numFaces = indices.size() / 3; + SECTION("Retrieves from valid accessor and face index; triangles mode") { + REQUIRE(model.accessors.size() > 0); + IndexAccessorType indexAccessor = + AccessorView(model, model.accessors[0]); + const size_t numFaces = std::visit(CountFromAccessor{}, indexAccessor) / 3; + for (size_t i = 0; i < numFaces; i++) { auto indicesForFace = std::visit( - IndicesForFaceFromAccessor{static_cast(i), vertexCount}, + IndicesForFaceFromAccessor{ + static_cast(i), + vertexCount, + MeshPrimitive::Mode::TRIANGLES}, indexAccessor); for (size_t j = 0; j < indicesForFace.size(); j++) { - int64_t expected = static_cast(indices[i * 3 + j]); + int64_t expected = static_cast(triangleIndices[i * 3 + j]); REQUIRE(indicesForFace[j] == expected); } } } + SECTION("Retrieves from valid accessor and face index; triangle strip mode") { + REQUIRE(model.accessors.size() > 1); + IndexAccessorType indexAccessor = + AccessorView(model, model.accessors[1]); + const size_t numFaces = std::visit(CountFromAccessor{}, indexAccessor) - 2; + for (size_t i = 0; i < numFaces; i++) { + auto indicesForFace = std::visit( + IndicesForFaceFromAccessor{ + static_cast(i), + vertexCount, + MeshPrimitive::Mode::TRIANGLE_STRIP}, + indexAccessor); + + for (size_t j = 0; j < indicesForFace.size(); j++) { + int64_t expected = static_cast(specialIndices[i + j]); + REQUIRE(indicesForFace[j] == expected); + } + } + } + + SECTION("Retrieves from valid accessor and face index; triangle fan mode") { + REQUIRE(model.accessors.size() > 1); + IndexAccessorType indexAccessor = + AccessorView(model, model.accessors[1]); + const size_t numFaces = std::visit(CountFromAccessor{}, indexAccessor) - 2; + for (size_t i = 0; i < numFaces; i++) { + auto indicesForFace = std::visit( + IndicesForFaceFromAccessor{ + static_cast(i), + vertexCount, + MeshPrimitive::Mode::TRIANGLE_FAN}, + indexAccessor); + + REQUIRE(indicesForFace[0] == specialIndices[0]); + REQUIRE(indicesForFace[1] == specialIndices[i + 1]); + REQUIRE(indicesForFace[2] == specialIndices[i + 2]); + } + } + SECTION("Handles invalid face index for nonexistent accessor") { IndexAccessorType indexAccessor; - auto indicesForFace = - std::visit(IndicesForFaceFromAccessor{-1, vertexCount}, indexAccessor); + auto indicesForFace = std::visit( + IndicesForFaceFromAccessor{ + -1, + vertexCount, + MeshPrimitive::Mode::TRIANGLES}, + indexAccessor); for (int64_t index : indicesForFace) { REQUIRE(index == -1); } - indicesForFace = - std::visit(IndicesForFaceFromAccessor{10, vertexCount}, indexAccessor); + indicesForFace = std::visit( + IndicesForFaceFromAccessor{ + 10, + vertexCount, + MeshPrimitive::Mode::TRIANGLES}, + indexAccessor); for (int64_t index : indicesForFace) { REQUIRE(index == -1); } } - SECTION("Retrieves from valid face index for nonexistent accessor") { + SECTION("Retrieves from valid face index for nonexistent accessor; triangles " + "mode") { IndexAccessorType indexAccessor; const int64_t numFaces = vertexCount / 3; for (int64_t i = 0; i < numFaces; i++) { - auto indicesForFace = - std::visit(IndicesForFaceFromAccessor{i, vertexCount}, indexAccessor); + auto indicesForFace = std::visit( + IndicesForFaceFromAccessor{ + i, + vertexCount, + MeshPrimitive::Mode::TRIANGLES}, + indexAccessor); for (size_t j = 0; j < indicesForFace.size(); j++) { int64_t expected = i * 3 + static_cast(j); @@ -383,6 +516,45 @@ TEST_CASE("Test FaceVertexIndicesFromAccessor") { } } } + + SECTION("Retrieves from valid face index for nonexistent accessor; triangle " + "strip mode") { + IndexAccessorType indexAccessor; + const int64_t numFaces = vertexCount - 2; + + for (int64_t i = 0; i < numFaces; i++) { + auto indicesForFace = std::visit( + IndicesForFaceFromAccessor{ + i, + vertexCount, + MeshPrimitive::Mode::TRIANGLE_STRIP}, + indexAccessor); + + for (size_t j = 0; j < indicesForFace.size(); j++) { + int64_t expected = i + static_cast(j); + REQUIRE(indicesForFace[j] == expected); + } + } + } + + SECTION("Retrieves from valid face index for nonexistent accessor; triangle " + "fan mode") { + IndexAccessorType indexAccessor; + const int64_t numFaces = vertexCount - 2; + + for (int64_t i = 0; i < numFaces; i++) { + auto indicesForFace = std::visit( + IndicesForFaceFromAccessor{ + i, + vertexCount, + MeshPrimitive::Mode::TRIANGLE_FAN}, + indexAccessor); + + REQUIRE(indicesForFace[0] == 0); + REQUIRE(indicesForFace[1] == i + 1); + REQUIRE(indicesForFace[2] == i + 2); + } + } } TEST_CASE("Test getTexCoordAccessorView") {