Skip to content

Commit

Permalink
Merge pull request #750 from CesiumGS/bounding-volumes
Browse files Browse the repository at this point in the history
3D Tiles bounding volume improvements
  • Loading branch information
j9liu authored Nov 15, 2023
2 parents 1b9f345 + d3700dd commit 007fe0b
Show file tree
Hide file tree
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
Expand Up @@ -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:

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

target_link_libraries(Cesium3DTilesContent
PUBLIC
Cesium3DTiles
CesiumAsync
CesiumGeometry
CesiumGeospatial
Expand Down
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
Expand Up @@ -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 {
Expand All @@ -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()(
Expand Down Expand Up @@ -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
Expand Down
13 changes: 13 additions & 0 deletions CesiumGeometry/include/CesiumGeometry/BoundingSphere.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "CullingResult.h"
#include "Library.h"

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

namespace CesiumGeometry {
Expand Down Expand Up @@ -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;
Expand Down
Loading

0 comments on commit 007fe0b

Please sign in to comment.