From 78467b5e72d5cfa0c9e9039a58cd6378e2a2420a Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 20 Apr 2022 15:07:54 +1000 Subject: [PATCH 1/4] Compute better bounding volumes for use by UE. --- .../CesiumRuntime/Private/Cesium3DTileset.cpp | 3 +- .../Private/CesiumGltfComponent.cpp | 13 ++- .../Private/CesiumGltfComponent.h | 4 +- .../Private/CesiumGltfPrimitiveComponent.cpp | 101 ++++++++++++++++++ .../Private/CesiumGltfPrimitiveComponent.h | 5 + extern/cesium-native | 2 +- 6 files changed, 122 insertions(+), 6 deletions(-) diff --git a/Source/CesiumRuntime/Private/Cesium3DTileset.cpp b/Source/CesiumRuntime/Private/Cesium3DTileset.cpp index 87ed7f74d..26f813e8a 100644 --- a/Source/CesiumRuntime/Private/Cesium3DTileset.cpp +++ b/Source/CesiumRuntime/Private/Cesium3DTileset.cpp @@ -590,7 +590,8 @@ class UnrealResourcePreparer _pActor->GetCesiumTilesetToUnrealRelativeWorldTransform(), this->_pActor->GetMaterial(), this->_pActor->GetWaterMaterial(), - this->_pActor->GetCustomDepthParameters()); + this->_pActor->GetCustomDepthParameters(), + tile.getContentBoundingVolume().value_or(tile.getBoundingVolume())); } // UE_LOG(LogCesium, VeryVerbose, TEXT("No content for tile")); return nullptr; diff --git a/Source/CesiumRuntime/Private/CesiumGltfComponent.cpp b/Source/CesiumRuntime/Private/CesiumGltfComponent.cpp index 38ac8aa43..d270e192a 100644 --- a/Source/CesiumRuntime/Private/CesiumGltfComponent.cpp +++ b/Source/CesiumRuntime/Private/CesiumGltfComponent.cpp @@ -1793,7 +1793,8 @@ static void SetMetadataParameterValues( static void loadPrimitiveGameThreadPart( UCesiumGltfComponent* pGltf, LoadPrimitiveResult& loadResult, - const glm::dmat4x4& cesiumToUnrealTransform) { + const glm::dmat4x4& cesiumToUnrealTransform, + const Cesium3DTilesSelection::BoundingVolume& boundingVolume) { FName meshName = createSafeName(loadResult.name, ""); UCesiumGltfPrimitiveComponent* pMesh = NewObject(pGltf, meshName); @@ -1809,6 +1810,7 @@ static void loadPrimitiveGameThreadPart( RF_Transient | RF_DuplicateTransient | RF_TextExportTransient); pMesh->pModel = loadResult.pModel; pMesh->pMeshPrimitive = loadResult.pMeshPrimitive; + pMesh->boundingVolume = boundingVolume; pMesh->SetRenderCustomDepth(pGltf->CustomDepthParameters.RenderCustomDepth); pMesh->SetCustomDepthStencilWriteMask( pGltf->CustomDepthParameters.CustomDepthStencilWriteMask); @@ -2012,7 +2014,8 @@ UCesiumGltfComponent::CreateOffGameThread( const glm::dmat4x4& cesiumToUnrealTransform, UMaterialInterface* pBaseMaterial, UMaterialInterface* pBaseWaterMaterial, - FCustomDepthParameters CustomDepthParameters) { + FCustomDepthParameters CustomDepthParameters, + const Cesium3DTilesSelection::BoundingVolume& boundingVolume) { HalfConstructedReal* pReal = static_cast(pHalfConstructed.Get()); @@ -2043,7 +2046,11 @@ UCesiumGltfComponent::CreateOffGameThread( for (LoadNodeResult& node : pReal->loadModelResult.nodeResults) { if (node.meshResult) { for (LoadPrimitiveResult& primitive : node.meshResult->primitiveResults) { - loadPrimitiveGameThreadPart(Gltf, primitive, cesiumToUnrealTransform); + loadPrimitiveGameThreadPart( + Gltf, + primitive, + cesiumToUnrealTransform, + boundingVolume); } } } diff --git a/Source/CesiumRuntime/Private/CesiumGltfComponent.h b/Source/CesiumRuntime/Private/CesiumGltfComponent.h index ace6c2668..e88c914dd 100644 --- a/Source/CesiumRuntime/Private/CesiumGltfComponent.h +++ b/Source/CesiumRuntime/Private/CesiumGltfComponent.h @@ -2,6 +2,7 @@ #pragma once +#include "Cesium3DTilesSelection/BoundingVolume.h" #include "CesiumEncodedMetadataUtility.h" #include "CesiumMetadataModel.h" #include "Components/PrimitiveComponent.h" @@ -72,7 +73,8 @@ class UCesiumGltfComponent : public USceneComponent { const glm::dmat4x4& CesiumToUnrealTransform, UMaterialInterface* BaseMaterial, UMaterialInterface* BaseWaterMaterial, - FCustomDepthParameters CustomDepthParameters); + FCustomDepthParameters CustomDepthParameters, + const Cesium3DTilesSelection::BoundingVolume& boundingVolume); UCesiumGltfComponent(); virtual ~UCesiumGltfComponent(); diff --git a/Source/CesiumRuntime/Private/CesiumGltfPrimitiveComponent.cpp b/Source/CesiumRuntime/Private/CesiumGltfPrimitiveComponent.cpp index 0bc54f7fd..7102e4663 100644 --- a/Source/CesiumRuntime/Private/CesiumGltfPrimitiveComponent.cpp +++ b/Source/CesiumRuntime/Private/CesiumGltfPrimitiveComponent.cpp @@ -6,6 +6,8 @@ #include "Engine/StaticMesh.h" #include "Materials/MaterialInstanceDynamic.h" #include "PhysicsEngine/BodySetup.h" +#include "VecMath.h" +#include // Sets default values for this component's properties UCesiumGltfPrimitiveComponent::UCesiumGltfPrimitiveComponent() { @@ -135,3 +137,102 @@ void UCesiumGltfPrimitiveComponent::BeginDestroy() { Super::BeginDestroy(); } + +namespace { + +struct CalcBoundsOperation { + const Cesium3DTilesSelection::BoundingVolume& boundingVolume; + const FTransform& localToWorld; + const glm::dmat4& highPrecisionNodeTransform; + + // Bounding volumes are expressed in tileset coordinates, which is usually + // ECEF. + // + // - `localToWorld` goes from model coordinates to Unreal world + // coordinates, where model coordinates include the tile's transform as + // well as any glTF node transforms. + // - `highPrecisionNodeTransform` transforms from model coordinates to tileset + // coordinates. + // + // So to transform a bounding volume, we need to first transform by the + // inverse of `HighPrecisionNodeTransform` in order bring the bounding volume + // into model coordinates, and then transform by `localToWorld` to bring the + // bounding volume into Unreal world coordinates. + + glm::dmat4 getModelToUnrealWorldMatrix() const { + const FMatrix matrix = localToWorld.ToMatrixWithScale(); + return VecMath::createMatrix4D(matrix); + } + + glm::dmat4 getTilesetToUnrealWorldMatrix() const { + const glm::dmat4 modelToUnreal = getModelToUnrealWorldMatrix(); + const glm::dmat4 tilesetToModel = + glm::affineInverse(highPrecisionNodeTransform); + return modelToUnreal * tilesetToModel; + } + + FBoxSphereBounds + operator()(const CesiumGeometry::BoundingSphere& sphere) const { + // Create a temporary box for the sphere, and then transform the box. + double side = glm::sqrt(sphere.getRadius() * sphere.getRadius() * 0.5); + glm::dmat3 halfAxes(side); + CesiumGeometry::OrientedBoundingBox box(sphere.getCenter(), halfAxes); + return (*this)(box); + } + + FBoxSphereBounds + operator()(const CesiumGeometry::OrientedBoundingBox& box) const { + glm::dmat4 matrix = getTilesetToUnrealWorldMatrix(); + glm::dvec3 center = glm::dvec3(matrix * glm::dvec4(box.getCenter(), 1.0)); + glm::dmat3 halfAxes = glm::dmat3(matrix) * box.getHalfAxes(); + + glm::dvec3 corner1 = halfAxes[0] + halfAxes[1]; + glm::dvec3 corner2 = halfAxes[0] + halfAxes[2]; + glm::dvec3 corner3 = halfAxes[1] + halfAxes[2]; + + double sphereRadius = glm::max(glm::length(corner1), glm::length(corner2)); + sphereRadius = glm::max(sphereRadius, glm::length(corner3)); + + FVector xs(halfAxes[0].x, halfAxes[1].x, halfAxes[2].x); + FVector ys(halfAxes[0].y, halfAxes[1].y, halfAxes[2].y); + FVector zs(halfAxes[0].z, halfAxes[1].z, halfAxes[2].z); + + FBoxSphereBounds result; + result.Origin = VecMath::createVector(center); + result.SphereRadius = sphereRadius; + result.BoxExtent = FVector(xs.GetAbsMax(), ys.GetAbsMax(), zs.GetAbsMax()); + return result; + } + + FBoxSphereBounds + operator()(const CesiumGeospatial::BoundingRegion& region) const { + return (*this)(region.getBoundingBox()); + } + + FBoxSphereBounds operator()( + const CesiumGeospatial::BoundingRegionWithLooseFittingHeights& region) + const { + return (*this)(region.getBoundingRegion()); + } + + FBoxSphereBounds + operator()(const CesiumGeospatial::S2CellBoundingVolume& s2) const { + return (*this)(s2.computeBoundingRegion()); + } +}; + +} // namespace + +FBoxSphereBounds UCesiumGltfPrimitiveComponent::CalcBounds( + const FTransform& LocalToWorld) const { + if (!this->boundingVolume) { + return Super::CalcBounds(LocalToWorld); + } + + return std::visit( + CalcBoundsOperation{ + *this->boundingVolume, + LocalToWorld, + this->HighPrecisionNodeTransform}, + *this->boundingVolume); +} diff --git a/Source/CesiumRuntime/Private/CesiumGltfPrimitiveComponent.h b/Source/CesiumRuntime/Private/CesiumGltfPrimitiveComponent.h index 730ba3d7f..6a9e8b23f 100644 --- a/Source/CesiumRuntime/Private/CesiumGltfPrimitiveComponent.h +++ b/Source/CesiumRuntime/Private/CesiumGltfPrimitiveComponent.h @@ -2,6 +2,7 @@ #pragma once +#include "Cesium3DTilesSelection/BoundingVolume.h" #include "CesiumEncodedMetadataUtility.h" #include "CesiumGltf/MeshPrimitive.h" #include "CesiumGltf/Model.h" @@ -39,6 +40,8 @@ class UCesiumGltfPrimitiveComponent : public UStaticMeshComponent { OverlayTextureCoordinateIDMap overlayTextureCoordinateIDToUVIndex; std::unordered_map textureCoordinateMap; + std::optional boundingVolume; + /** * Updates this component's transform from a new double-precision * transformation from the Cesium world to the Unreal Engine world, as well as @@ -49,4 +52,6 @@ class UCesiumGltfPrimitiveComponent : public UStaticMeshComponent { void UpdateTransformFromCesium(const glm::dmat4& CesiumToUnrealTransform); virtual void BeginDestroy() override; + + virtual FBoxSphereBounds CalcBounds(const FTransform& LocalToWorld) const; }; diff --git a/extern/cesium-native b/extern/cesium-native index af2d16bb6..8d76d3194 160000 --- a/extern/cesium-native +++ b/extern/cesium-native @@ -1 +1 @@ -Subproject commit af2d16bb60dad73f2556acecde5687e9c2d47b82 +Subproject commit 8d76d3194f3f635f156d3dc92b69a517cd67f067 From 2362471bb1a84aab2cf7ea2751863dc702382f6c Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 20 Apr 2022 16:06:44 +1000 Subject: [PATCH 2/4] Better computation of AABB from sphere. --- .../Private/CesiumGltfPrimitiveComponent.cpp | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/Source/CesiumRuntime/Private/CesiumGltfPrimitiveComponent.cpp b/Source/CesiumRuntime/Private/CesiumGltfPrimitiveComponent.cpp index 7102e4663..6c16efd37 100644 --- a/Source/CesiumRuntime/Private/CesiumGltfPrimitiveComponent.cpp +++ b/Source/CesiumRuntime/Private/CesiumGltfPrimitiveComponent.cpp @@ -173,11 +173,23 @@ struct CalcBoundsOperation { FBoxSphereBounds operator()(const CesiumGeometry::BoundingSphere& sphere) const { - // Create a temporary box for the sphere, and then transform the box. - double side = glm::sqrt(sphere.getRadius() * sphere.getRadius() * 0.5); - glm::dmat3 halfAxes(side); - CesiumGeometry::OrientedBoundingBox box(sphere.getCenter(), halfAxes); - return (*this)(box); + glm::dmat4 matrix = getTilesetToUnrealWorldMatrix(); + glm::dvec3 center = glm::dvec3(matrix * glm::dvec4(sphere.getCenter(), 1.0)); + glm::dmat3 halfAxes = glm::dmat3(matrix) * glm::dmat3(sphere.getRadius()); + + // The sphere only needs to reach the sides of the box, not the corners. + double sphereRadius = glm::max(glm::length(halfAxes[0]), glm::length(halfAxes[1])); + sphereRadius = glm::max(sphereRadius, glm::length(halfAxes[2])); + + FVector xs(halfAxes[0].x, halfAxes[1].x, halfAxes[2].x); + FVector ys(halfAxes[0].y, halfAxes[1].y, halfAxes[2].y); + FVector zs(halfAxes[0].z, halfAxes[1].z, halfAxes[2].z); + + FBoxSphereBounds result; + result.Origin = VecMath::createVector(center); + result.SphereRadius = sphereRadius; + result.BoxExtent = FVector(xs.GetAbsMax(), ys.GetAbsMax(), zs.GetAbsMax()); + return result; } FBoxSphereBounds From 11f5a18b403bf5da28e1d53f3e0a426b56b1532e Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 20 Apr 2022 16:13:20 +1000 Subject: [PATCH 3/4] Formatting. --- .../CesiumRuntime/Private/CesiumGltfPrimitiveComponent.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Source/CesiumRuntime/Private/CesiumGltfPrimitiveComponent.cpp b/Source/CesiumRuntime/Private/CesiumGltfPrimitiveComponent.cpp index 6c16efd37..4eb6e21a7 100644 --- a/Source/CesiumRuntime/Private/CesiumGltfPrimitiveComponent.cpp +++ b/Source/CesiumRuntime/Private/CesiumGltfPrimitiveComponent.cpp @@ -174,11 +174,13 @@ struct CalcBoundsOperation { FBoxSphereBounds operator()(const CesiumGeometry::BoundingSphere& sphere) const { glm::dmat4 matrix = getTilesetToUnrealWorldMatrix(); - glm::dvec3 center = glm::dvec3(matrix * glm::dvec4(sphere.getCenter(), 1.0)); + glm::dvec3 center = + glm::dvec3(matrix * glm::dvec4(sphere.getCenter(), 1.0)); glm::dmat3 halfAxes = glm::dmat3(matrix) * glm::dmat3(sphere.getRadius()); // The sphere only needs to reach the sides of the box, not the corners. - double sphereRadius = glm::max(glm::length(halfAxes[0]), glm::length(halfAxes[1])); + double sphereRadius = + glm::max(glm::length(halfAxes[0]), glm::length(halfAxes[1])); sphereRadius = glm::max(sphereRadius, glm::length(halfAxes[2])); FVector xs(halfAxes[0].x, halfAxes[1].x, halfAxes[2].x); From 408b5d9c18469becdb1a0132abfc8ba266a666c8 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 20 Apr 2022 16:30:47 +1000 Subject: [PATCH 4/4] Update CHANGES.md. --- CHANGES.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 6a0a8abe8..a7f3482c4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,11 +4,11 @@ ##### Breaking Changes :mega: -- Deprecated parts of the old Blueprint API for feature ID attributes from `EXT_feature_metadata`. +- Deprecated parts of the old Blueprint API for feature ID attributes from `EXT_feature_metadata`. ##### Additions :tada: -- Added a Blueprint API to access feature ID textures and feature textures from the `EXT_feature_metadata` extension. +- Added a Blueprint API to access feature ID textures and feature textures from the `EXT_feature_metadata` extension. - Improved the Blueprint API for feature ID attributes from `EXT_feature_metadata` (and upgraded batch tables). - Added the `UCesiumEncodedMetadataComponent` to enable styling with the metadata from the `EXT_feature_metadata` extension. This component provides a convenient way to query for existing metadata, dictate which metadata properties to encode for styling, and generate a starter material layer to access the wanted properties. - Added two example metadata styling materials for the NYC buildings tileset. @@ -16,6 +16,7 @@ ##### Fixes :wrench: - glTF normal, occlusion, and metallic/roughness textures are no longer treated as sRGB. +- Improved the computation of axis-aligned bounding boxes for Unreal Engine, producing much smaller and more accurate bounding boxes in many cases. ### v1.12.0 - 2022-04-01