From ec02c89a6b2dfec000a408452e222197fa032e87 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 6 Nov 2023 18:48:23 +1100 Subject: [PATCH 1/4] Add TileBoundingVolumes class, other BV tweaks. --- Cesium3DTilesContent/CMakeLists.txt | 1 + .../TileBoundingVolumes.h | 70 ++++++++++++ .../src/TileBoundingVolumes.cpp | 57 ++++++++++ .../test/TestTileBoundingVolumes.cpp | 103 ++++++++++++++++++ Cesium3DTilesSelection/src/BoundingVolume.cpp | 21 +--- .../include/CesiumGeometry/BoundingSphere.h | 13 +++ .../CesiumGeometry/OrientedBoundingBox.h | 21 ++++ CesiumGeometry/src/BoundingSphere.cpp | 15 +++ CesiumGeometry/src/OrientedBoundingBox.cpp | 24 ++++ CesiumGeometry/test/TestBoundingSphere.cpp | 44 ++++++++ .../test/TestOrientedBoundingBox.cpp | 51 +++++++++ 11 files changed, 402 insertions(+), 18 deletions(-) create mode 100644 Cesium3DTilesContent/include/Cesium3DTilesContent/TileBoundingVolumes.h create mode 100644 Cesium3DTilesContent/src/TileBoundingVolumes.cpp create mode 100644 Cesium3DTilesContent/test/TestTileBoundingVolumes.cpp diff --git a/Cesium3DTilesContent/CMakeLists.txt b/Cesium3DTilesContent/CMakeLists.txt index 8493e1397..649b0c327 100644 --- a/Cesium3DTilesContent/CMakeLists.txt +++ b/Cesium3DTilesContent/CMakeLists.txt @@ -53,6 +53,7 @@ target_include_directories( target_link_libraries(Cesium3DTilesContent PUBLIC + Cesium3DTiles CesiumAsync CesiumGeometry CesiumGeospatial diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/TileBoundingVolumes.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/TileBoundingVolumes.h new file mode 100644 index 000000000..0d76aa83e --- /dev/null +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/TileBoundingVolumes.h @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +#include + +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 + 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 + 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 + 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 + getS2CellBoundingVolume(const Cesium3DTiles::BoundingVolume& boundingVolume); +}; + +} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/TileBoundingVolumes.cpp b/Cesium3DTilesContent/src/TileBoundingVolumes.cpp new file mode 100644 index 000000000..023e9d1ca --- /dev/null +++ b/Cesium3DTilesContent/src/TileBoundingVolumes.cpp @@ -0,0 +1,57 @@ +#include +#include +#include + +using namespace Cesium3DTiles; +using namespace CesiumGeometry; +using namespace CesiumGeospatial; + +namespace Cesium3DTilesContent { + +std::optional TileBoundingVolumes::getOrientedBoundingBox( + const BoundingVolume& boundingVolume) { + if (boundingVolume.box.size() < 12) + return std::nullopt; + + const std::vector& 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 +TileBoundingVolumes::getBoundingRegion(const BoundingVolume& boundingVolume) { + if (boundingVolume.region.size() < 6) + return std::nullopt; + + const std::vector& a = boundingVolume.region; + return CesiumGeospatial::BoundingRegion( + CesiumGeospatial::GlobeRectangle(a[0], a[1], a[2], a[3]), + a[4], + a[5]); +} + +std::optional +TileBoundingVolumes::getBoundingSphere(const BoundingVolume& boundingVolume) { + if (boundingVolume.sphere.size() < 4) + return std::nullopt; + + const std::vector& a = boundingVolume.sphere; + return CesiumGeometry::BoundingSphere(glm::dvec3(a[0], a[1], a[2]), a[3]); +} + +std::optional +TileBoundingVolumes::getS2CellBoundingVolume( + const BoundingVolume& boundingVolume) { + const Extension3dTilesBoundingVolumeS2* pExtension = + boundingVolume.getExtension(); + if (!pExtension) + return std::nullopt; + + return CesiumGeospatial::S2CellBoundingVolume( + CesiumGeospatial::S2CellID::fromToken(pExtension->token), + pExtension->minimumHeight, + pExtension->maximumHeight); +} + +} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/test/TestTileBoundingVolumes.cpp b/Cesium3DTilesContent/test/TestTileBoundingVolumes.cpp new file mode 100644 index 000000000..ec6f0c168 --- /dev/null +++ b/Cesium3DTilesContent/test/TestTileBoundingVolumes.cpp @@ -0,0 +1,103 @@ +#include +#include +#include + +#include + +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 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 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 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(); + extension.token = "89c6c7"; + extension.minimumHeight = 0.0; + extension.maximumHeight = 1000.0; + + std::optional 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()); + } +} diff --git a/Cesium3DTilesSelection/src/BoundingVolume.cpp b/Cesium3DTilesSelection/src/BoundingVolume.cpp index db9084985..790dee8fe 100644 --- a/Cesium3DTilesSelection/src/BoundingVolume.cpp +++ b/Cesium3DTilesSelection/src/BoundingVolume.cpp @@ -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 diff --git a/CesiumGeometry/include/CesiumGeometry/BoundingSphere.h b/CesiumGeometry/include/CesiumGeometry/BoundingSphere.h index f65e49f35..c910a2057 100644 --- a/CesiumGeometry/include/CesiumGeometry/BoundingSphere.h +++ b/CesiumGeometry/include/CesiumGeometry/BoundingSphere.h @@ -3,6 +3,7 @@ #include "CullingResult.h" #include "Library.h" +#include #include 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 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; diff --git a/CesiumGeometry/include/CesiumGeometry/OrientedBoundingBox.h b/CesiumGeometry/include/CesiumGeometry/OrientedBoundingBox.h index 80b16a7c2..0d50bb88a 100644 --- a/CesiumGeometry/include/CesiumGeometry/OrientedBoundingBox.h +++ b/CesiumGeometry/include/CesiumGeometry/OrientedBoundingBox.h @@ -1,6 +1,7 @@ #pragma once #include "AxisAlignedBox.h" +#include "BoundingSphere.h" #include "CullingResult.h" #include "Library.h" @@ -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; diff --git a/CesiumGeometry/src/BoundingSphere.cpp b/CesiumGeometry/src/BoundingSphere.cpp index 88ca2839c..2fb3c71f8 100644 --- a/CesiumGeometry/src/BoundingSphere.cpp +++ b/CesiumGeometry/src/BoundingSphere.cpp @@ -3,6 +3,7 @@ #include "CesiumGeometry/Plane.h" #include +#include namespace CesiumGeometry { @@ -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 diff --git a/CesiumGeometry/src/OrientedBoundingBox.cpp b/CesiumGeometry/src/OrientedBoundingBox.cpp index c950e2f41..d524b9239 100644 --- a/CesiumGeometry/src/OrientedBoundingBox.cpp +++ b/CesiumGeometry/src/OrientedBoundingBox.cpp @@ -124,4 +124,28 @@ AxisAlignedBox OrientedBoundingBox::toAxisAligned() const noexcept { return AxisAlignedBox(ll.x, ll.y, ll.z, ur.x, ur.y, ur.z); } +BoundingSphere OrientedBoundingBox::toSphere() const noexcept { + const glm::dmat3& halfAxes = this->_halfAxes; + glm::dvec3 corner = halfAxes[0] + halfAxes[1] + halfAxes[2]; + double sphereRadius = glm::length(corner); + return BoundingSphere(this->_center, sphereRadius); +} + +/*static*/ OrientedBoundingBox OrientedBoundingBox::fromAxisAligned( + const AxisAlignedBox& axisAligned) noexcept { + return OrientedBoundingBox( + axisAligned.center, + glm::dmat3( + glm::dvec3(axisAligned.lengthX * 0.5, 0.0, 0.0), + glm::dvec3(0.0, axisAligned.lengthY * 0.5, 0.0), + glm::dvec3(0.0, 0.0, axisAligned.lengthZ * 0.5))); +} + +/*static*/ OrientedBoundingBox +OrientedBoundingBox::fromSphere(const BoundingSphere& sphere) noexcept { + glm::dvec3 center = sphere.getCenter(); + glm::dmat3 halfAxes = glm::dmat3(sphere.getRadius()); + return OrientedBoundingBox(center, halfAxes); +} + } // namespace CesiumGeometry diff --git a/CesiumGeometry/test/TestBoundingSphere.cpp b/CesiumGeometry/test/TestBoundingSphere.cpp index d17867e4c..52ba6e49b 100644 --- a/CesiumGeometry/test/TestBoundingSphere.cpp +++ b/CesiumGeometry/test/TestBoundingSphere.cpp @@ -3,9 +3,12 @@ #include "CesiumUtility/Math.h" #include +#include +#include #include using namespace CesiumGeometry; +using namespace CesiumUtility; TEST_CASE("BoundingSphere::intersectPlane") { struct TestCase { @@ -54,6 +57,47 @@ TEST_CASE( CHECK(bs.computeDistanceSquaredToPosition(position) == 0); } +TEST_CASE("BoundingSphere::transform") { + BoundingSphere sphere(glm::dvec3(1.0, 2.0, 3.0), 45.0); + + SECTION("translating moves the center only") { + glm::dmat4 transformation = glm::translate( + glm::identity(), + glm::dvec3(10.0, 20.0, 30.0)); + BoundingSphere transformed = sphere.transform(transformation); + CHECK(transformed.getRadius() == Approx(sphere.getRadius())); + CHECK(transformed.getCenter().x == Approx(sphere.getCenter().x + 10.0)); + CHECK(transformed.getCenter().y == Approx(sphere.getCenter().y + 20.0)); + CHECK(transformed.getCenter().z == Approx(sphere.getCenter().z + 30.0)); + } + + SECTION("rotating moves the center only") { + double fortyFiveDegrees = Math::OnePi / 4.0; + glm::dmat4 transformation = glm::eulerAngleY(fortyFiveDegrees); + BoundingSphere transformed = sphere.transform(transformation); + CHECK(transformed.getRadius() == Approx(sphere.getRadius())); + + glm::dvec3 rotatedCenter = glm::dmat3(transformation) * sphere.getCenter(); + CHECK(transformed.getCenter().x == Approx(rotatedCenter.x)); + CHECK(transformed.getCenter().y == Approx(rotatedCenter.y)); + CHECK(transformed.getCenter().z == Approx(rotatedCenter.z)); + } + + SECTION( + "scaling moves the center, and scales the radius by the max component") { + glm::dmat4 transformation = + glm::scale(glm::identity(), glm::dvec3(2.0, 3.0, 4.0)); + BoundingSphere transformed = sphere.transform(transformation); + + glm::dvec3 scaledCenter = glm::dmat3(transformation) * sphere.getCenter(); + CHECK(transformed.getCenter().x == Approx(scaledCenter.x)); + CHECK(transformed.getCenter().y == Approx(scaledCenter.y)); + CHECK(transformed.getCenter().z == Approx(scaledCenter.z)); + + CHECK(transformed.getRadius() == Approx(45.0 * 4.0)); + } +} + TEST_CASE("BoundingSphere::computeDistanceSquaredToPosition example") { auto anyOldSphereArray = []() { return std::vector{ diff --git a/CesiumGeometry/test/TestOrientedBoundingBox.cpp b/CesiumGeometry/test/TestOrientedBoundingBox.cpp index 45013b1e0..36863955b 100644 --- a/CesiumGeometry/test/TestOrientedBoundingBox.cpp +++ b/CesiumGeometry/test/TestOrientedBoundingBox.cpp @@ -389,3 +389,54 @@ TEST_CASE("OrientedBoundingBox::toAxisAligned") { CHECK(Math::equalsEpsilon(aabb.maximumZ, 3.0 + glm::sqrt(2.0), 0.0, 1e-14)); } } + +TEST_CASE("OrientedBoundingBox::toSphere") { + SECTION("axis-aligned box with identity half-axes") { + OrientedBoundingBox obb( + glm::dvec3(1.0, 2.0, 3.0), + glm::dmat3( + glm::dvec3(1.0, 0.0, 0.0), + glm::dvec3(0.0, 1.0, 0.0), + glm::dvec3(0.0, 0.0, 1.0))); + + BoundingSphere sphere = obb.toSphere(); + CHECK(sphere.getCenter().x == Approx(1.0)); + CHECK(sphere.getCenter().y == Approx(2.0)); + CHECK(sphere.getCenter().z == Approx(3.0)); + + CHECK(sphere.getRadius() == Approx(glm::sqrt(3.0))); + } + + SECTION("rotating the box does not change the bounding sphere") { + // Rotate the OBB 45 degrees around the Y-axis. + // This shouldn't change the bounding sphere at all. + double fortyFiveDegrees = Math::OnePi / 4.0; + glm::dmat3 rotation = glm::dmat3(glm::eulerAngleY(fortyFiveDegrees)); + OrientedBoundingBox obb(glm::dvec3(1.0, 2.0, 3.0), rotation); + + BoundingSphere sphere = obb.toSphere(); + CHECK(sphere.getCenter().x == Approx(1.0)); + CHECK(sphere.getCenter().y == Approx(2.0)); + CHECK(sphere.getCenter().z == Approx(3.0)); + + CHECK(sphere.getRadius() == Approx(glm::sqrt(3.0))); + } + + SECTION("a scaled axis-aligned box") { + OrientedBoundingBox obb( + glm::dvec3(1.0, 2.0, 3.0), + glm::dmat3( + glm::dvec3(10.0, 0.0, 0.0), + glm::dvec3(0.0, 20.0, 0.0), + glm::dvec3(0.0, 0.0, 30.0))); + + BoundingSphere sphere = obb.toSphere(); + CHECK(sphere.getCenter().x == Approx(1.0)); + CHECK(sphere.getCenter().y == Approx(2.0)); + CHECK(sphere.getCenter().z == Approx(3.0)); + + CHECK( + sphere.getRadius() == + Approx(glm::sqrt(10.0 * 10.0 + 20.0 * 20.0 + 30.0 * 30.0))); + } +} From 5e860d6d10a7421885718b34c2367b01f3aa882a Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 6 Nov 2023 19:19:12 +1100 Subject: [PATCH 2/4] Fix OrientedBoundingBox::contains. --- CHANGES.md | 7 ++++ CesiumGeometry/src/OrientedBoundingBox.cpp | 4 +- .../test/TestOrientedBoundingBox.cpp | 39 +++++++++++++++++++ 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 9245143d7..bdfa907d6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -12,6 +12,13 @@ ##### 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`. + +##### Fixes :wrench: + +- Fixed a bug in `OrientedBoundingBox::contains` where it didn't account for the bounding box's center. ### v0.29.0 - 2023-11-01 diff --git a/CesiumGeometry/src/OrientedBoundingBox.cpp b/CesiumGeometry/src/OrientedBoundingBox.cpp index d524b9239..6ee147b70 100644 --- a/CesiumGeometry/src/OrientedBoundingBox.cpp +++ b/CesiumGeometry/src/OrientedBoundingBox.cpp @@ -100,9 +100,9 @@ double OrientedBoundingBox::computeDistanceSquaredToPosition( return distanceSquared; } -// TODO: add test for this bool OrientedBoundingBox::contains(const glm::dvec3& position) const noexcept { - glm::dvec3 localPosition = this->_inverseHalfAxes * position; + glm::dvec3 localPosition = position - this->_center; + localPosition = this->_inverseHalfAxes * localPosition; return glm::abs(localPosition.x) <= 1.0 && glm::abs(localPosition.y) <= 1.0 && glm::abs(localPosition.z) <= 1.0; } diff --git a/CesiumGeometry/test/TestOrientedBoundingBox.cpp b/CesiumGeometry/test/TestOrientedBoundingBox.cpp index 36863955b..6b0dc7823 100644 --- a/CesiumGeometry/test/TestOrientedBoundingBox.cpp +++ b/CesiumGeometry/test/TestOrientedBoundingBox.cpp @@ -440,3 +440,42 @@ TEST_CASE("OrientedBoundingBox::toSphere") { Approx(glm::sqrt(10.0 * 10.0 + 20.0 * 20.0 + 30.0 * 30.0))); } } + +TEST_CASE("OrientedBoundingBox::contains") { + SECTION("axis-aligned") { + OrientedBoundingBox obb( + glm::dvec3(10.0, 20.0, 30.0), + glm::dmat3( + glm::dvec3(2.0, 0.0, 0.0), + glm::dvec3(0.0, 3.0, 0.0), + glm::dvec3(0.0, 0.0, 4.0))); + CHECK(!obb.contains(glm::dvec3(0.0, 0.0, 0.0))); + CHECK(obb.contains(glm::dvec3(10.0, 20.0, 30.0))); + CHECK(obb.contains(glm::dvec3(12.0, 23.0, 34.0))); + CHECK(obb.contains(glm::dvec3(8.0, 17.0, 26.0))); + CHECK(!obb.contains(glm::dvec3(13.0, 20.0, 30.0))); + CHECK(!obb.contains(glm::dvec3(10.0, 24.0, 30.0))); + CHECK(!obb.contains(glm::dvec3(10.0, 20.0, 35.0))); + } + + SECTION("rotated") { + // Rotate the OBB 45 degrees around the Y-axis + double fortyFiveDegrees = Math::OnePi / 4.0; + glm::dmat3 halfAngles( + glm::dvec3(2.0, 0.0, 0.0), + glm::dvec3(0.0, 3.0, 0.0), + glm::dvec3(0.0, 0.0, 4.0)); + glm::dmat3 rotation = glm::dmat3(glm::eulerAngleY(fortyFiveDegrees)); + glm::dmat3 transformed = rotation * halfAngles; + glm::dvec3 center(10.0, 20.0, 30.0); + OrientedBoundingBox obb(center, transformed); + + CHECK(!obb.contains(glm::dvec3(0.0, 0.0, 0.0))); + CHECK(obb.contains(center)); + CHECK(obb.contains(center + rotation * glm::dvec3(2.0, 3.0, 4.0))); + CHECK(obb.contains(center + rotation * glm::dvec3(-2.0, -3.0, -4.0))); + CHECK(!obb.contains(center + rotation * glm::dvec3(3.0, 0.0, 0.0))); + CHECK(!obb.contains(center + rotation * glm::dvec3(0.0, 4.0, 0.0))); + CHECK(!obb.contains(center + rotation * glm::dvec3(0.0, 0.0, 5.0))); + } +} From a1b37258160b77a61971df780679f1a314854a57 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 6 Nov 2023 22:24:26 +1100 Subject: [PATCH 3/4] Add TileTransform class. --- CHANGES.md | 1 + .../Cesium3DTilesContent/TileTransform.h | 19 +++++++++++++++++++ Cesium3DTilesContent/src/TileTransform.cpp | 19 +++++++++++++++++++ 3 files changed, 39 insertions(+) create mode 100644 Cesium3DTilesContent/include/Cesium3DTilesContent/TileTransform.h create mode 100644 Cesium3DTilesContent/src/TileTransform.cpp diff --git a/CHANGES.md b/CHANGES.md index bdfa907d6..106d62e3e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -15,6 +15,7 @@ - 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: diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/TileTransform.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/TileTransform.h new file mode 100644 index 000000000..16b079fea --- /dev/null +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/TileTransform.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +#include + +namespace Cesium3DTiles { +struct Tile; +} + +namespace Cesium3DTilesContent { + +class TileTransform { +public: + static std::optional + getTransform(const Cesium3DTiles::Tile& tile); +}; + +} // namespace Cesium3DTilesContent diff --git a/Cesium3DTilesContent/src/TileTransform.cpp b/Cesium3DTilesContent/src/TileTransform.cpp new file mode 100644 index 000000000..7e4b77ddf --- /dev/null +++ b/Cesium3DTilesContent/src/TileTransform.cpp @@ -0,0 +1,19 @@ +#include +#include + +namespace Cesium3DTilesContent { + +std::optional +TileTransform::getTransform(const Cesium3DTiles::Tile& tile) { + if (tile.transform.size() < 16) + return std::nullopt; + + const std::vector& 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 From d3700dda57bf70f8058a2b5697f658807716826d Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Wed, 15 Nov 2023 11:36:48 -0500 Subject: [PATCH 4/4] Minor doc changes --- .../include/Cesium3DTilesContent/TileBoundingVolumes.h | 2 +- CesiumGeometry/include/CesiumGeometry/BoundingSphere.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cesium3DTilesContent/include/Cesium3DTilesContent/TileBoundingVolumes.h b/Cesium3DTilesContent/include/Cesium3DTilesContent/TileBoundingVolumes.h index 0d76aa83e..bb2b9b74d 100644 --- a/Cesium3DTilesContent/include/Cesium3DTilesContent/TileBoundingVolumes.h +++ b/Cesium3DTilesContent/include/Cesium3DTilesContent/TileBoundingVolumes.h @@ -36,7 +36,7 @@ class TileBoundingVolumes { * * @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. + * define a region. The region is defined in geographic coordinates. */ static std::optional getBoundingRegion(const Cesium3DTiles::BoundingVolume& boundingVolume); diff --git a/CesiumGeometry/include/CesiumGeometry/BoundingSphere.h b/CesiumGeometry/include/CesiumGeometry/BoundingSphere.h index c910a2057..3f0827f27 100644 --- a/CesiumGeometry/include/CesiumGeometry/BoundingSphere.h +++ b/CesiumGeometry/include/CesiumGeometry/BoundingSphere.h @@ -67,7 +67,7 @@ class CESIUMGEOMETRY_API BoundingSphere final { * 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. + * 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.