Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compute much better axis-aligned bounding boxes for tiles, for use by Unreal Engine #823

Merged
merged 4 commits into from
Apr 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,19 @@

##### 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.

##### 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

Expand Down
3 changes: 2 additions & 1 deletion Source/CesiumRuntime/Private/Cesium3DTileset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
13 changes: 10 additions & 3 deletions Source/CesiumRuntime/Private/CesiumGltfComponent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<UCesiumGltfPrimitiveComponent>(pGltf, meshName);
Expand All @@ -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);
Expand Down Expand Up @@ -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<HalfConstructedReal*>(pHalfConstructed.Get());

Expand Down Expand Up @@ -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);
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion Source/CesiumRuntime/Private/CesiumGltfComponent.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#pragma once

#include "Cesium3DTilesSelection/BoundingVolume.h"
#include "CesiumEncodedMetadataUtility.h"
#include "CesiumMetadataModel.h"
#include "Components/PrimitiveComponent.h"
Expand Down Expand Up @@ -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();
Expand Down
115 changes: 115 additions & 0 deletions Source/CesiumRuntime/Private/CesiumGltfPrimitiveComponent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#include "Engine/StaticMesh.h"
#include "Materials/MaterialInstanceDynamic.h"
#include "PhysicsEngine/BodySetup.h"
#include "VecMath.h"
#include <glm/gtc/matrix_inverse.hpp>

// Sets default values for this component's properties
UCesiumGltfPrimitiveComponent::UCesiumGltfPrimitiveComponent() {
Expand Down Expand Up @@ -135,3 +137,116 @@ 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 {
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
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);
}
5 changes: 5 additions & 0 deletions Source/CesiumRuntime/Private/CesiumGltfPrimitiveComponent.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#pragma once

#include "Cesium3DTilesSelection/BoundingVolume.h"
#include "CesiumEncodedMetadataUtility.h"
#include "CesiumGltf/MeshPrimitive.h"
#include "CesiumGltf/Model.h"
Expand Down Expand Up @@ -39,6 +40,8 @@ class UCesiumGltfPrimitiveComponent : public UStaticMeshComponent {
OverlayTextureCoordinateIDMap overlayTextureCoordinateIDToUVIndex;
std::unordered_map<uint32_t, uint32_t> textureCoordinateMap;

std::optional<Cesium3DTilesSelection::BoundingVolume> boundingVolume;

/**
* Updates this component's transform from a new double-precision
* transformation from the Cesium world to the Unreal Engine world, as well as
Expand All @@ -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;
};
2 changes: 1 addition & 1 deletion extern/cesium-native