From 16374a8ae3b553b500bb836a8d2c4824fb7c7fff Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 29 Jan 2025 12:50:37 +1100 Subject: [PATCH 1/6] Use our own matrix -> FTransform conversion. The one built into Unreal (at least through version 5.5) will produce a scale of 0.0 for any scale less than or equal to about 1e-4. --- .../Private/CesiumBoundingVolumeComponent.cpp | 4 ++-- .../Private/CesiumGlobeAnchorComponent.cpp | 3 +-- .../Private/CesiumGltfComponent.cpp | 3 +-- .../Private/CesiumGltfPrimitiveComponent.cpp | 4 ++-- .../Private/CesiumSubLevelComponent.cpp | 5 ++--- Source/CesiumRuntime/Private/VecMath.cpp | 17 +++++++++++++++++ Source/CesiumRuntime/Private/VecMath.h | 11 +++++++++++ 7 files changed, 36 insertions(+), 11 deletions(-) diff --git a/Source/CesiumRuntime/Private/CesiumBoundingVolumeComponent.cpp b/Source/CesiumRuntime/Private/CesiumBoundingVolumeComponent.cpp index 7f517369d..dd262db00 100644 --- a/Source/CesiumRuntime/Private/CesiumBoundingVolumeComponent.cpp +++ b/Source/CesiumRuntime/Private/CesiumBoundingVolumeComponent.cpp @@ -120,8 +120,8 @@ void UCesiumBoundingVolumeComponent::UpdateOcclusion( } void UCesiumBoundingVolumeComponent::_updateTransform() { - const FTransform transform = FTransform( - VecMath::createMatrix(this->_cesiumToUnreal * this->_tileTransform)); + const FTransform transform = + VecMath::createTransform(this->_cesiumToUnreal * this->_tileTransform); this->SetRelativeTransform_Direct(transform); this->SetComponentToWorld(transform); diff --git a/Source/CesiumRuntime/Private/CesiumGlobeAnchorComponent.cpp b/Source/CesiumRuntime/Private/CesiumGlobeAnchorComponent.cpp index 055d656d8..3ad84b02c 100644 --- a/Source/CesiumRuntime/Private/CesiumGlobeAnchorComponent.cpp +++ b/Source/CesiumRuntime/Private/CesiumGlobeAnchorComponent.cpp @@ -732,8 +732,7 @@ void UCesiumGlobeAnchorComponent::_updateFromNativeGlobeAnchor( glm::dmat4 anchorToLocal = nativeAnchor.getAnchorToLocalTransform( pGeoreference->GetCoordinateSystem()); - this->_setCurrentRelativeTransform( - FTransform(VecMath::createMatrix(anchorToLocal))); + this->_setCurrentRelativeTransform(VecMath::createTransform(anchorToLocal)); } else { this->_lastRelativeTransformIsValid = false; } diff --git a/Source/CesiumRuntime/Private/CesiumGltfComponent.cpp b/Source/CesiumRuntime/Private/CesiumGltfComponent.cpp index d82ae89dc..c899d71c8 100644 --- a/Source/CesiumRuntime/Private/CesiumGltfComponent.cpp +++ b/Source/CesiumRuntime/Private/CesiumGltfComponent.cpp @@ -2040,8 +2040,7 @@ static void loadInstancingData( for (int64_t i = 0; i < count; ++i) { glm::dmat4 unrealMat = yInvertMatrix * instanceTransforms[i] * yInvertMatrix; - auto unrealFMatrix = VecMath::createMatrix(unrealMat); - result.InstanceTransforms[i].SetFromMatrix(unrealFMatrix); + result.InstanceTransforms[i] = VecMath::createTransform(unrealMat); } if (pInstanceFeatures) { result.pInstanceFeatures = diff --git a/Source/CesiumRuntime/Private/CesiumGltfPrimitiveComponent.cpp b/Source/CesiumRuntime/Private/CesiumGltfPrimitiveComponent.cpp index ddcd89caa..68951e5f9 100644 --- a/Source/CesiumRuntime/Private/CesiumGltfPrimitiveComponent.cpp +++ b/Source/CesiumRuntime/Private/CesiumGltfPrimitiveComponent.cpp @@ -107,8 +107,8 @@ bool UpdateTransformFromCesiumAux( const glm::dmat4& CesiumToUnrealTransform, CesiumComponent* cesiumComponent) { const CesiumPrimitiveData& primData = cesiumComponent->getPrimitiveData(); - const FTransform transform = FTransform(VecMath::createMatrix( - CesiumToUnrealTransform * primData.HighPrecisionNodeTransform)); + const FTransform transform = VecMath::createTransform( + CesiumToUnrealTransform * primData.HighPrecisionNodeTransform); if (cesiumComponent->Mobility == EComponentMobility::Movable) { // For movable objects, move the component in the normal way, but don't diff --git a/Source/CesiumRuntime/Private/CesiumSubLevelComponent.cpp b/Source/CesiumRuntime/Private/CesiumSubLevelComponent.cpp index 3831d70b9..c74f2959b 100644 --- a/Source/CesiumRuntime/Private/CesiumSubLevelComponent.cpp +++ b/Source/CesiumRuntime/Private/CesiumSubLevelComponent.cpp @@ -284,8 +284,7 @@ void UCesiumSubLevelComponent::PlaceOriginAtEcef(const FVector& NewOriginEcef) { } pOwner->Modify(); - pOwner->SetActorTransform( - FTransform(VecMath::createMatrix(NewLevelTransform))); + pOwner->SetActorTransform(VecMath::createTransform(NewLevelTransform)); // Set the new sub-level georeference origin. this->Modify(); @@ -355,7 +354,7 @@ void UCesiumSubLevelComponent::PlaceOriginAtEcef(const FVector& NewOriginEcef) { Tileset->Modify(); Root->Modify(); Root->SetRelativeTransform( - FTransform(VecMath::createMatrix(RelativeTransformInNew)), + VecMath::createTransform(RelativeTransformInNew), false, nullptr, ETeleportType::TeleportPhysics); diff --git a/Source/CesiumRuntime/Private/VecMath.cpp b/Source/CesiumRuntime/Private/VecMath.cpp index 87d46a352..46597034d 100644 --- a/Source/CesiumRuntime/Private/VecMath.cpp +++ b/Source/CesiumRuntime/Private/VecMath.cpp @@ -5,6 +5,7 @@ #include "CesiumUtility/Math.h" #include "Math/Quat.h" #include "Math/RotationMatrix.h" +#include #include glm::dmat4 VecMath::createMatrix4D(const FMatrix& m) noexcept { @@ -123,6 +124,22 @@ FMatrix VecMath::createMatrix(const glm::dmat4& m) noexcept { FVector(m[3].x, m[3].y, m[3].z)); } +FTransform VecMath::createTransform(const glm::dmat4& m) noexcept { + glm::dvec3 translation; + glm::dquat rotation; + glm::dvec3 scale; + CesiumGeometry::Transforms::computeTranslationRotationScaleFromMatrix( + m, + &translation, + &rotation, + &scale); + + return FTransform( + VecMath::createQuaternion(rotation), + VecMath::createVector(translation), + VecMath::createVector(scale)); +} + FMatrix VecMath::createMatrix(const glm::dmat3& m) noexcept { return FMatrix( FVector(m[0].x, m[0].y, m[0].z), diff --git a/Source/CesiumRuntime/Private/VecMath.h b/Source/CesiumRuntime/Private/VecMath.h index d6b6ecc97..8320bf271 100644 --- a/Source/CesiumRuntime/Private/VecMath.h +++ b/Source/CesiumRuntime/Private/VecMath.h @@ -142,12 +142,23 @@ class VecMath { /** * @brief Create a `FMatrix` from the given `glm` matrix. + * + * If the ultimate goal is to create an `FTransform`, use + * {@link createTransform} instead. * * @param m The `glm` matrix. * @return The `FMatrix`. */ static FMatrix createMatrix(const glm::dmat4& m) noexcept; + /** + * @brief Create a `FTransform` from the given `glm` matrix. + * + * @param m The `glm` matrix. + * @return The `FTransform`. + */ + static FTransform createTransform(const glm::dmat4& m) noexcept; + /** * @brief Create a `FMatrix` from the given `glm` columns * From 64773958acc5b8952946250512f22d22cff9b648 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 29 Jan 2025 12:53:45 +1100 Subject: [PATCH 2/6] Update CHANGES.md. --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 706a1577c..6640936c3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -9,6 +9,7 @@ ##### Fixes :wrench: - Fixed another bug in `CesiumSubLevelSwitcherComponent` that could prevent all sub-levels from loading if a single sub-level failed to load. +- Worked around a limitation in Unreal's `FMatrix` -> `FTransform` conversion that caused models using a small scale factor (such as where vertex positions are expressed in millimeters) to fail to render because their scale was treated as 0.0. ### v2.12.0 - 2025-01-02 From 32cc31b337ecba5a66e47cdc95639aa7abab2a76 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 29 Jan 2025 13:42:52 +1100 Subject: [PATCH 3/6] Add test for VecMath::createTransform. --- .../Private/Tests/VecMath.spec.cpp | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 Source/CesiumRuntime/Private/Tests/VecMath.spec.cpp diff --git a/Source/CesiumRuntime/Private/Tests/VecMath.spec.cpp b/Source/CesiumRuntime/Private/Tests/VecMath.spec.cpp new file mode 100644 index 000000000..ce97ba63f --- /dev/null +++ b/Source/CesiumRuntime/Private/Tests/VecMath.spec.cpp @@ -0,0 +1,77 @@ +// Copyright 2020-2025 CesiumGS, Inc. and Contributors + +#include "VecMath.h" +#include "Misc/AutomationTest.h" + +BEGIN_DEFINE_SPEC( + FVecMathSpec, + "Cesium.Unit.VecMath", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | + EAutomationTestFlags::ServerContext | + EAutomationTestFlags::CommandletContext | + EAutomationTestFlags::ProductFilter) +END_DEFINE_SPEC(FVecMathSpec) + +void FVecMathSpec::Define() { + Describe("createTransform", [this]() { + It("matches FMatrix -> FTransform for larger scales", [this]() { + FTransform original = FTransform( + FQuat::MakeFromRotator(FRotator(10.0, 20.0, 30.0)), + FVector(3000.0, 2000.0, 1000.0), + FVector(1.0, 2.0, 3.0)); + + FMatrix originalUnrealMatrix = original.ToMatrixWithScale(); + glm::dmat4 originalGlmMatrix = + VecMath::createMatrix4D(originalUnrealMatrix); + + FTransform viaUnrealMatrix = + FTransform(VecMath::createMatrix(originalGlmMatrix)); + FTransform viaVecMath = VecMath::createTransform(originalGlmMatrix); + + TestNearlyEqual( + TEXT("Translation"), + viaVecMath.GetTranslation(), + viaUnrealMatrix.GetTranslation(), + 1e-8); + TestNearlyEqual( + TEXT("Rotation"), + viaVecMath.GetRotation().Rotator(), + viaUnrealMatrix.GetRotation().Rotator(), + 1e-10); + TestNearlyEqual( + TEXT("Scale"), + viaVecMath.GetScale3D(), + viaUnrealMatrix.GetScale3D(), + 1e-11); + }); + + It("returns correct values when scale is small", [this]() { + FTransform original = FTransform( + FQuat::MakeFromRotator(FRotator(10.0, 20.0, 30.0)), + FVector(3000.0, 2000.0, 1000.0), + FVector(1e-7, 2e-7, 3e-7)); + + FMatrix originalUnrealMatrix = original.ToMatrixWithScale(); + glm::dmat4 originalGlmMatrix = + VecMath::createMatrix4D(originalUnrealMatrix); + + FTransform viaVecMath = VecMath::createTransform(originalGlmMatrix); + + TestNearlyEqual( + TEXT("Translation"), + viaVecMath.GetTranslation(), + original.GetTranslation(), + 1e-8); + TestNearlyEqual( + TEXT("Rotation"), + viaVecMath.GetRotation().Rotator(), + original.GetRotation().Rotator(), + 1e-10); + TestNearlyEqual( + TEXT("Scale"), + viaVecMath.GetScale3D(), + original.GetScale3D(), + 1e-18); + }); + }); +} From 725bd1cd00bcef4d075d7f60f9fbcfe79d67ca74 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 29 Jan 2025 13:44:39 +1100 Subject: [PATCH 4/6] Formatting. --- Source/CesiumRuntime/Private/VecMath.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/CesiumRuntime/Private/VecMath.h b/Source/CesiumRuntime/Private/VecMath.h index 8320bf271..2ef3e6c08 100644 --- a/Source/CesiumRuntime/Private/VecMath.h +++ b/Source/CesiumRuntime/Private/VecMath.h @@ -142,7 +142,7 @@ class VecMath { /** * @brief Create a `FMatrix` from the given `glm` matrix. - * + * * If the ultimate goal is to create an `FTransform`, use * {@link createTransform} instead. * @@ -153,7 +153,7 @@ class VecMath { /** * @brief Create a `FTransform` from the given `glm` matrix. - * + * * @param m The `glm` matrix. * @return The `FTransform`. */ From 074141c70738aca4f242ecca9c7f47f52431d72f Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 29 Jan 2025 13:46:28 +1100 Subject: [PATCH 5/6] Fix include style. --- Source/CesiumRuntime/Private/VecMath.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/CesiumRuntime/Private/VecMath.cpp b/Source/CesiumRuntime/Private/VecMath.cpp index 46597034d..cbfa08d83 100644 --- a/Source/CesiumRuntime/Private/VecMath.cpp +++ b/Source/CesiumRuntime/Private/VecMath.cpp @@ -2,10 +2,10 @@ #include "VecMath.h" -#include "CesiumUtility/Math.h" #include "Math/Quat.h" #include "Math/RotationMatrix.h" #include +#include #include glm::dmat4 VecMath::createMatrix4D(const FMatrix& m) noexcept { From 6476f8d00d086434bffde8608fbeaa935160e9d5 Mon Sep 17 00:00:00 2001 From: Janine Liu <32226860+j9liu@users.noreply.github.com> Date: Fri, 31 Jan 2025 14:24:19 -0500 Subject: [PATCH 6/6] Update CHANGES.md --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index dea1a3eb8..62b5768b3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -12,7 +12,7 @@ ##### Fixes :wrench: - Fixed another bug in `CesiumSubLevelSwitcherComponent` that could prevent all sub-levels from loading if a single sub-level failed to load. -- Worked around a limitation in Unreal's `FMatrix` -> `FTransform` conversion that caused models using a small scale factor (such as where vertex positions are expressed in millimeters) to fail to render because their scale was treated as 0.0. +- Worked around a limitation in Unreal's `FMatrix` -> `FTransform` conversion that prevented models with a small scale factor (e.g., where vertex positions are expressed in millimeters) from rendering because their scale was treated as 0.0. - Fixed crash when calling `SampleHeightMostDetailed` blueprint function without a valid tileset. ### v2.12.0 - 2025-01-02