diff --git a/Build/macOSPackageRoot/packages.config b/Build/macOSPackageRoot/packages.config index 43bb3c1..2bae673 100644 --- a/Build/macOSPackageRoot/packages.config +++ b/Build/macOSPackageRoot/packages.config @@ -1,4 +1,4 @@  - - \ No newline at end of file + + diff --git a/GLTFSDK.Test/Source/MeshPrimitiveUtilsTests.cpp b/GLTFSDK.Test/Source/MeshPrimitiveUtilsTests.cpp index bc60b03..da1f724 100644 --- a/GLTFSDK.Test/Source/MeshPrimitiveUtilsTests.cpp +++ b/GLTFSDK.Test/Source/MeshPrimitiveUtilsTests.cpp @@ -1323,6 +1323,392 @@ namespace Microsoft Assert::AreEqual(outputIndices[262138], 131069U); Assert::AreEqual(outputIndices[262139], 0U); } + + GLTFSDK_TEST_METHOD(MeshPrimitiveUtilsTests, MeshPrimitiveUtils_Test_SerializeTriangulatedIndices16_TriangleStrip) + { + std::vector triangulatedIndices = + { + 0, 3, 1, + 3, 2, 1, + 1, 2, 4, + 2, 5, 4 + }; + + auto outputIndices = MeshPrimitiveUtils::ReverseTriangulateIndices16(triangulatedIndices, MESH_TRIANGLE_STRIP); + + std::vector expectedIndices = + { + 0, 3, 1, 2, 4, 5 + }; + + AreEqual(expectedIndices, outputIndices); + } + + GLTFSDK_TEST_METHOD(MeshPrimitiveUtilsTests, MeshPrimitiveUtils_Test_SerializeTriangulatedIndices16_TriangleFan) + { + std::vector triangulatedIndices = + { + 5, 2, 0, + 5, 0, 1, + 5, 1, 4, + 5, 4, 3 + }; + + auto outputIndices = MeshPrimitiveUtils::ReverseTriangulateIndices16(triangulatedIndices, MESH_TRIANGLE_FAN); + + std::vector expectedIndices = + { + 5, 2, 0, 1, 4, 3 + }; + + AreEqual(expectedIndices, outputIndices); + } + + GLTFSDK_TEST_METHOD(MeshPrimitiveUtilsTests, MeshPrimitiveUtils_Test_SerializeTriangulatedIndices32_TriangleStrip) + { + std::vector triangulatedIndices = + { + 0, 3, 1, + 3, 2, 1, + 1, 2, 4, + 2, 5, 4 + }; + + auto outputIndices = MeshPrimitiveUtils::ReverseTriangulateIndices32(triangulatedIndices, MESH_TRIANGLE_STRIP); + + std::vector expectedIndices = + { + 0, 3, 1, 2, 4, 5 + }; + + AreEqual(expectedIndices, outputIndices); + } + + GLTFSDK_TEST_METHOD(MeshPrimitiveUtilsTests, MeshPrimitiveUtils_Test_SerializeTriangulatedIndices32_TriangleFan) + { + std::vector triangulatedIndices = + { + 5, 2, 0, + 5, 0, 1, + 5, 1, 4, + 5, 4, 3 + }; + + auto outputIndices = MeshPrimitiveUtils::ReverseTriangulateIndices32(triangulatedIndices, MESH_TRIANGLE_FAN); + + std::vector expectedIndices = + { + 5, 2, 0, 1, 4, 3 + }; + + AreEqual(expectedIndices, outputIndices); + } + + GLTFSDK_TEST_METHOD(MeshPrimitiveUtilsTests, MeshPrimitiveUtils_Test_SerializeSegmentedIndices16_LineStrip) + { + std::vector segmentedIndices = + { + 4, 2, + 2, 1, + 1, 3, + 3, 0 + }; + + auto outputIndices = MeshPrimitiveUtils::ReverseSegmentIndices16(segmentedIndices, MESH_LINE_STRIP); + + std::vector expectedIndices = + { + 4, 2, 1, 3, 0 + }; + + AreEqual(expectedIndices, outputIndices); + } + + GLTFSDK_TEST_METHOD(MeshPrimitiveUtilsTests, MeshPrimitiveUtils_Test_SerializeSegmentedIndices16_LineLoop) + { + std::vector segmentedIndices = + { + 4, 2, + 2, 1, + 1, 3, + 3, 0, + 0, 4 + }; + + auto outputIndices = MeshPrimitiveUtils::ReverseSegmentIndices16(segmentedIndices, MESH_LINE_LOOP); + + std::vector expectedIndices = + { + 4, 2, 1, 3, 0 + }; + + AreEqual(expectedIndices, outputIndices); + } + + GLTFSDK_TEST_METHOD(MeshPrimitiveUtilsTests, MeshPrimitiveUtils_Test_SerializeSegmentedIndices32_LineStrip) + { + std::vector segmentedIndices = + { + 4, 2, + 2, 1, + 1, 3, + 3, 0 + }; + + auto outputIndices = MeshPrimitiveUtils::ReverseSegmentIndices32(segmentedIndices, MESH_LINE_STRIP); + + std::vector expectedIndices = + { + 4, 2, 1, 3, 0 + }; + + AreEqual(expectedIndices, outputIndices); + } + + GLTFSDK_TEST_METHOD(MeshPrimitiveUtilsTests, MeshPrimitiveUtils_Test_SerializeSegmentedIndices32_LineLoop) + { + std::vector segmentedIndices = + { + 4, 2, + 2, 1, + 1, 3, + 3, 0, + 0, 4 + }; + + auto outputIndices = MeshPrimitiveUtils::ReverseSegmentIndices32(segmentedIndices, MESH_LINE_LOOP); + + std::vector expectedIndices = + { + 4, 2, 1, 3, 0 + }; + + AreEqual(expectedIndices, outputIndices); + } + + GLTFSDK_TEST_METHOD(MeshPrimitiveUtilsTests, MeshPrimitiveUtils_Test_TriangulatedIndices16Roundtrip_TriangleStrip) + { + auto readerWriter = std::make_shared(); + auto bufferBuilder = BufferBuilder(std::make_unique(readerWriter)); + + bufferBuilder.AddBuffer(); + bufferBuilder.AddBufferView(BufferViewTarget::ARRAY_BUFFER); + + std::vector indices = { + 0U, 3U, 1U, 2U + }; + auto indicesAccessor = bufferBuilder.AddAccessor(indices, { TYPE_SCALAR, COMPONENT_UNSIGNED_SHORT }); + + Document doc; + bufferBuilder.Output(doc); + + MeshPrimitive meshPrimitive; + meshPrimitive.indicesAccessorId = indicesAccessor.id; + meshPrimitive.mode = MESH_TRIANGLE_STRIP; + + GLTFResourceReader reader(readerWriter); + + auto triangulatedIndices = MeshPrimitiveUtils::GetTriangulatedIndices16(doc, reader, meshPrimitive); + auto outputIndices = MeshPrimitiveUtils::ReverseTriangulateIndices16(triangulatedIndices, meshPrimitive.mode); + + AreEqual(outputIndices, indices); + } + + GLTFSDK_TEST_METHOD(MeshPrimitiveUtilsTests, MeshPrimitiveUtils_Test_TriangulatedIndices16Roundtrip_TriangleFan) + { + auto readerWriter = std::make_shared(); + auto bufferBuilder = BufferBuilder(std::make_unique(readerWriter)); + + bufferBuilder.AddBuffer(); + bufferBuilder.AddBufferView(BufferViewTarget::ARRAY_BUFFER); + + std::vector indices = { + 0U, 3U, 1U, 2U + }; + auto indicesAccessor = bufferBuilder.AddAccessor(indices, { TYPE_SCALAR, COMPONENT_UNSIGNED_SHORT }); + + Document doc; + bufferBuilder.Output(doc); + + MeshPrimitive meshPrimitive; + meshPrimitive.indicesAccessorId = indicesAccessor.id; + meshPrimitive.mode = MESH_TRIANGLE_FAN; + + GLTFResourceReader reader(readerWriter); + + auto triangulatedIndices = MeshPrimitiveUtils::GetTriangulatedIndices16(doc, reader, meshPrimitive); + auto outputIndices = MeshPrimitiveUtils::ReverseTriangulateIndices16(triangulatedIndices, meshPrimitive.mode); + + AreEqual(outputIndices, indices); + } + + GLTFSDK_TEST_METHOD(MeshPrimitiveUtilsTests, MeshPrimitiveUtils_Test_TriangulatedIndices32Roundtrip_TriangleStrip) + { + auto readerWriter = std::make_shared(); + auto bufferBuilder = BufferBuilder(std::make_unique(readerWriter)); + + bufferBuilder.AddBuffer(); + bufferBuilder.AddBufferView(BufferViewTarget::ARRAY_BUFFER); + + std::vector indices = { + 0U, 3U, 1U, 2U + }; + auto indicesAccessor = bufferBuilder.AddAccessor(indices, { TYPE_SCALAR, COMPONENT_UNSIGNED_INT }); + + Document doc; + bufferBuilder.Output(doc); + + MeshPrimitive meshPrimitive; + meshPrimitive.indicesAccessorId = indicesAccessor.id; + meshPrimitive.mode = MESH_TRIANGLE_STRIP; + + GLTFResourceReader reader(readerWriter); + + auto triangulatedIndices = MeshPrimitiveUtils::GetTriangulatedIndices32(doc, reader, meshPrimitive); + auto outputIndices = MeshPrimitiveUtils::ReverseTriangulateIndices32(triangulatedIndices, meshPrimitive.mode); + + AreEqual(outputIndices, indices); + } + + GLTFSDK_TEST_METHOD(MeshPrimitiveUtilsTests, MeshPrimitiveUtils_Test_TriangulatedIndices32Roundtrip_TriangleFan) + { + auto readerWriter = std::make_shared(); + auto bufferBuilder = BufferBuilder(std::make_unique(readerWriter)); + + bufferBuilder.AddBuffer(); + bufferBuilder.AddBufferView(BufferViewTarget::ARRAY_BUFFER); + + std::vector indices = { + 0U, 3U, 1U, 2U + }; + auto indicesAccessor = bufferBuilder.AddAccessor(indices, { TYPE_SCALAR, COMPONENT_UNSIGNED_INT }); + + Document doc; + bufferBuilder.Output(doc); + + MeshPrimitive meshPrimitive; + meshPrimitive.indicesAccessorId = indicesAccessor.id; + meshPrimitive.mode = MESH_TRIANGLE_FAN; + + GLTFResourceReader reader(readerWriter); + + auto triangulatedIndices = MeshPrimitiveUtils::GetTriangulatedIndices32(doc, reader, meshPrimitive); + auto outputIndices = MeshPrimitiveUtils::ReverseTriangulateIndices32(triangulatedIndices, meshPrimitive.mode); + + AreEqual(outputIndices, indices); + } + + GLTFSDK_TEST_METHOD(MeshPrimitiveUtilsTests, MeshPrimitiveUtils_Test_SegmentedIndices16Roundtrip_LineStrip) + { + auto readerWriter = std::make_shared(); + auto bufferBuilder = BufferBuilder(std::make_unique(readerWriter)); + + bufferBuilder.AddBuffer(); + bufferBuilder.AddBufferView(BufferViewTarget::ARRAY_BUFFER); + + std::vector indices = { + 0U, 3U, 1U, 2U + }; + auto indicesAccessor = bufferBuilder.AddAccessor(indices, { TYPE_SCALAR, COMPONENT_UNSIGNED_SHORT }); + + Document doc; + bufferBuilder.Output(doc); + + MeshPrimitive meshPrimitive; + meshPrimitive.indicesAccessorId = indicesAccessor.id; + meshPrimitive.mode = MESH_LINE_STRIP; + + GLTFResourceReader reader(readerWriter); + + auto segmentedIndices = MeshPrimitiveUtils::GetSegmentedIndices16(doc, reader, meshPrimitive); + auto outputIndices = MeshPrimitiveUtils::ReverseSegmentIndices16(segmentedIndices, meshPrimitive.mode); + + AreEqual(outputIndices, indices); + } + + GLTFSDK_TEST_METHOD(MeshPrimitiveUtilsTests, MeshPrimitiveUtils_Test_SegmentedIndices16Roundtrip_LineLoop) + { + auto readerWriter = std::make_shared(); + auto bufferBuilder = BufferBuilder(std::make_unique(readerWriter)); + + bufferBuilder.AddBuffer(); + bufferBuilder.AddBufferView(BufferViewTarget::ARRAY_BUFFER); + + std::vector indices = { + 0U, 3U, 1U, 2U + }; + auto indicesAccessor = bufferBuilder.AddAccessor(indices, { TYPE_SCALAR, COMPONENT_UNSIGNED_SHORT }); + + Document doc; + bufferBuilder.Output(doc); + + MeshPrimitive meshPrimitive; + meshPrimitive.indicesAccessorId = indicesAccessor.id; + meshPrimitive.mode = MESH_LINE_LOOP; + + GLTFResourceReader reader(readerWriter); + + auto segmentedIndices = MeshPrimitiveUtils::GetSegmentedIndices16(doc, reader, meshPrimitive); + auto outputIndices = MeshPrimitiveUtils::ReverseSegmentIndices16(segmentedIndices, meshPrimitive.mode); + + AreEqual(outputIndices, indices); + } + + GLTFSDK_TEST_METHOD(MeshPrimitiveUtilsTests, MeshPrimitiveUtils_Test_SegmentedIndices32Roundtrip_LineStrip) + { + auto readerWriter = std::make_shared(); + auto bufferBuilder = BufferBuilder(std::make_unique(readerWriter)); + + bufferBuilder.AddBuffer(); + bufferBuilder.AddBufferView(BufferViewTarget::ARRAY_BUFFER); + + std::vector indices = { + 0U, 3U, 1U, 2U + }; + auto indicesAccessor = bufferBuilder.AddAccessor(indices, { TYPE_SCALAR, COMPONENT_UNSIGNED_INT }); + + Document doc; + bufferBuilder.Output(doc); + + MeshPrimitive meshPrimitive; + meshPrimitive.indicesAccessorId = indicesAccessor.id; + meshPrimitive.mode = MESH_LINE_STRIP; + + GLTFResourceReader reader(readerWriter); + + auto segmentedIndices = MeshPrimitiveUtils::GetSegmentedIndices32(doc, reader, meshPrimitive); + auto outputIndices = MeshPrimitiveUtils::ReverseSegmentIndices32(segmentedIndices, meshPrimitive.mode); + + AreEqual(outputIndices, indices); + } + + GLTFSDK_TEST_METHOD(MeshPrimitiveUtilsTests, MeshPrimitiveUtils_Test_SegmentedIndices32Roundtrip_LineLoop) + { + auto readerWriter = std::make_shared(); + auto bufferBuilder = BufferBuilder(std::make_unique(readerWriter)); + + bufferBuilder.AddBuffer(); + bufferBuilder.AddBufferView(BufferViewTarget::ARRAY_BUFFER); + + std::vector indices = { + 0U, 3U, 1U, 2U + }; + auto indicesAccessor = bufferBuilder.AddAccessor(indices, { TYPE_SCALAR, COMPONENT_UNSIGNED_INT }); + + Document doc; + bufferBuilder.Output(doc); + + MeshPrimitive meshPrimitive; + meshPrimitive.indicesAccessorId = indicesAccessor.id; + meshPrimitive.mode = MESH_LINE_LOOP; + + GLTFResourceReader reader(readerWriter); + + auto segmentedIndices = MeshPrimitiveUtils::GetSegmentedIndices32(doc, reader, meshPrimitive); + auto outputIndices = MeshPrimitiveUtils::ReverseSegmentIndices32(segmentedIndices, meshPrimitive.mode); + + AreEqual(outputIndices, indices); + } }; } } diff --git a/GLTFSDK/Inc/GLTFSDK/MeshPrimitiveUtils.h b/GLTFSDK/Inc/GLTFSDK/MeshPrimitiveUtils.h index 15db670..7b518b8 100644 --- a/GLTFSDK/Inc/GLTFSDK/MeshPrimitiveUtils.h +++ b/GLTFSDK/Inc/GLTFSDK/MeshPrimitiveUtils.h @@ -5,15 +5,14 @@ #include +#include + namespace Microsoft { namespace glTF { class Document; class GLTFResourceReader; - struct Accessor; - struct MeshPrimitive; - struct MorphTarget; namespace MeshPrimitiveUtils { @@ -57,6 +56,18 @@ namespace Microsoft std::vector GetJointWeights32(const Document& doc, const GLTFResourceReader& reader, const Accessor& accessor); std::vector GetJointWeights32_0(const Document& doc, const GLTFResourceReader& reader, const MeshPrimitive& meshPrimitive); + + std::vector ReverseTriangulateIndices16(const uint16_t* indices, size_t indexCount, MeshMode mode); + std::vector ReverseTriangulateIndices32(const uint32_t* indices, size_t indexCount, MeshMode mode); + + std::vector ReverseTriangulateIndices16(const std::vector& indices, MeshMode mode); + std::vector ReverseTriangulateIndices32(const std::vector& indices, MeshMode mode); + + std::vector ReverseSegmentIndices16(const uint16_t* indices, size_t indexCount, MeshMode mode); + std::vector ReverseSegmentIndices32(const uint32_t* indices, size_t indexCount, MeshMode mode); + + std::vector ReverseSegmentIndices16(const std::vector& indices, MeshMode mode); + std::vector ReverseSegmentIndices32(const std::vector& indices, MeshMode mode); }; } } diff --git a/GLTFSDK/Source/MeshPrimitiveUtils.cpp b/GLTFSDK/Source/MeshPrimitiveUtils.cpp index 85d45b4..fb40d24 100644 --- a/GLTFSDK/Source/MeshPrimitiveUtils.cpp +++ b/GLTFSDK/Source/MeshPrimitiveUtils.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -192,7 +193,7 @@ namespace { std::vector texcoordsFloat; texcoordsFloat.reserve(texcoords.size()); - for(size_t i = 0; i < texcoords.size(); i++) + for (size_t i = 0; i < texcoords.size(); i++) { texcoordsFloat.push_back(texcoords[i] / FLOAT_UINT8_MAX); } @@ -476,7 +477,7 @@ namespace if (doc.accessors.Has(meshPrimitive.indicesAccessorId)) { const auto& indicesAccessor = doc.accessors.Get(meshPrimitive.indicesAccessorId); - return Microsoft::glTF::MeshPrimitiveUtils::GetIndices16(doc, reader, indicesAccessor); + return MeshPrimitiveUtils::GetIndices16(doc, reader, indicesAccessor); } else { @@ -498,7 +499,7 @@ namespace if (doc.accessors.Has(meshPrimitive.indicesAccessorId)) { const auto& indicesAccessor = doc.accessors.Get(meshPrimitive.indicesAccessorId); - return Microsoft::glTF::MeshPrimitiveUtils::GetIndices32(doc, reader, indicesAccessor); + return MeshPrimitiveUtils::GetIndices32(doc, reader, indicesAccessor); } else { @@ -509,6 +510,128 @@ namespace return rawIndices; } } + + template + std::vector ReconstructTriangleStripIndexing(const T* indices, size_t indexCount) + { + if (indexCount % 3 != 0) + { + throw GLTFException("Input triangulated triangle strip has non-multiple-of-3 indices."); + } + + if (indexCount < 3) + { + throw GLTFException("Input triangulated triangle strip has fewer than 3 indices."); + } + + std::vector result; + result.reserve(2 + indexCount / 3); + + result.push_back(indices[0]); + result.push_back(indices[1]); + + for (size_t i = 2; i < indexCount; i += 3) + { + if (i % 2 == 0) + { + result.push_back(indices[i]); + } + else + { + result.push_back(indices[i - 1]); + } + } + + return result; + } + + template + std::vector ReconstructTriangleFanIndexing(const T* indices, size_t indexCount) + { + if (indexCount % 3 != 0) + { + throw GLTFException("Input triangulated triangle fan has non-multiple-of-3 indices."); + } + + if (indexCount < 3) + { + throw GLTFException("Input triangulated triangle fan has fewer than 3 indices."); + } + + std::vector result; + result.reserve(2 + indexCount / 3); + + result.push_back(indices[0]); + result.push_back(indices[1]); + + for (size_t i = 2; i < indexCount; i += 3) + { + result.push_back(indices[i]); + } + + return result; + } + + template + std::vector ReverseTriangulateIndices(const T* indices, size_t indexCount, MeshMode mode) + { + if (mode == MeshMode::MESH_TRIANGLE_STRIP) + { + return ReconstructTriangleStripIndexing(indices, indexCount); + } + else if (mode == MeshMode::MESH_TRIANGLE_FAN) + { + return ReconstructTriangleFanIndexing(indices, indexCount); + } + else + { + throw GLTFException("Non-triangulated mesh mode specificed."); + } + } + + template + std::vector ReconstructLineLoopIndexing(const T* indices, size_t indexCount) + { + if (indexCount % 2 != 0) + { + throw GLTFException("Input segmented line has non-multiple-of-2 indices."); + } + + std::vector result; + result.reserve(indexCount / 2); + + for (size_t i = 0; i < indexCount; i += 2) + { + result.push_back(indices[i]); + } + + return result; + } + + template + std::vector ReconstructLineStripIndexing(const T* indices, size_t indexCount) + { + auto result = ReconstructLineLoopIndexing(indices, indexCount); + result.push_back(indices[indexCount - 1]); + return result; + } + + template + std::vector ReverseSegmentIndices(const T* indices, size_t indexCount, MeshMode mode) + { + if (mode == MeshMode::MESH_LINE_STRIP) + { + return ReconstructLineStripIndexing(indices, indexCount); + } + else if (mode == MeshMode::MESH_LINE_LOOP) + { + return ReconstructLineLoopIndexing(indices, indexCount); + } + else + { + throw GLTFException("Non-segmented mesh mode specificed."); + } + } } std::vector MeshPrimitiveUtils::GetIndices16(const Document& doc, const GLTFResourceReader& reader, const Accessor& accessor) @@ -828,3 +951,43 @@ std::vector MeshPrimitiveUtils::GetJointWeights32_0(const Document& do const auto& accessor = doc.accessors.Get(meshPrimitive.GetAttributeAccessorId(ACCESSOR_WEIGHTS_0)); return GetJointWeights32(doc, reader, accessor); } + +std::vector MeshPrimitiveUtils::ReverseTriangulateIndices16(const uint16_t* indices, size_t indexCount, MeshMode mode) +{ + return ReverseTriangulateIndices(indices, indexCount, mode); +} + +std::vector MeshPrimitiveUtils::ReverseTriangulateIndices32(const uint32_t* indices, size_t indexCount, MeshMode mode) +{ + return ReverseTriangulateIndices(indices, indexCount, mode); +} + +std::vector MeshPrimitiveUtils::ReverseTriangulateIndices16(const std::vector& indices, MeshMode mode) +{ + return ReverseTriangulateIndices(indices.data(), indices.size(), mode); +} + +std::vector MeshPrimitiveUtils::ReverseTriangulateIndices32(const std::vector& indices, MeshMode mode) +{ + return ReverseTriangulateIndices(indices.data(), indices.size(), mode); +} + +std::vector MeshPrimitiveUtils::ReverseSegmentIndices16(const uint16_t* indices, size_t indexCount, MeshMode mode) +{ + return ReverseSegmentIndices(indices, indexCount, mode); +} + +std::vector MeshPrimitiveUtils::ReverseSegmentIndices32(const uint32_t* indices, size_t indexCount, MeshMode mode) +{ + return ReverseSegmentIndices(indices, indexCount, mode); +} + +std::vector MeshPrimitiveUtils::ReverseSegmentIndices16(const std::vector& indices, MeshMode mode) +{ + return ReverseSegmentIndices(indices.data(), indices.size(), mode); +} + +std::vector MeshPrimitiveUtils::ReverseSegmentIndices32(const std::vector& indices, MeshMode mode) +{ + return ReverseSegmentIndices(indices.data(), indices.size(), mode); +}