Skip to content

Commit

Permalink
Add TileBoundingVolumes class, other BV tweaks.
Browse files Browse the repository at this point in the history
  • Loading branch information
kring committed Nov 6, 2023
1 parent 58f1b7f commit ec02c89
Show file tree
Hide file tree
Showing 11 changed files with 402 additions and 18 deletions.
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 box 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
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
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 scale of the transformation's axis with the largest scale.
*
* @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
21 changes: 21 additions & 0 deletions CesiumGeometry/include/CesiumGeometry/OrientedBoundingBox.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include "AxisAlignedBox.h"
#include "BoundingSphere.h"
#include "CullingResult.h"
#include "Library.h"

Expand Down Expand Up @@ -117,8 +118,28 @@ class CESIUMGEOMETRY_API OrientedBoundingBox final {
OrientedBoundingBox
transform(const glm::dmat4& transformation) const noexcept;

/**
* @brief Converts this oriented bounding box to an axis-aligned bounding box.
*/
AxisAlignedBox toAxisAligned() const noexcept;

/**
* @brief Converts this oriented bounding box to a bounding sphere.
*/
BoundingSphere toSphere() const noexcept;

/**
* @brief Creates an oriented bounding box from the given axis-aligned
* bounding box.
*/
static OrientedBoundingBox
fromAxisAligned(const AxisAlignedBox& axisAligned) noexcept;

/**
* @brief Creates an oriented bounding box from the given bounding sphere.
*/
static OrientedBoundingBox fromSphere(const BoundingSphere& sphere) noexcept;

private:
glm::dvec3 _center;
glm::dmat3 _halfAxes;
Expand Down
15 changes: 15 additions & 0 deletions CesiumGeometry/src/BoundingSphere.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "CesiumGeometry/Plane.h"

#include <glm/geometric.hpp>
#include <glm/mat4x4.hpp>

namespace CesiumGeometry {

Expand Down Expand Up @@ -34,4 +35,18 @@ double BoundingSphere::computeDistanceSquaredToPosition(
return distance * distance;
}

BoundingSphere
BoundingSphere::transform(const glm::dmat4& transformation) const noexcept {
const glm::dvec3 center =
glm::dvec3(transformation * glm::dvec4(this->getCenter(), 1.0));

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

return BoundingSphere(center, this->getRadius() * uniformScale);
}

} // namespace CesiumGeometry
Loading

0 comments on commit ec02c89

Please sign in to comment.