Skip to content

Commit

Permalink
Merge pull request CesiumGS#750 from CesiumGS/bounding-volumes
Browse files Browse the repository at this point in the history
3D Tiles bounding volume improvements
j9liu authored Nov 15, 2023

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
2 parents 1b9f345 + d3700dd commit 007fe0b
Showing 14 changed files with 489 additions and 20 deletions.
8 changes: 8 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -12,6 +12,14 @@
##### Additions :tada:

- Added `Cesium3DTilesContent` library and namespace. It has classes for loading, converting, and manipulating 3D Tiles tile content.
- Added `TileBoundingVolumes` class to `Cesium3DTilesContent`, making it easier to create the rich bounding volume types in `CesiumGeometry` and `CesiumGeospatial` from the simple vector representations in `Cesium3DTiles`.
- Added `transform` method to `CesiumGeometry::BoundingSphere`.
- Added `toSphere`, `fromSphere`, and `fromAxisAligned` methods to `CesiumGeometry::OrientedBoundingBox`.
- Added `TileTransform` class to `Cesium3DTilesContent`, making it easier to create a `glm::dmat4` from the `transform` property of a `Cesium3DTiles::Tile`.

##### Fixes :wrench:

- Fixed a bug in `OrientedBoundingBox::contains` where it didn't account for the bounding box's center.

##### Fixes :wrench:

1 change: 1 addition & 0 deletions Cesium3DTilesContent/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -53,6 +53,7 @@ target_include_directories(

target_link_libraries(Cesium3DTilesContent
PUBLIC
Cesium3DTiles
CesiumAsync
CesiumGeometry
CesiumGeospatial
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#pragma once

#include <CesiumGeometry/BoundingSphere.h>
#include <CesiumGeometry/OrientedBoundingBox.h>
#include <CesiumGeospatial/BoundingRegion.h>
#include <CesiumGeospatial/S2CellBoundingVolume.h>

#include <optional>

namespace Cesium3DTiles {
struct BoundingVolume;
}

namespace Cesium3DTilesContent {

/**
* @brief Provides functions for extracting bounding volumes types from the
* vectors stored in {@link Cesium3DTiles::BoundingVolume}.
*/
class TileBoundingVolumes {
public:
/**
* @brief Gets the bounding box defined in a
* {@link Cesium3DTiles::BoundingVolume}, if any.
*
* @param boundingVolume The bounding volume from which to get the box.
* @return The box, or `std::nullopt` if the bounding volume does not
* define a box. The box is defined in the tile's coordinate system.
*/
static std::optional<CesiumGeometry::OrientedBoundingBox>
getOrientedBoundingBox(const Cesium3DTiles::BoundingVolume& boundingVolume);

/**
* @brief Gets the bounding region defined in a
* {@link Cesium3DTiles::BoundingVolume}, if any.
*
* @param boundingVolume The bounding volume from which to get the region.
* @return The region, or `std::nullopt` if the bounding volume does not
* define a region. The region is defined in geographic coordinates.
*/
static std::optional<CesiumGeospatial::BoundingRegion>
getBoundingRegion(const Cesium3DTiles::BoundingVolume& boundingVolume);

/**
* @brief Gets the bounding sphere defined in a
* {@link Cesium3DTiles::BoundingVolume}, if any.
*
* @param boundingVolume The bounding volume from which to get the sphere.
* @return The sphere, or `std::nullopt` if the bounding volume does not
* define a sphere. The sphere is defined in the tile's coordinate system.
*/
static std::optional<CesiumGeometry::BoundingSphere>
getBoundingSphere(const Cesium3DTiles::BoundingVolume& boundingVolume);

/**
* @brief Gets the S2 cell bounding volume defined in the
* `3DTILES_bounding_volume_S2` extension of a
* {@link Cesium3DTiles::BoundingVolume}, if any.
*
* @param boundingVolume The bounding volume from which to get the S2 cell
* bounding volume.
* @return The S2 cell bounding volume, or `std::nullopt` if the bounding
* volume does not define an S2 cell bounding volume. The S2 cell bounding
* volume is defined in geographic coordinates.
*/
static std::optional<CesiumGeospatial::S2CellBoundingVolume>
getS2CellBoundingVolume(const Cesium3DTiles::BoundingVolume& boundingVolume);
};

} // namespace Cesium3DTilesContent
19 changes: 19 additions & 0 deletions Cesium3DTilesContent/include/Cesium3DTilesContent/TileTransform.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#pragma once

#include <glm/mat4x4.hpp>

#include <optional>

namespace Cesium3DTiles {
struct Tile;
}

namespace Cesium3DTilesContent {

class TileTransform {
public:
static std::optional<glm::dmat4>
getTransform(const Cesium3DTiles::Tile& tile);
};

} // namespace Cesium3DTilesContent
57 changes: 57 additions & 0 deletions Cesium3DTilesContent/src/TileBoundingVolumes.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#include <Cesium3DTiles/BoundingVolume.h>
#include <Cesium3DTiles/Extension3dTilesBoundingVolumeS2.h>
#include <Cesium3DTilesContent/TileBoundingVolumes.h>

using namespace Cesium3DTiles;
using namespace CesiumGeometry;
using namespace CesiumGeospatial;

namespace Cesium3DTilesContent {

std::optional<OrientedBoundingBox> TileBoundingVolumes::getOrientedBoundingBox(
const BoundingVolume& boundingVolume) {
if (boundingVolume.box.size() < 12)
return std::nullopt;

const std::vector<double>& a = boundingVolume.box;
return CesiumGeometry::OrientedBoundingBox(
glm::dvec3(a[0], a[1], a[2]),
glm::dmat3(a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11]));
}

std::optional<BoundingRegion>
TileBoundingVolumes::getBoundingRegion(const BoundingVolume& boundingVolume) {
if (boundingVolume.region.size() < 6)
return std::nullopt;

const std::vector<double>& a = boundingVolume.region;
return CesiumGeospatial::BoundingRegion(
CesiumGeospatial::GlobeRectangle(a[0], a[1], a[2], a[3]),
a[4],
a[5]);
}

std::optional<BoundingSphere>
TileBoundingVolumes::getBoundingSphere(const BoundingVolume& boundingVolume) {
if (boundingVolume.sphere.size() < 4)
return std::nullopt;

const std::vector<double>& a = boundingVolume.sphere;
return CesiumGeometry::BoundingSphere(glm::dvec3(a[0], a[1], a[2]), a[3]);
}

std::optional<S2CellBoundingVolume>
TileBoundingVolumes::getS2CellBoundingVolume(
const BoundingVolume& boundingVolume) {
const Extension3dTilesBoundingVolumeS2* pExtension =
boundingVolume.getExtension<Extension3dTilesBoundingVolumeS2>();
if (!pExtension)
return std::nullopt;

return CesiumGeospatial::S2CellBoundingVolume(
CesiumGeospatial::S2CellID::fromToken(pExtension->token),
pExtension->minimumHeight,
pExtension->maximumHeight);
}

} // namespace Cesium3DTilesContent
19 changes: 19 additions & 0 deletions Cesium3DTilesContent/src/TileTransform.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#include <Cesium3DTiles/Tile.h>
#include <Cesium3DTilesContent/TileTransform.h>

namespace Cesium3DTilesContent {

std::optional<glm::dmat4>
TileTransform::getTransform(const Cesium3DTiles::Tile& tile) {
if (tile.transform.size() < 16)
return std::nullopt;

const std::vector<double>& a = tile.transform;
return glm::dmat4(
glm::dvec4(a[0], a[1], a[2], a[3]),
glm::dvec4(a[4], a[5], a[6], a[7]),
glm::dvec4(a[8], a[9], a[10], a[11]),
glm::dvec4(a[12], a[13], a[14], a[15]));
}

} // namespace Cesium3DTilesContent
103 changes: 103 additions & 0 deletions Cesium3DTilesContent/test/TestTileBoundingVolumes.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
#include <Cesium3DTiles/BoundingVolume.h>
#include <Cesium3DTiles/Extension3dTilesBoundingVolumeS2.h>
#include <Cesium3DTilesContent/TileBoundingVolumes.h>

#include <catch2/catch.hpp>

using namespace Cesium3DTiles;
using namespace Cesium3DTilesContent;
using namespace CesiumGeometry;
using namespace CesiumGeospatial;

TEST_CASE("TileBoundingVolumes") {
SECTION("box") {
BoundingVolume bv{};

// Example bounding box from the 3D Tiles spec
// clang-format off
bv.box = {
0.0, 0.0, 10.0,
100.0, 0.0, 0.0,
0.0, 100.0, 0.0,
0.0, 0.0, 10.0};
// clang-format on

std::optional<OrientedBoundingBox> box =
TileBoundingVolumes::getOrientedBoundingBox(bv);
REQUIRE(box);

CHECK(box->getCenter().x == Approx(0.0));
CHECK(box->getCenter().y == Approx(0.0));
CHECK(box->getCenter().z == Approx(10.0));
CHECK(glm::length(box->getHalfAxes()[0]) == Approx(100.0));
CHECK(glm::length(box->getHalfAxes()[1]) == Approx(100.0));
CHECK(glm::length(box->getHalfAxes()[2]) == Approx(10.0));
}

SECTION("sphere") {
BoundingVolume bv{};

// Example bounding sphere from the 3D Tiles spec
bv.sphere = {0.0, 0.0, 10.0, 141.4214};

std::optional<BoundingSphere> sphere =
TileBoundingVolumes::getBoundingSphere(bv);
REQUIRE(sphere);

CHECK(sphere->getCenter().x == Approx(0.0));
CHECK(sphere->getCenter().y == Approx(0.0));
CHECK(sphere->getCenter().z == Approx(10.0));
CHECK(sphere->getRadius() == Approx(141.4214));
}

SECTION("region") {
BoundingVolume bv{};

// Example bounding region from the 3D Tiles spec
bv.region = {
-1.3197004795898053,
0.6988582109,
-1.3196595204101946,
0.6988897891,
0.0,
20.0};

std::optional<BoundingRegion> region =
TileBoundingVolumes::getBoundingRegion(bv);
REQUIRE(region);

CHECK(region->getRectangle().getWest() == Approx(-1.3197004795898053));
CHECK(region->getRectangle().getSouth() == Approx(0.6988582109));
CHECK(region->getRectangle().getEast() == Approx(-1.3196595204101946));
CHECK(region->getRectangle().getNorth() == Approx(0.6988897891));
CHECK(region->getMinimumHeight() == Approx(0.0));
CHECK(region->getMaximumHeight() == Approx(20.0));
}

SECTION("S2") {
BoundingVolume bv{};

// Example from 3DTILES_bounding_volume_S2 spec
Extension3dTilesBoundingVolumeS2& extension =
bv.addExtension<Extension3dTilesBoundingVolumeS2>();
extension.token = "89c6c7";
extension.minimumHeight = 0.0;
extension.maximumHeight = 1000.0;

std::optional<S2CellBoundingVolume> s2 =
TileBoundingVolumes::getS2CellBoundingVolume(bv);
REQUIRE(s2);

CHECK(s2->getCellID().getID() == S2CellID::fromToken("89c6c7").getID());
CHECK(s2->getMinimumHeight() == Approx(0.0));
CHECK(s2->getMaximumHeight() == Approx(1000.0));
}

SECTION("invalid") {
BoundingVolume bv{};
CHECK(!TileBoundingVolumes::getOrientedBoundingBox(bv).has_value());
CHECK(!TileBoundingVolumes::getBoundingSphere(bv).has_value());
CHECK(!TileBoundingVolumes::getBoundingRegion(bv).has_value());
CHECK(!TileBoundingVolumes::getS2CellBoundingVolume(bv).has_value());
}
}
21 changes: 3 additions & 18 deletions Cesium3DTilesSelection/src/BoundingVolume.cpp
Original file line number Diff line number Diff line change
@@ -17,11 +17,7 @@ BoundingVolume transformBoundingVolume(
const glm::dmat4x4& transform;

BoundingVolume operator()(const OrientedBoundingBox& boundingBox) {
const glm::dvec3 center =
glm::dvec3(transform * glm::dvec4(boundingBox.getCenter(), 1.0));
const glm::dmat3 halfAxes =
glm::dmat3(transform) * boundingBox.getHalfAxes();
return OrientedBoundingBox(center, halfAxes);
return boundingBox.transform(transform);
}

BoundingVolume operator()(const BoundingRegion& boundingRegion) noexcept {
@@ -30,16 +26,7 @@ BoundingVolume transformBoundingVolume(
}

BoundingVolume operator()(const BoundingSphere& boundingSphere) {
const glm::dvec3 center =
glm::dvec3(transform * glm::dvec4(boundingSphere.getCenter(), 1.0));

const double uniformScale = glm::max(
glm::max(
glm::length(glm::dvec3(transform[0])),
glm::length(glm::dvec3(transform[1]))),
glm::length(glm::dvec3(transform[2])));

return BoundingSphere(center, boundingSphere.getRadius() * uniformScale);
return boundingSphere.transform(transform);
}

BoundingVolume operator()(
@@ -214,9 +201,7 @@ OrientedBoundingBox
getOrientedBoundingBoxFromBoundingVolume(const BoundingVolume& boundingVolume) {
struct Operation {
OrientedBoundingBox operator()(const BoundingSphere& sphere) const {
glm::dvec3 center = sphere.getCenter();
glm::dmat3 halfAxes = glm::dmat3(sphere.getRadius());
return OrientedBoundingBox(center, halfAxes);
return OrientedBoundingBox::fromSphere(sphere);
}

OrientedBoundingBox
13 changes: 13 additions & 0 deletions CesiumGeometry/include/CesiumGeometry/BoundingSphere.h
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@
#include "CullingResult.h"
#include "Library.h"

#include <glm/fwd.hpp>
#include <glm/vec3.hpp>

namespace CesiumGeometry {
@@ -61,6 +62,18 @@ class CESIUMGEOMETRY_API BoundingSphere final {
double
computeDistanceSquaredToPosition(const glm::dvec3& position) const noexcept;

/**
* @brief Transforms this bounding sphere to another coordinate system using a
* 4x4 matrix.
*
* If the transformation has non-uniform scale, the bounding sphere's radius
* is scaled by the largest scale value among the transformation's axes.
*
* @param transformation The transformation.
* @return The bounding sphere in the new coordinate system.
*/
BoundingSphere transform(const glm::dmat4& transformation) const noexcept;

private:
glm::dvec3 _center;
double _radius;
Loading

0 comments on commit 007fe0b

Please sign in to comment.