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

3D Tiles bounding volume improvements #750

Merged
merged 4 commits into from
Nov 15, 2023
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
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.

### v0.29.0 - 2023-11-01

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
Loading