From f23f80a279d13e1a7ea0e7bf35f2eb3f6630fb22 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Tue, 17 Oct 2023 16:18:26 -0400 Subject: [PATCH] Temporary commit for debugging test bug --- .../CesiumFeaturesMetadataComponent.cpp | 4 +- .../Private/CesiumGltfComponent.cpp | 8 +- .../CesiumMetadataPickingBlueprintLibrary.cpp | 8 +- .../Tests/CesiumFeatureIdTexture.spec.cpp | 236 +++++++ .../Private/Tests/CesiumGltfSpecUtility.h | 11 +- ...umMetadataPickingBlueprintLibrary.spec.cpp | 618 +++++++++++++++--- .../Tests/CesiumPrimitiveFeatures.spec.cpp | 10 - .../CesiumMetadataPickingBlueprintLibrary.h | 4 +- 8 files changed, 769 insertions(+), 130 deletions(-) diff --git a/Source/CesiumRuntime/Private/CesiumFeaturesMetadataComponent.cpp b/Source/CesiumRuntime/Private/CesiumFeaturesMetadataComponent.cpp index 6f684f741..255338c11 100644 --- a/Source/CesiumRuntime/Private/CesiumFeaturesMetadataComponent.cpp +++ b/Source/CesiumRuntime/Private/CesiumFeaturesMetadataComponent.cpp @@ -855,6 +855,8 @@ FString GetCodeForAssemblingPropertyFromTexture( " uint channel = uint(f.Get(Channels, i));\n" " uint sample = asuint(f.Get(sample, channel));\n" "}\n"; + + return code; } UMaterialExpressionMaterialFunctionCall* GenerateNodesForFeatureIdTexture( @@ -1769,7 +1771,7 @@ void GenerateMaterialNodes( // NodeX = MetadataSectionLeft; //// Generate nodes for any property textures that aren't linked to a - ///primitive / texture coordinate set. + /// primitive / texture coordinate set. // for (const FCesiumPropertyTextureDescription& propertyTexture : // pComponent->PropertyTextures) { // if (!GeneratedPropertyTextureNames.Find(propertyTexture.Name)) { diff --git a/Source/CesiumRuntime/Private/CesiumGltfComponent.cpp b/Source/CesiumRuntime/Private/CesiumGltfComponent.cpp index 5498f6316..d01b7a62e 100644 --- a/Source/CesiumRuntime/Private/CesiumGltfComponent.cpp +++ b/Source/CesiumRuntime/Private/CesiumGltfComponent.cpp @@ -1577,6 +1577,12 @@ static void loadIndexedPrimitive( positionView, indexAccessor); primitiveResult.IndexAccessor = indexAccessor; + } else { + UE_LOG( + LogCesium, + VeryVerbose, + TEXT( + "Skip loading primitive due to invalid component type in its index accessor.")); } } @@ -1605,7 +1611,6 @@ static void loadPrimitive( } AccessorView positionView(model, *pPositionAccessor); - result.PositionAccessor = positionView; if (primitive.indices < 0 || primitive.indices >= model.accessors.size()) { std::vector syntheticIndexBuffer(positionView.size()); @@ -1628,6 +1633,7 @@ static void loadPrimitive( *pPositionAccessor, positionView); } + result.PositionAccessor = std::move(positionView); } static void loadMesh( diff --git a/Source/CesiumRuntime/Private/CesiumMetadataPickingBlueprintLibrary.cpp b/Source/CesiumRuntime/Private/CesiumMetadataPickingBlueprintLibrary.cpp index c46ba8ae6..d1dee376d 100644 --- a/Source/CesiumRuntime/Private/CesiumMetadataPickingBlueprintLibrary.cpp +++ b/Source/CesiumRuntime/Private/CesiumMetadataPickingBlueprintLibrary.cpp @@ -89,6 +89,11 @@ bool UCesiumMetadataPickingBlueprintLibrary::FindUVFromHit( return false; } + if (pGltfComponent->PositionAccessor.status() != + CesiumGltf::AccessorViewStatus::Valid) { + return false; + } + auto accessorIt = pGltfComponent->TexCoordAccessorMap.find(GltfTexCoordSetIndex); if (accessorIt == pGltfComponent->TexCoordAccessorMap.end()) { @@ -112,14 +117,13 @@ bool UCesiumMetadataPickingBlueprintLibrary::FindUVFromHit( if (!maybeTexCoord) { return false; } - const glm::dvec2& texCoord = *maybeTexCoord; UVs[i] = FVector2D(texCoord[0], texCoord[1]); } std::array Positions; for (size_t i = 0; i < Positions.size(); i++) { - auto Position = pGltfComponent->PositionAccessor[VertexIndices[i]]; + auto& Position = pGltfComponent->PositionAccessor[VertexIndices[i]]; // The Y-component of glTF positions must be inverted Positions[i] = FVector(Position[0], -Position[1], Position[2]); } diff --git a/Source/CesiumRuntime/Private/Tests/CesiumFeatureIdTexture.spec.cpp b/Source/CesiumRuntime/Private/Tests/CesiumFeatureIdTexture.spec.cpp index e66024a42..4f47d1271 100644 --- a/Source/CesiumRuntime/Private/Tests/CesiumFeatureIdTexture.spec.cpp +++ b/Source/CesiumRuntime/Private/Tests/CesiumFeatureIdTexture.spec.cpp @@ -1,5 +1,6 @@ #include "CesiumFeatureIdTexture.h" #include "CesiumGltf/ExtensionExtMeshFeatures.h" +#include "CesiumGltfPrimitiveComponent.h" #include "CesiumGltfSpecUtility.h" #include "Misc/AutomationTest.h" @@ -17,6 +18,7 @@ const std::vector texCoords{ glm::vec2(0.5, 0), glm::vec2(0, 0.5), glm::vec2(0.5, 0.5)}; +TObjectPtr pPrimitiveComponent; END_DEFINE_SPEC(FCesiumFeatureIdTextureSpec) void FCesiumFeatureIdTextureSpec::Define() { @@ -434,4 +436,238 @@ void FCesiumFeatureIdTextureSpec::Define() { } }); }); + + Describe("GetFeatureIDFromHit", [this]() { + BeforeEach([this]() { + model = Model(); + Mesh& mesh = model.meshes.emplace_back(); + pPrimitive = &mesh.primitives.emplace_back(); + pPrimitiveComponent = NewObject(); + + std::vector positions{ + glm::vec3(-1, 0, 0), + glm::vec3(0, 1, 0), + glm::vec3(1, 0, 0), + glm::vec3(-1, 3, 0), + glm::vec3(0, 4, 0), + glm::vec3(1, 3, 0), + }; + + CreateAttributeForPrimitive( + model, + *pPrimitive, + "POSITION", + AccessorSpec::Type::VEC3, + AccessorSpec::ComponentType::FLOAT, + positions); + + pPrimitiveComponent->PositionAccessor = + CesiumGltf::AccessorView( + model, + static_cast(model.accessors.size() - 1)); + + // For convenience when testing, the UVs are the same as the positions + // they correspond to. This means that the interpolated UV value should be + // directly equal to the barycentric coordinates of the triangle. + std::vector texCoords{ + glm::vec2(-1, 0), + glm::vec2(0, 1), + glm::vec2(1, 0), + glm::vec2(-1, 0), + glm::vec2(0, 1), + glm::vec2(1, 0)}; + CreateAttributeForPrimitive( + model, + *pPrimitive, + "TEXCOORD_0", + AccessorSpec::Type::VEC2, + AccessorSpec::ComponentType::FLOAT, + texCoords); + + pPrimitiveComponent->TexCoordAccessorMap.emplace( + 0, + AccessorView>( + model, + static_cast(model.accessors.size() - 1))); + }); + + It("returns -1 for invalid texture", [this]() { + FeatureIdTexture texture; + texture.index = -1; + texture.texCoord = 0; + texture.channels = {0}; + + FCesiumFeatureIdTexture featureIDTexture( + model, + *pPrimitive, + texture, + "PropertyTableName"); + + TestNotEqual( + "FeatureIDTextureStatus", + UCesiumFeatureIdTextureBlueprintLibrary::GetFeatureIDTextureStatus( + featureIDTexture), + ECesiumFeatureIdTextureStatus::Valid); + + FHitResult Hit; + Hit.Location = FVector_NetQuantize::Zero(); + Hit.Component = pPrimitiveComponent; + Hit.FaceIndex = 0; + + TestEqual( + "FeatureIDForVertex", + UCesiumFeatureIdTextureBlueprintLibrary::GetFeatureIDFromHit( + featureIDTexture, + Hit), + -1); + }); + + // It("returns -1 if hit has no valid component", [this]() { + // FHitResult Hit; + // Hit.Location = FVector_NetQuantize(0, -1, 0); + // Hit.FaceIndex = 0; + // Hit.Component = nullptr; + + // FVector2D UV; + // TestFalse( + // "found hit", + // UCesiumMetadataPickingBlueprintLibrary::FindUVFromHit(Hit, 0, UV)); + //}); + + // It("returns -1 if specified texcoord set does not exist", [this]() { + // FHitResult Hit; + // Hit.Location = FVector_NetQuantize(0, -1, 0); + // Hit.FaceIndex = 0; + // Hit.Component = pPrimitiveComponent; + + // FVector2D UV; + // TestFalse( + // "found hit", + // UCesiumMetadataPickingBlueprintLibrary::FindUVFromHit(Hit, 1, UV)); + //}); + + // It("returns correct value for valid texture", [this]() { + // const std::vector featureIDs{0, 3, 1, 2}; + + // FeatureId& featureId = AddFeatureIDsAsTextureToModel( + // model, + // *pPrimitive, + // featureIDs, + // 4, + // 2, + // 2, + // texCoords, + // 0); + + // FCesiumFeatureIdTexture featureIDTexture( + // model, + // *pPrimitive, + // *featureId.texture, + // "PropertyTableName"); + + // TestEqual( + // "FeatureIDTextureStatus", + // UCesiumFeatureIdTextureBlueprintLibrary::GetFeatureIDTextureStatus( + // featureIDTexture), + // ECesiumFeatureIdTextureStatus::Valid); + + // for (size_t i = 0; i < featureIDs.size(); i++) { + // int64 featureID = + // UCesiumFeatureIdTextureBlueprintLibrary::GetFeatureIDForVertex( + // featureIDTexture, + // static_cast(i)); + // TestEqual("FeatureIDForVertex", featureID, featureIDs[i]); + // } + //}); + + // It("returns correct value for primitive with multiple texcoords", + // [this]() { + // const std::vector featureIDs{0, 3, 1, 2}; + // const std::vector texCoord0{ + // glm::vec2(0, 0), + // glm::vec2(0.5, 0), + // glm::vec2(0, 0.5), + // glm::vec2(0.5, 0.5)}; + + // std::vector values(texCoord0.size()); + // std::memcpy(values.data(), texCoord0.data(), values.size()); + + // CreateAttributeForPrimitive( + // model, + // *pPrimitive, + // "TEXCOORD_0", + // AccessorSpec::Type::VEC2, + // AccessorSpec::ComponentType::FLOAT, + // std::move(values)); + + // const std::vector texCoord1{ + // glm::vec2(0.5, 0.5), + // glm::vec2(0, 0), + // glm::vec2(0.5, 0), + // glm::vec2(0.0, 0.5)}; + + // FeatureId& featureId = AddFeatureIDsAsTextureToModel( + // model, + // *pPrimitive, + // featureIDs, + // 4, + // 2, + // 2, + // texCoord1, + // 1); + + // FCesiumFeatureIdTexture featureIDTexture( + // model, + // *pPrimitive, + // *featureId.texture, + // "PropertyTableName"); + + // TestEqual( + // "FeatureIDTextureStatus", + // UCesiumFeatureIdTextureBlueprintLibrary::GetFeatureIDTextureStatus( + // featureIDTexture), + // ECesiumFeatureIdTextureStatus::Valid); + + // const std::vector expected{2, 0, 3, 1}; + // for (size_t i = 0; i < featureIDs.size(); i++) { + // int64 featureID = + // UCesiumFeatureIdTextureBlueprintLibrary::GetFeatureIDForVertex( + // featureIDTexture, + // static_cast(i)); + // TestEqual("FeatureIDForVertex", featureID, expected[i]); + // } + //}); + + // It("gets hit for primitive without indices", [this]() { + // FHitResult Hit; + // Hit.Location = FVector_NetQuantize(0, -1, 0); + // Hit.FaceIndex = 0; + // Hit.Component = pPrimitiveComponent; + + // FVector2D UV = FVector2D::Zero(); + + // TestTrue( + // "found hit", + // UCesiumMetadataPickingBlueprintLibrary::FindUVFromHit(Hit, 0, UV)); + // TestEqual("UV at point", UV, FVector2D(0, 1)); + + // Hit.Location = FVector_NetQuantize(0, -0.5, 0); + // TestTrue( + // "found hit", + // UCesiumMetadataPickingBlueprintLibrary::FindUVFromHit(Hit, 0, UV)); + // TestTrue( + // "UV at point inside triangle (X)", + // FMath::IsNearlyEqual(UV[0], 0.0)); + // TestTrue( + // "UV at point inside triangle (Y)", + // FMath::IsNearlyEqual(UV[1], 1.0 / 3.0)); + + // Hit.FaceIndex = 1; + // Hit.Location = FVector_NetQuantize(0, -4, 0); + // TestTrue( + // "found hit", + // UCesiumMetadataPickingBlueprintLibrary::FindUVFromHit(Hit, 0, UV)); + // TestEqual("UV at point", UV, FVector2D(0, 1)); + //}); + }); } diff --git a/Source/CesiumRuntime/Private/Tests/CesiumGltfSpecUtility.h b/Source/CesiumRuntime/Private/Tests/CesiumGltfSpecUtility.h index c5efc99f7..cd41befa2 100644 --- a/Source/CesiumRuntime/Private/Tests/CesiumGltfSpecUtility.h +++ b/Source/CesiumRuntime/Private/Tests/CesiumGltfSpecUtility.h @@ -58,12 +58,14 @@ template void CreateIndicesForPrimitive( CesiumGltf::Model& model, CesiumGltf::MeshPrimitive& primitive, - const std::string& type, const int32_t componentType, const std::vector& indices) { std::vector values = GetValuesAsBytes(indices); - const int32_t accessor = - AddBufferToModel(model, type, componentType, std::move(values)); + const int32_t accessor = AddBufferToModel( + model, + AccessorSpec::Type::SCALAR, + componentType, + std::move(values)); primitive.indices = accessor; } @@ -215,7 +217,6 @@ CesiumGltf::PropertyTextureProperty& AddPropertyTexturePropertyToModel( CesiumGltf::PropertyTextureProperty& property = propertyTexture.properties[propertyName]; property.channels = channels; - property.index = static_cast(model.textures.size() - 1) - ; + property.index = static_cast(model.textures.size() - 1); return property; } diff --git a/Source/CesiumRuntime/Private/Tests/CesiumMetadataPickingBlueprintLibrary.spec.cpp b/Source/CesiumRuntime/Private/Tests/CesiumMetadataPickingBlueprintLibrary.spec.cpp index cf239f920..0f8e85108 100644 --- a/Source/CesiumRuntime/Private/Tests/CesiumMetadataPickingBlueprintLibrary.spec.cpp +++ b/Source/CesiumRuntime/Private/Tests/CesiumMetadataPickingBlueprintLibrary.spec.cpp @@ -19,56 +19,522 @@ MeshPrimitive* pPrimitive; ExtensionExtMeshFeatures* pMeshFeatures; ExtensionModelExtStructuralMetadata* pStructuralMetadata; PropertyTable* pPropertyTable; +PropertyTexture* pPropertyTexture; TObjectPtr pModelComponent; TObjectPtr pPrimitiveComponent; END_DEFINE_SPEC(FCesiumMetadataPickingSpec) void FCesiumMetadataPickingSpec::Define() { - BeforeEach([this]() { - model = Model(); - - Mesh& mesh = model.meshes.emplace_back(); - pPrimitive = &mesh.primitives.emplace_back(); - - // Two disconnected triangles. - std::vector positions{ - glm::vec3(-1, 1, 0), - glm::vec3(1, 1, 0), - glm::vec3(1, -1, 0), - glm::vec3(2, 2, 0), - glm::vec3(-2, 2, 0), - glm::vec3(-2, -2, 0), - }; - std::vector positionData(positions.size() * sizeof(glm::vec3)); - std::memcpy(positionData.data(), positions.data(), positionData.size()); - CreateAttributeForPrimitive( - model, - *pPrimitive, - "POSITION", - AccessorSpec::Type::VEC3, - AccessorSpec::ComponentType::FLOAT, - std::move(positionData)); - - pMeshFeatures = &pPrimitive->addExtension(); - pStructuralMetadata = - &model.addExtension(); - - std::string className = "testClass"; - pStructuralMetadata->schema.emplace(); - pStructuralMetadata->schema->classes[className]; - - pPropertyTable = &pStructuralMetadata->propertyTables.emplace_back(); - pPropertyTable->classProperty = className; - - pModelComponent = NewObject(); - pPrimitiveComponent = - NewObject(pModelComponent); - pPrimitiveComponent->AttachToComponent( - pModelComponent, - FAttachmentTransformRules(EAttachmentRule::KeepRelative, false)); + Describe("FindUVFromHit", [this]() { + BeforeEach([this]() { + model = Model(); + Mesh& mesh = model.meshes.emplace_back(); + pPrimitive = &mesh.primitives.emplace_back(); + pPrimitiveComponent = NewObject(); + + std::vector positions{ + glm::vec3(-1, 0, 0), + glm::vec3(0, 1, 0), + glm::vec3(1, 0, 0), + glm::vec3(-1, 3, 0), + glm::vec3(0, 4, 0), + glm::vec3(1, 3, 0), + }; + CreateAttributeForPrimitive( + model, + *pPrimitive, + "POSITION", + AccessorSpec::Type::VEC3, + AccessorSpec::ComponentType::FLOAT, + positions); + pPrimitiveComponent->PositionAccessor = AccessorView( + model, + static_cast(model.accessors.size() - 1)); + + // For convenience when testing, the UVs are the same as the positions + // they correspond to. This means that the interpolated UV value should be + // directly equal to the barycentric coordinates of the triangle. + std::vector texCoords{ + glm::vec2(-1, 0), + glm::vec2(0, 1), + glm::vec2(1, 0), + glm::vec2(-1, 0), + glm::vec2(0, 1), + glm::vec2(1, 0)}; + CreateAttributeForPrimitive( + model, + *pPrimitive, + "TEXCOORD_0", + AccessorSpec::Type::VEC2, + AccessorSpec::ComponentType::FLOAT, + texCoords); + + pPrimitiveComponent->TexCoordAccessorMap.emplace( + 0, + AccessorView>( + model, + static_cast(model.accessors.size() - 1))); + }); + + It("returns false if hit has no valid component", [this]() { + FHitResult Hit; + Hit.Location = FVector_NetQuantize(0, -1, 0); + Hit.FaceIndex = 0; + Hit.Component = nullptr; + + FVector2D UV; + TestFalse( + "found hit", + UCesiumMetadataPickingBlueprintLibrary::FindUVFromHit(Hit, 0, UV)); + }); + + It("returns false if specified texcoord set does not exist", [this]() { + FHitResult Hit; + Hit.Location = FVector_NetQuantize(0, -1, 0); + Hit.FaceIndex = 0; + Hit.Component = pPrimitiveComponent; + + FVector2D UV; + TestFalse( + "found hit", + UCesiumMetadataPickingBlueprintLibrary::FindUVFromHit(Hit, 1, UV)); + }); + + It("gets hit for primitive without indices", [this]() { + FHitResult Hit; + Hit.Location = FVector_NetQuantize(0, -1, 0); + Hit.FaceIndex = 0; + Hit.Component = pPrimitiveComponent; + + FVector2D UV = FVector2D::Zero(); + TestTrue( + "found hit", + UCesiumMetadataPickingBlueprintLibrary::FindUVFromHit(Hit, 0, UV)); + TestEqual("UV at point", UV, FVector2D(0, 1)); + + Hit.Location = FVector_NetQuantize(0, -0.5, 0); + TestTrue( + "found hit", + UCesiumMetadataPickingBlueprintLibrary::FindUVFromHit(Hit, 0, UV)); + TestTrue( + "UV at point inside triangle (X)", + FMath::IsNearlyEqual(UV[0], 0.0)); + TestTrue( + "UV at point inside triangle (Y)", + FMath::IsNearlyEqual(UV[1], 0.5)); + + Hit.FaceIndex = 1; + Hit.Location = FVector_NetQuantize(0, -4, 0); + TestTrue( + "found hit", + UCesiumMetadataPickingBlueprintLibrary::FindUVFromHit(Hit, 0, UV)); + TestEqual("UV at point", UV, FVector2D(0, 1)); + }); + + It("gets hit for primitive with indices", [this]() { + // Switch the order of the triangles. + const std::vector indices{3, 4, 5, 0, 1, 2}; + CreateIndicesForPrimitive( + model, + *pPrimitive, + AccessorSpec::ComponentType::UNSIGNED_BYTE, + indices); + + pPrimitiveComponent->IndexAccessor = AccessorView( + model, + static_cast(model.accessors.size() - 1)); + + FHitResult Hit; + Hit.Location = FVector_NetQuantize(0, -4, 0); + Hit.FaceIndex = 0; + Hit.Component = pPrimitiveComponent; + + FVector2D UV = FVector2D::Zero(); + + TestTrue( + "found hit", + UCesiumMetadataPickingBlueprintLibrary::FindUVFromHit(Hit, 0, UV)); + TestEqual("UV at point", UV, FVector2D(0, 1)); + + Hit.Location = FVector_NetQuantize(0, -3.5, 0); + TestTrue( + "found hit", + UCesiumMetadataPickingBlueprintLibrary::FindUVFromHit(Hit, 0, UV)); + TestTrue( + "UV at point inside triangle (X)", + FMath::IsNearlyEqual(UV[0], 0.0)); + TestTrue( + "UV at point inside triangle (Y)", + FMath::IsNearlyEqual(UV[1], 0.5)); + + Hit.FaceIndex = 1; + Hit.Location = FVector_NetQuantize(0, -1, 0); + TestTrue( + "found hit", + UCesiumMetadataPickingBlueprintLibrary::FindUVFromHit(Hit, 0, UV)); + TestEqual("UV at point", UV, FVector2D(0, 1)); + }); }); - Describe("GetMetadataValuesForFace", [this]() { + Describe("GetPropertyTableValuesFromHit", [this]() { + BeforeEach([this]() { + // Two disconnected triangles. + std::vector positions{ + glm::vec3(-1, 1, 0), + glm::vec3(1, 1, 0), + glm::vec3(1, -1, 0), + glm::vec3(2, 2, 0), + glm::vec3(-2, 2, 0), + glm::vec3(-2, -2, 0), + }; + std::vector positionData(positions.size() * sizeof(glm::vec3)); + std::memcpy(positionData.data(), positions.data(), positionData.size()); + CreateAttributeForPrimitive( + model, + *pPrimitive, + "POSITION", + AccessorSpec::Type::VEC3, + AccessorSpec::ComponentType::FLOAT, + std::move(positionData)); + + pMeshFeatures = &pPrimitive->addExtension(); + pStructuralMetadata = + &model.addExtension(); + + std::string className = "testClass"; + pStructuralMetadata->schema.emplace(); + pStructuralMetadata->schema->classes[className]; + + pPropertyTable = &pStructuralMetadata->propertyTables.emplace_back(); + pPropertyTable->classProperty = className; + + pModelComponent = NewObject(); + pPrimitiveComponent = + NewObject(pModelComponent); + pPrimitiveComponent->AttachToComponent( + pModelComponent, + FAttachmentTransformRules(EAttachmentRule::KeepRelative, false)); + }); + + It("returns empty map for invalid component", [this]() { + std::vector featureIDs{0, 0, 0, 1, 1, 1}; + FeatureId& featureId = + AddFeatureIDsAsAttributeToModel(model, *pPrimitive, featureIDs, 2, 0); + featureId.propertyTable = + static_cast(pStructuralMetadata->propertyTables.size() - 1); + + std::vector scalarValues{1, 2}; + pPropertyTable->count = static_cast(scalarValues.size()); + const std::string scalarPropertyName("scalarProperty"); + AddPropertyTablePropertyToModel( + model, + *pPropertyTable, + scalarPropertyName, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::INT32, + scalarValues); + + pModelComponent->Metadata = + FCesiumModelMetadata(model, *pStructuralMetadata); + pPrimitiveComponent->Features = + FCesiumPrimitiveFeatures(model, *pPrimitive, *pMeshFeatures); + + FHitResult Hit; + Hit.FaceIndex = -1; + Hit.Component = nullptr; + + auto values = + UCesiumMetadataPickingBlueprintLibrary::GetPropertyTableValuesFromHit( + Hit); + TestTrue("empty values for invalid hit", values.IsEmpty()); + }); + + It("returns empty map for invalid feature ID set index", [this]() { + std::vector featureIDs{0, 0, 0, 1, 1, 1}; + FeatureId& featureId = + AddFeatureIDsAsAttributeToModel(model, *pPrimitive, featureIDs, 2, 0); + featureId.propertyTable = + static_cast(pStructuralMetadata->propertyTables.size() - 1); + + std::vector scalarValues{1, 2}; + pPropertyTable->count = static_cast(scalarValues.size()); + const std::string scalarPropertyName("scalarProperty"); + AddPropertyTablePropertyToModel( + model, + *pPropertyTable, + scalarPropertyName, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::INT32, + scalarValues); + + pModelComponent->Metadata = + FCesiumModelMetadata(model, *pStructuralMetadata); + pPrimitiveComponent->Features = + FCesiumPrimitiveFeatures(model, *pPrimitive, *pMeshFeatures); + + FHitResult Hit; + Hit.FaceIndex = 0; + Hit.Component = pPrimitiveComponent; + + auto values = + UCesiumMetadataPickingBlueprintLibrary::GetPropertyTableValuesFromHit( + Hit, + -1); + TestTrue("empty values for negative index", values.IsEmpty()); + + values = + UCesiumMetadataPickingBlueprintLibrary::GetPropertyTableValuesFromHit( + Hit, + 1); + TestTrue( + "empty values for positive out-of-range index", + values.IsEmpty()); + }); + + It("returns empty values if feature ID set is not associated with a property table", + [this]() { + std::vector featureIDs{0, 0, 0, 1, 1, 1}; + AddFeatureIDsAsAttributeToModel(model, *pPrimitive, featureIDs, 2, 0); + + std::vector scalarValues{1, 2}; + pPropertyTable->count = static_cast(scalarValues.size()); + const std::string scalarPropertyName("scalarProperty"); + AddPropertyTablePropertyToModel( + model, + *pPropertyTable, + scalarPropertyName, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::INT32, + scalarValues); + + std::vector vec2Values{ + glm::vec2(1.0f, 2.5f), + glm::vec2(3.1f, -4.0f)}; + const std::string vec2PropertyName("vec2Property"); + AddPropertyTablePropertyToModel( + model, + *pPropertyTable, + vec2PropertyName, + ClassProperty::Type::VEC2, + ClassProperty::ComponentType::FLOAT32, + vec2Values); + + pModelComponent->Metadata = + FCesiumModelMetadata(model, *pStructuralMetadata); + pPrimitiveComponent->Features = + FCesiumPrimitiveFeatures(model, *pPrimitive, *pMeshFeatures); + + FHitResult Hit; + Hit.FaceIndex = 0; + Hit.Component = pPrimitiveComponent; + + const auto values = UCesiumMetadataPickingBlueprintLibrary:: + GetPropertyTableValuesFromHit(Hit); + TestTrue("values are empty", values.IsEmpty()); + }); + + It("returns values for first feature ID set by default", [this]() { + std::vector featureIDs{0, 0, 0, 1, 1, 1}; + FeatureId& featureId = + AddFeatureIDsAsAttributeToModel(model, *pPrimitive, featureIDs, 2, 0); + featureId.propertyTable = + static_cast(pStructuralMetadata->propertyTables.size() - 1); + + std::vector scalarValues{1, 2}; + pPropertyTable->count = static_cast(scalarValues.size()); + const std::string scalarPropertyName("scalarProperty"); + AddPropertyTablePropertyToModel( + model, + *pPropertyTable, + scalarPropertyName, + ClassProperty::Type::SCALAR, + ClassProperty::ComponentType::INT32, + scalarValues); + + std::vector vec2Values{ + glm::vec2(1.0f, 2.5f), + glm::vec2(3.1f, -4.0f)}; + const std::string vec2PropertyName("vec2Property"); + AddPropertyTablePropertyToModel( + model, + *pPropertyTable, + vec2PropertyName, + ClassProperty::Type::VEC2, + ClassProperty::ComponentType::FLOAT32, + vec2Values); + + pModelComponent->Metadata = + FCesiumModelMetadata(model, *pStructuralMetadata); + pPrimitiveComponent->Features = + FCesiumPrimitiveFeatures(model, *pPrimitive, *pMeshFeatures); + + FHitResult Hit; + Hit.Component = pPrimitiveComponent; + + for (size_t i = 0; i < scalarValues.size(); i++) { + Hit.FaceIndex = i; + + const auto values = UCesiumMetadataPickingBlueprintLibrary:: + GetPropertyTableValuesFromHit(Hit); + + TestEqual("number of values", values.Num(), 2); + TestTrue( + "contains scalar value", + values.Contains(FString(scalarPropertyName.c_str()))); + TestTrue( + "contains vec2 value", + values.Contains(FString(vec2PropertyName.c_str()))); + + const FCesiumMetadataValue* pScalarValue = + values.Find(FString(scalarPropertyName.c_str())); + if (pScalarValue) { + TestEqual( + "scalar value", + UCesiumMetadataValueBlueprintLibrary::GetInteger( + *pScalarValue, + 0), + scalarValues[i]); + } + + const FCesiumMetadataValue* pVec2Value = + values.Find(FString(vec2PropertyName.c_str())); + if (pVec2Value) { + FVector2D expected( + static_cast(vec2Values[i][0]), + static_cast(vec2Values[i][1])); + TestEqual( + "vec2 value", + UCesiumMetadataValueBlueprintLibrary::GetVector2D( + *pVec2Value, + FVector2D::Zero()), + expected); + } + } + }); + + // It("returns values for specified feature ID set", [this]() { + // std::vector featureIDs0{1, 1, 1, 0, 0, 0}; + // FeatureId& featureId0 = AddFeatureIDsAsAttributeToModel( + // model, + // *pPrimitive, + // featureIDs0, + // 2, + // 0); + // std::vector featureIDs1{0, 0, 0, 1, 1, 1}; + // FeatureId& featureId1 = AddFeatureIDsAsAttributeToModel( + // model, + // *pPrimitive, + // featureIDs1, + // 2, + // 1); + // featureId0.propertyTable = featureId1.propertyTable = + // static_cast(pStructuralMetadata->propertyTables.size() - + // 1); + + // std::vector scalarValues{1, 2}; + // pPropertyTable->count = static_cast(scalarValues.size()); + // const std::string scalarPropertyName("scalarProperty"); + // AddPropertyTablePropertyToModel( + // model, + // *pPropertyTable, + // scalarPropertyName, + // ClassProperty::Type::SCALAR, + // ClassProperty::ComponentType::INT32, + // scalarValues); + + // std::vector vec2Values{ + // glm::vec2(1.0f, 2.5f), + // glm::vec2(3.1f, -4.0f)}; + // const std::string vec2PropertyName("vec2Property"); + // AddPropertyTablePropertyToModel( + // model, + // *pPropertyTable, + // vec2PropertyName, + // ClassProperty::Type::VEC2, + // ClassProperty::ComponentType::FLOAT32, + // vec2Values); + + // pModelComponent->Metadata = + // FCesiumModelMetadata(model, *pStructuralMetadata); + // pPrimitiveComponent->Features = + // FCesiumPrimitiveFeatures(model, *pPrimitive, *pMeshFeatures); + + // for (size_t i = 0; i < scalarValues.size(); i++) { + // const auto values = + // UCesiumMetadataPickingBlueprintLibrary::GetMetadataValuesForFace( + // pPrimitiveComponent, + // i, + // 1); + // TestEqual("number of values", values.Num(), 2); + // TestTrue( + // "contains scalar value", + // values.Contains(FString(scalarPropertyName.c_str()))); + // TestTrue( + // "contains vec2 value", + // values.Contains(FString(vec2PropertyName.c_str()))); + + // const FCesiumMetadataValue& scalarValue = + // *values.Find(FString(scalarPropertyName.c_str())); + // TestEqual( + // "scalar value", + // UCesiumMetadataValueBlueprintLibrary::GetInteger(scalarValue, + // 0), scalarValues[i]); + + // const FCesiumMetadataValue& vec2Value = + // *values.Find(FString(vec2PropertyName.c_str())); + // FVector2D expected( + // static_cast(vec2Values[i][0]), + // static_cast(vec2Values[i][1])); + // TestEqual( + // "vec2 value", + // UCesiumMetadataValueBlueprintLibrary::GetVector2D( + // vec2Value, + // FVector2D::Zero()), + // expected); + // } + //}); + }); + + Describe("GetMetadataValuesForFace (Deprecated)", [this]() { + BeforeEach([this]() { + // Two disconnected triangles. + std::vector positions{ + glm::vec3(-1, 1, 0), + glm::vec3(1, 1, 0), + glm::vec3(1, -1, 0), + glm::vec3(2, 2, 0), + glm::vec3(-2, 2, 0), + glm::vec3(-2, -2, 0), + }; + std::vector positionData(positions.size() * sizeof(glm::vec3)); + std::memcpy(positionData.data(), positions.data(), positionData.size()); + CreateAttributeForPrimitive( + model, + *pPrimitive, + "POSITION", + AccessorSpec::Type::VEC3, + AccessorSpec::ComponentType::FLOAT, + std::move(positionData)); + + pMeshFeatures = &pPrimitive->addExtension(); + pStructuralMetadata = + &model.addExtension(); + + std::string className = "testClass"; + pStructuralMetadata->schema.emplace(); + pStructuralMetadata->schema->classes[className]; + + pPropertyTable = &pStructuralMetadata->propertyTables.emplace_back(); + pPropertyTable->classProperty = className; + + pModelComponent = NewObject(); + pPrimitiveComponent = + NewObject(pModelComponent); + pPrimitiveComponent->AttachToComponent( + pModelComponent, + FAttachmentTransformRules(EAttachmentRule::KeepRelative, false)); + }); + It("returns empty map for invalid face index", [this]() { std::vector featureIDs{0, 0, 0, 1, 1, 1}; FeatureId& featureId = @@ -335,70 +801,4 @@ void FCesiumMetadataPickingSpec::Define() { } }); }); - - Describe("GetMetadataValuesForFaceAsStrings", [this]() { - It("returns values for first feature ID set by default", [this]() { - std::vector featureIDs{0, 0, 0, 1, 1, 1}; - FeatureId& featureId = - AddFeatureIDsAsAttributeToModel(model, *pPrimitive, featureIDs, 2, 0); - featureId.propertyTable = - static_cast(pStructuralMetadata->propertyTables.size() - 1); - - std::vector scalarValues{1, 2}; - pPropertyTable->count = static_cast(scalarValues.size()); - const std::string scalarPropertyName("scalarProperty"); - AddPropertyTablePropertyToModel( - model, - *pPropertyTable, - scalarPropertyName, - ClassProperty::Type::SCALAR, - ClassProperty::ComponentType::INT32, - scalarValues); - - std::vector vec2Values{ - glm::vec2(1.0f, 2.5f), - glm::vec2(3.1f, -4.0f)}; - const std::string vec2PropertyName("vec2Property"); - AddPropertyTablePropertyToModel( - model, - *pPropertyTable, - vec2PropertyName, - ClassProperty::Type::VEC2, - ClassProperty::ComponentType::FLOAT32, - vec2Values); - - pModelComponent->Metadata = - FCesiumModelMetadata(model, *pStructuralMetadata); - pPrimitiveComponent->Features = - FCesiumPrimitiveFeatures(model, *pPrimitive, *pMeshFeatures); - - for (size_t i = 0; i < scalarValues.size(); i++) { - const auto strings = UCesiumMetadataPickingBlueprintLibrary:: - GetMetadataValuesForFaceAsStrings( - pPrimitiveComponent, - static_cast(i)); - TestEqual("number of strings", strings.Num(), 2); - TestTrue( - "contains scalar value", - strings.Contains(FString(scalarPropertyName.c_str()))); - TestTrue( - "contains vec2 value", - strings.Contains(FString(vec2PropertyName.c_str()))); - - const FString& scalarString = - *strings.Find(FString(scalarPropertyName.c_str())); - TestEqual( - "scalar value", - scalarString, - FString(std::to_string(scalarValues[i]).c_str())); - - const FString& vec2Value = - *strings.Find(FString(vec2PropertyName.c_str())); - std::string expected( - "X=" + std::to_string(vec2Values[i][0]) + - " Y=" + std::to_string(vec2Values[i][1])); - TestEqual("vec2 value", vec2Value, FString(expected.c_str())); - } - }); - }); } diff --git a/Source/CesiumRuntime/Private/Tests/CesiumPrimitiveFeatures.spec.cpp b/Source/CesiumRuntime/Private/Tests/CesiumPrimitiveFeatures.spec.cpp index cc28df267..a0499f58b 100644 --- a/Source/CesiumRuntime/Private/Tests/CesiumPrimitiveFeatures.spec.cpp +++ b/Source/CesiumRuntime/Private/Tests/CesiumPrimitiveFeatures.spec.cpp @@ -227,7 +227,6 @@ void FCesiumPrimitiveFeaturesSpec::Define() { CreateIndicesForPrimitive( model, *pPrimitive, - AccessorSpec::Type::SCALAR, AccessorSpec::ComponentType::UNSIGNED_BYTE, indices); @@ -272,7 +271,6 @@ void FCesiumPrimitiveFeaturesSpec::Define() { CreateIndicesForPrimitive( model, *pPrimitive, - AccessorSpec::Type::SCALAR, AccessorSpec::ComponentType::UNSIGNED_BYTE, indices); @@ -309,7 +307,6 @@ void FCesiumPrimitiveFeaturesSpec::Define() { CreateIndicesForPrimitive( model, *pPrimitive, - AccessorSpec::Type::SCALAR, AccessorSpec::ComponentType::UNSIGNED_BYTE, indices); @@ -343,7 +340,6 @@ void FCesiumPrimitiveFeaturesSpec::Define() { CreateIndicesForPrimitive( model, *pPrimitive, - AccessorSpec::Type::SCALAR, AccessorSpec::ComponentType::UNSIGNED_BYTE, indices); @@ -383,7 +379,6 @@ void FCesiumPrimitiveFeaturesSpec::Define() { CreateIndicesForPrimitive( model, *pPrimitive, - AccessorSpec::Type::SCALAR, AccessorSpec::ComponentType::UNSIGNED_BYTE, indices); @@ -440,7 +435,6 @@ void FCesiumPrimitiveFeaturesSpec::Define() { CreateIndicesForPrimitive( model, *pPrimitive, - AccessorSpec::Type::SCALAR, AccessorSpec::ComponentType::UNSIGNED_BYTE, indices); @@ -485,7 +479,6 @@ void FCesiumPrimitiveFeaturesSpec::Define() { CreateIndicesForPrimitive( model, *pPrimitive, - AccessorSpec::Type::SCALAR, AccessorSpec::ComponentType::UNSIGNED_BYTE, indices); @@ -573,7 +566,6 @@ void FCesiumPrimitiveFeaturesSpec::Define() { CreateIndicesForPrimitive( model, *pPrimitive, - AccessorSpec::Type::SCALAR, AccessorSpec::ComponentType::UNSIGNED_BYTE, indices); @@ -657,7 +649,6 @@ void FCesiumPrimitiveFeaturesSpec::Define() { CreateIndicesForPrimitive( model, *pPrimitive, - AccessorSpec::Type::SCALAR, AccessorSpec::ComponentType::UNSIGNED_BYTE, indices); @@ -699,7 +690,6 @@ void FCesiumPrimitiveFeaturesSpec::Define() { CreateIndicesForPrimitive( model, *pPrimitive, - AccessorSpec::Type::SCALAR, AccessorSpec::ComponentType::UNSIGNED_BYTE, indices); diff --git a/Source/CesiumRuntime/Public/CesiumMetadataPickingBlueprintLibrary.h b/Source/CesiumRuntime/Public/CesiumMetadataPickingBlueprintLibrary.h index cc2b5c7c9..ca68f1d66 100644 --- a/Source/CesiumRuntime/Public/CesiumMetadataPickingBlueprintLibrary.h +++ b/Source/CesiumRuntime/Public/CesiumMetadataPickingBlueprintLibrary.h @@ -46,7 +46,7 @@ class CESIUMRUNTIME_API UCesiumMetadataPickingBlueprintLibrary BlueprintPure, Meta = (DeprecatedFunction, - DeprecationMessage = "Use GetPropertyTableValuesForHit instead.")) + DeprecationMessage = "Use GetPropertyTableValuesFromHit instead.")) static TMap GetMetadataValuesForFace( const UPrimitiveComponent* Component, int64 FaceIndex, @@ -84,7 +84,7 @@ class CESIUMRUNTIME_API UCesiumMetadataPickingBlueprintLibrary Meta = (DeprecatedFunction, DeprecationMessage = - "Use GetValuesAsStrings to convert the output of GetPropertyTableValuesForHit instead.")) + "Use GetValuesAsStrings to convert the output of GetPropertyTableValuesFromHit instead.")) static TMap GetMetadataValuesForFaceAsStrings( const UPrimitiveComponent* Component, int64 FaceIndex,